Spaces:
Sleeping
Sleeping
File size: 57,956 Bytes
bfc1cf6 2f3d61a 6439bc9 64d6a1c 56c0514 ce027ba d29ccbb 5fc9baf 6439bc9 64d6a1c 6439bc9 bfc1cf6 2f3d61a 516fc47 49a9551 1a27daf 49a9551 1a27daf 516fc47 e5a93f2 516fc47 2a8e458 08c00fa 2a8e458 08c00fa 2a8e458 08c00fa 2a8e458 69b437a 2a8e458 d29ccbb 175ecd4 e5a93f2 175ecd4 e5a93f2 175ecd4 b56d8f3 389c70e d29ccbb 389c70e d29ccbb 389c70e 543c39f d29ccbb 543c39f d29ccbb 543c39f d29ccbb 543c39f d29ccbb 543c39f 5e14f9b 86bebd0 5e14f9b b5b49df bab4ce8 cbe8375 1c01413 cbe8375 fad07d6 cbe8375 bab4ce8 5e14f9b 6ec21c9 5e14f9b cbe8375 d29ccbb 3646962 add2681 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 406c0d6 3646962 f3ba4ac 3646962 406c0d6 2841710 9682d40 6439bc9 6b1a461 6e9b3c0 6b1a461 6439bc9 2a8e458 6439bc9 2a8e458 6439bc9 2a8e458 64d6a1c ac1b43b 4a92fe8 e31e231 4a92fe8 ac1b43b aa4ea66 c515f30 ac1b43b c515f30 ac1b43b aa4ea66 ac1b43b 36fcbe3 1a27daf 6560f91 1a27daf 6560f91 1a27daf d657e5b 1a27daf d657e5b 1a27daf d657e5b 1a27daf d657e5b 1a27daf d657e5b 6560f91 1a27daf 6560f91 1a27daf 6560f91 1a27daf 6560f91 1a27daf 6560f91 bc29f9e 1d8b45f 1a27daf 1d8b45f 1a27daf 8d11edc 0dd349a 8d11edc ea5683e 9f8e253 0dd349a 1d8b45f 0dd349a 8d11edc 1a27daf bce5635 f656a5d 1a27daf f656a5d d3f8085 f656a5d 1a27daf eacb8ca 1a27daf 4b72003 1a27daf 4b72003 1a27daf 36fcbe3 1a27daf 6560f91 1a27daf 4b72003 f08c424 1a27daf 1e0648a 022800b 1871af1 f08c424 6439bc9 6e9b3c0 f3ba4ac 6439bc9 6b1a461 e6857f6 6439bc9 bfc1cf6 69b437a 2a8e458 1871af1 ac1b43b 22e8e0a 171e4a2 bc5c4ac b3da6e1 118e67c 171e4a2 30f4a0b 171e4a2 9eb3a05 2429022 17d180b 171e4a2 1a27daf 8191966 f2ca1b6 171e4a2 aec4652 4b72003 7426a13 4b72003 773481c d9683c4 773481c e0261c3 b08d1d8 e0261c3 b08d1d8 e0261c3 773481c b08d1d8 773481c e0261c3 773481c e0261c3 b08d1d8 773481c e0261c3 d9683c4 e0261c3 d9683c4 e0261c3 d9683c4 e0261c3 773481c da61262 4b72003 6f13d68 4b72003 b988d1c 4b72003 6408d99 4b72003 4e9a407 4b72003 5a3373d 4b72003 072d34e 5a3373d 773481c f6e4af4 3646962 f3ba4ac 3646962 f3ba4ac 3646962 6439bc9 bfc1cf6 ef4fbab 2a474e7 60b557b bfc1cf6 521f13c ef4fbab bfc1cf6 6439bc9 d04b940 6439bc9 7748dcf 7ff1e00 4b72003 7ff1e00 1a27daf 3b9c255 1a27daf 4b72003 1a27daf 9814205 6439bc9 64d6a1c 6e9b3c0 9814205 64d6a1c 9814205 64d6a1c fbc3848 9223a59 4a59421 4b72003 4a59421 4b72003 4a59421 543c39f fab49e1 543c39f 5e14f9b 543c39f 5e14f9b 543c39f d29ccbb 543c39f fab49e1 543c39f 4a59421 8975086 4a59421 5e14f9b 4a59421 5e14f9b bbf54a8 18205a7 02551fc bbf54a8 1c43a53 1816322 36fcbe3 3015823 bbf54a8 5e14f9b ae38345 86e2004 ae38345 36fcbe3 ae38345 36fcbe3 be5bcf6 1224e3d ae38345 36fcbe3 ae38345 bbf54a8 18205a7 5e14f9b bbf54a8 f6e4af4 bbf54a8 f6e4af4 18205a7 f6e4af4 02551fc 7983613 4de4b44 7983613 02551fc 7983613 fbc3848 7983613 6f7bf4c 7983613 fbc3848 7983613 fbc3848 7983613 b425202 36fcbe3 02551fc 36fcbe3 f6e4af4 188329d 36fcbe3 02551fc 36fcbe3 02551fc dac34cb 02551fc 86bebd0 02551fc 3e6b08c c047aac 3e6b08c 36fcbe3 b425202 3e6b08c a3e7dc8 3e6b08c 1694377 3e6b08c a3e7dc8 b425202 02551fc f6e4af4 02551fc 2a8e458 8de0ae8 2a8e458 1e0648a 8de0ae8 1e0648a 2a8e458 91aaf8c 9682d40 6e9b3c0 e5a93f2 9682d40 1871af1 14b4dc6 1871af1 6e9b3c0 1871af1 1e0648a 2a8e458 1871af1 d3d9d2e 1871af1 d3d9d2e 1871af1 d3d9d2e 1871af1 6e9b3c0 1871af1 a7ab11a 14b4dc6 f3ba4ac e5a93f2 f3ba4ac 1871af1 6e9b3c0 9682d40 6e9b3c0 91aaf8c 9682d40 6e9b3c0 9682d40 f3ba4ac 9682d40 6e9b3c0 1871af1 f3ba4ac 2a8e458 3b9c255 1871af1 2a8e458 9682d40 2a8e458 91aaf8c 6439bc9 2f3d61a |
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 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 |
import os
import gradio as gr
from anthropic import Anthropic
from datetime import datetime, timedelta
from collections import deque
import random
import logging
import tempfile
from pathlib import Path
from sympy import *
import json
from pathlib import Path
import openai
# Set up logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Initialize Anthropic client
anthropic = Anthropic(
api_key=os.environ.get('ANTHROPIC_API_KEY')
)
# Initialize OpenAI client
openai.api_key = os.environ.get('My_MathTest_Key')
if openai.api_key is None:
logger.error("OpenAI API key not found in environment variables")
raise ValueError("OpenAI API key not found. Please set the My_MathTest_Key environment variable.")
# Request tracking
MAX_REQUESTS_PER_DAY = 20
request_history = deque(maxlen=1000)
# Define subtopic mappings
SUBJECT_SUBTOPICS = {
"Single Variable Calculus": [
"limits",
"derivatives",
"integrals",
"related rates",
"linear approximation",
"integration techniques",
"improper integrals",
"area between curves",
"volume of revolution",
"surface area of revolution",
"arc length",
"parametric equations",
"polar coordinates",
"sequences",
"series",
"integration by parts",
"partial fractions",
"Taylor series",
"L'Hôpital's rule",
"mean value theorem",
"intermediate value theorem",
"extreme value theorem",
"fundamental theorem of calculus 1",
"fundamental theorem of calculus 2"
],
"Multivariable Calculus": [
"partial derivatives",
"multiple integrals",
"vector fields",
"optimization",
"stokes theorem",
"green's theorem",
"line integrals",
"parametric equations",
"spherical coordinates"
],
"Linear Algebra": [
"matrices",
"vector spaces",
"eigenvalues",
"linear transformations"
],
"Differential Equations": [
"first order equations",
"second order equations",
"systems",
"stability analysis"
],
"Real Analysis": [
"sequences",
"series",
"continuity",
"differentiation",
"integration"
],
"Complex Analysis": [
"complex functions",
"analyticity",
"contour integration",
"residues"
],
"Abstract Algebra": [
"groups",
"rings",
"fields",
"homomorphisms"
],
"Probability Theory": [
"probability spaces",
"random variables",
"distributions",
"limit theorems"
],
"Numerical Analysis": [
"approximation",
"interpolation",
"numerical integration",
"error analysis"
],
"Topology": [
"metric spaces",
"continuity",
"compactness",
"connectedness"
],
}
SYMPY_GUIDELINES = """
When writing SymPy code to verify solutions:
Import Guidelines:
- ALWAYS import with "from sympy..." using specific imports like this:
from sympy import Matrix, solve, Symbol, pi, cos, sin
- DO NOT use full namespace import like this: import sympy as sp
NOTE: For eigenvalue problems, use 'lam = Symbol('lam')' instead of importing from sympy.abc
1. Variable Declaration and Expressions:
- ALWAYS create symbolic expressions instead of literal numbers when working with mathematical operations:
```python
# CORRECT:
x = Symbol('x')
expr = x + 1 # Creates a symbolic expression
# INCORRECT:
expr = 1 # This is just a number, can't be differentiated
```
- For polynomials and functions:
```python
# CORRECT:
x = Symbol('x')
p = x**2 + 2*x + 1 # Creates a polynomial expression
# INCORRECT:
p = 1 # This won't work for operations like diff()
```
- When verifying operator actions:
```python
# CORRECT:
x = Symbol('x')
def verify_operator(p):
x = Symbol('x') # Always use Symbol inside functions too
return p.subs(x, 1) # Substitute values after creating expression
# INCORRECT:
def verify_operator(p):
return p # Passing raw numbers won't work
```
- For integration bounds:
```python
# CORRECT:
t = Symbol('t')
expr = t**2
result = integrate(expr, (t, 0, 1))
# INCORRECT:
result = integrate(2, (t, 0, 1)) # Can't integrate a number
```
2. Solving and Computing:
- Never use strings in solve() or other SymPy functions:
CORRECT: solve(eq, x)
INCORRECT: solve(eq, 'x')
- Define equations symbolically:
CORRECT: eq = 2*sqrt(h) - sqrt(12) + 5*k
INCORRECT: eq = 2*sqrt('h') - sqrt(12) + 5*k
3. Printing and Output:
- Include print statements for ALL calculations and results
- Print intermediate steps and final answers
- Print variable values after they are computed
- Use simple print statements instead of f-strings for SymPy expressions
- Print expressions with labels on separate lines:
```python
print("Expression label:")
print(expression)
```
4. Numeric Calculations:
- Use Float() for decimal numbers in calculations
- Use float() for final printing of results
- Avoid evalf() as it may cause errors
- For numeric results:
```python
result = expression.evalf()
print("Result:")
print(float(result))
```
5. Working with Series and Sequences:
- Use Float() for sequence terms
- Convert sums to float() before printing
- For series calculations, print intermediate terms
6. Matrix Operations and Systems of Equations:
- Never use symbolic variables as matrix indices:
```python
# CORRECT:
i, j = 0, 1 # Use integers for indexing
M = Matrix([[1, 2], [3, 4]])
element = M[i, j]
# INCORRECT:
x = Symbol('x')
element = M[x, 0] # This will raise an error
```
- For matrix analysis, always convert equations to Matrix form:
```python
# CORRECT:
A = Matrix([[1, 2], [3, 4]])
eigenvals = A.eigenvals()
# For system of equations:
x, y = symbols('x y')
system = Matrix([[2, 1], [1, -1]])
b = Matrix([5, 1])
solution = system.solve(b)
```
- For matrix operations with variables:
```python
# CORRECT:
x = Symbol('x')
M = Matrix([[x, 1], [2, 3]])
result = M * M # Matrix multiplication
# INCORRECT:
M[Symbol('i'), Symbol('j')] = x # Don't use symbolic indices
```
- For systems of equations that might be linearly dependent, use row reduction instead of matrix inversion. Here's the template for handling such systems:
7. Limit Calculations:
- ALWAYS compute one-sided limits (from the left and the right) when evaluating any limit:
```python
# Example:
x = Symbol('x')
expr = (sin(x)*cos(x) - x + x**3) / (x**3 * sqrt(1 + x) - x**3)
# Calculate the limit from the left (x -> 0-):
left_limit = limit(expr, x, 0, dir='-')
print("Limit from the left (x -> 0-):")
print(left_limit)
# Calculate the limit from the right (x -> 0+):
right_limit = limit(expr, x, 0, dir='+')
print("Limit from the right (x -> 0+):")
print(right_limit)
```
- After computing both one-sided limits, verify if they match:
```python
if left_limit == right_limit:
print("The two-sided limit exists and is:", left_limit)
else:
print("The two-sided limit does not exist.")
```
- If the limit diverges (\(\infty\) or \(-\infty\)), explicitly state that in the print output.
- For piecewise or discontinuous functions, compute limits at all points of interest, including boundaries.
- **Important Note**: Always test limits symbolically first. If SymPy produces unexpected results, simplify the expression or expand it (e.g., using `series`) before re-evaluating the limit.
8. If calculating integrals, do so directly with respect to the original expression and original variable of integration,
not any integral that might have been rewritten with a variable substitution
For indefinite integrals:
```python
# Just compute and display the symbolic result
x = Symbol('x')
expr = x**2
indefinite_integral = integrate(expr, x)
print("Indefinite integral:")
print(indefinite_integral)
```
NOTE: If there was an indefinite integral before you manipulated the expression, always try the above approach before proceeding.
For numerical verification, always use definite integrals:
```python
# Use specific bounds and verify numerically
definite_integral = integrate(expr, (x, 0, 1))
print("Definite integral value:")
print(float(definite_integral.evalf()))
# Include mpmath verification:
import mpmath
f = lambda x: x**2
mpmath_result = mpmath.quad(f, [0, 1])
print("mpmath verification:", float(mpmath_result))
```
9. If using SymPy to evalute an infinite sum, attempt using the infinite sum, and in addition report what the sum of the first 100 terms is as a sanity check.
10. Do not use SciPy.
11. Always use this template when working with systems of equations to handle potential linear dependence correctly.
```python
from sympy import Matrix, symbols, solve
def analyze_system(A, b):
'Analyze a system Ax = b using row reduction. Returns whether solution exists and if it's unique.'
# Augmented matrix [A|b]
aug = Matrix(A.row_join(b))
# Get row echelon form
rref, pivots = aug.rref()
print("Row reduced augmented matrix:")
print(rref)
print("\\nPivot columns:", pivots)
# Get rank of coefficient matrix and augmented matrix
rank_A = Matrix(A).rank()
rank_aug = aug.rank()
print(f"\\nRank of coefficient matrix: {rank_A}")
print(f"Rank of augmented matrix: {rank_aug}")
if rank_aug > rank_A:
print("\\nNo solution exists")
return None
elif rank_A < A.cols:
print("\\nInfinitely many solutions exist")
return "infinite"
else:
print("\\nUnique solution exists")
return "unique"
# When solving a system Ax = b:
A = Matrix([[...], [...], [...]]) # coefficient matrix
b = Matrix([[...], [...], [...]]) # right-hand side
# Analyze system
result = analyze_system(A, b)
if result == "infinite":
# Get parametric form of solution
aug = Matrix(A.row_join(b))
rref, pivots = aug.rref()
# Get free variables
vars = symbols('x y z') # adjust variable names as needed
free_vars = [var for i, var in enumerate(vars) if i not in pivots]
print("\\nParametric solution (t is free parameter):")
for i, var in enumerate(vars):
if i in pivots:
row = pivots.index(i)
expr = rref[row, -1]
for j, free_var in enumerate(free_vars):
expr -= rref[row, pivots[-1] + 1 + j] * free_var
print(f"{var} = {expr}")
else:
print(f"{var} = t") # use different parameter names for multiple free variables
```
"""
def load_proof_repository():
"""Load the proof repository from the repository file"""
repo_path = Path("Lebl-theorems-all.json")
try:
with open(repo_path, "r") as f:
return json.load(f)
except Exception as e:
logger.error(f"Error loading proof repository: {str(e)}")
return None
TOPIC_MAPPINGS = {
"integration": ["integral", "integrable", "riemann", "integrate", "antiderivative"],
"continuity": ["continuous", "discontinuous", "discontinuity", "uniformly continuous"],
"sequences": ["sequence", "convergent", "divergent", "monotone", "subsequence"],
"series": ["series", "sum", "convergent series", "power series"],
"differentiation": ["derivative", "differentiable", "differential"],
"limits": ["limit", "cluster point", "accumulation"],
"functions": ["function", "mapping", "surjective", "injective", "bijective"],
"bounded": ["bound", "bounded above", "bounded below", "supremum", "infimum"]
}
def get_related_terms(topic):
"""Get all related terms for a given topic"""
# Get direct mappings
related = TOPIC_MAPPINGS.get(topic.lower(), [])
# Add the original topic
related.append(topic.lower())
# Remove duplicates while preserving order
return list(dict.fromkeys(related))
def matches_topic(text, topic_terms):
"""Check if any topic terms appear in the text"""
text_lower = text.lower()
return any(term in text_lower for term in topic_terms)
def get_relevant_proofs(topic):
"""Get relevant proofs from repository based on topic, randomly selecting examples"""
repository = load_proof_repository()
if not repository:
logger.error("Failed to load proof repository")
return []
logger.debug(f"Searching for proofs related to topic: {topic}")
topic_terms = get_related_terms(topic)
logger.debug(f"Related terms: {topic_terms}")
relevant_proofs = []
for theorem in repository.get("dataset", {}).get("theorems", []):
# Check categories
categories = theorem.get("categories", [])
category_match = any(matches_topic(cat, topic_terms) for cat in categories)
# Check contents
contents = theorem.get("contents", [])
content_match = any(matches_topic(content, topic_terms) for content in contents)
# Check title
title = theorem.get("title", "")
title_match = matches_topic(title, topic_terms)
if (category_match or content_match or title_match):
if theorem.get("contents") and theorem.get("proofs"):
proof_content = {
"title": theorem.get("title", ""),
"contents": theorem.get("contents", []),
"proofs": [p.get("contents", []) for p in theorem.get("proofs", [])]
}
relevant_proofs.append(proof_content)
logger.debug(f"Found matching proof: {proof_content['title']}")
logger.debug(f"Matched via: {'categories' if category_match else 'contents' if content_match else 'title'}")
logger.debug(f"Found {len(relevant_proofs)} relevant proofs before sampling")
# Randomly select 3 proofs if we have more than 3
if len(relevant_proofs) > 3:
selected = random.sample(relevant_proofs, 3)
logger.debug("Selected proofs for enhancement:")
for proof in selected:
logger.debug(f"- {proof['title']}")
return selected
return relevant_proofs
def enhance_prompt_with_proofs(system_prompt, subject, topic):
"""Enhance the system prompt with relevant proofs if subject is Real Analysis"""
if subject != "Real Analysis":
logger.debug("Skipping proof enhancement - not Real Analysis")
return system_prompt
relevant_proofs = get_relevant_proofs(topic)
if not relevant_proofs:
logger.debug(f"No relevant proofs found for topic: {topic}")
return system_prompt
logger.debug(f"Enhancing prompt with {len(relevant_proofs)} proofs")
# Add proof examples to the prompt
proof_examples = "\n\nReference these proof examples for style and approach:\n"
for proof in relevant_proofs:
logger.debug(f"Adding proof: {proof['title']}")
proof_examples += f"\nTheorem: {proof['title']}\n"
proof_examples += "Statement: " + " ".join(proof['contents']) + "\n"
if proof['proofs']:
first_proof = " ".join(proof['proofs'][0])
logger.debug(f"Proof length: {len(first_proof)} characters")
proof_examples += "Proof: " + first_proof + "\n"
# Add specific instructions for using the examples
enhanced_prompt = f"""{system_prompt}
ADDITIONAL PROOF GUIDELINES:
1. Consider the following proof examples from an established textbook
2. Maintain similar level of rigor and detail
3. Use similar proof techniques where applicable
4. Follow similar notation and presentation style
{proof_examples}"""
return enhanced_prompt
def create_latex_document(content, questions_only=False):
"""Create a complete LaTeX document"""
try:
latex_header = r"""\documentclass{article}
\usepackage{amsmath,amssymb}
\usepackage[margin=1in]{geometry}
\begin{document}
\title{Mathematics Question}
\maketitle
"""
latex_footer = r"\end{document}"
if questions_only:
# Find where the question ends and solution begins
question_marker = "Here is a test question"
solution_marker = "Here is a detailed solution"
q_start = content.find(question_marker)
s_start = content.find(solution_marker)
if q_start != -1 and s_start != -1:
# Extract only the question part
question_content = content[q_start:s_start].strip()
# Remove any SymPy code or verification text if present
code_start = question_content.find('```python')
if code_start != -1:
question_content = question_content[:code_start].strip()
content = question_content
else:
# Fallback: try to split at the first occurrence of "Solution:"
parts = content.split('Solution:', 1)
content = parts[0].strip()
full_document = f"{latex_header}\n{content}\n{latex_footer}"
logger.debug(f"Created {'questions-only' if questions_only else 'full'} LaTeX document")
return full_document
except Exception as e:
logger.error(f"Error creating LaTeX document: {str(e)}")
raise
def save_to_temp_file(content, filename):
"""Save content to a temporary file and return the path"""
try:
temp_dir = Path(tempfile.gettempdir()) / "math_test_files"
temp_dir.mkdir(exist_ok=True)
file_path = temp_dir / filename
file_path.write_text(content, encoding='utf-8')
logger.debug(f"Saved content to temporary file: {file_path}")
return str(file_path)
except Exception as e:
logger.error(f"Error saving temporary file: {str(e)}")
raise
def get_problem_type_addition(question_type):
"""Return specific requirements based on problem type"""
problem_type_additions = {
"application": """
The application question MUST:
- Present a real-world scenario or practical problem
- Require modeling the situation mathematically
- Connect abstract mathematical concepts to concrete situations
- Include realistic context and data
- Require students to:
1. Identify relevant mathematical concepts
2. Translate the practical problem into mathematical terms
3. Solve using appropriate mathematical techniques
4. Interpret the results in the context of the original problem
- Randomly select one of these topic areas with equal probability
* Physics applications (motion, forces, work)
* Engineering scenarios
* Economics problems
* Biological systems
* Business applications
* Social science applications
* Data science applications
""",
"proof": """
The proof question MUST:
- Require a formal mathematical proof
- Focus on demonstrating logical reasoning
- Require justification for each step
- Emphasize theoretical understanding
The proof question MAY NOT:
- Include Real-world applications or scenarios
- Include Pure computation problems
- Ask only for numerical answers
""",
"computation": """
The computation question MUST:
- Be a Pure computation problem
- Require specific algebraic calculations
- Focus on mathematical techniques
- Have concrete answers in the form of algebraic expressions (about half of questions) or numbers (about half of questions)
- Test procedural knowledge
The computation question MAY NOT:
- Include extended real-world applications or scenarios
- Ask for a proof
- Require numerical analysis that would only be possible to solve with a computer
"""
}
return problem_type_additions.get(question_type, "")
def get_solution_for_verification(response_text, SYMPY_CONFIRMED, final_verification=None):
"""
Extract the relevant parts of the solution for verification based on whether
the original solution was correct or not. Always preserves the original question.
Returns tuple of (question, solution).
"""
# Extract the question using the specific markers
question_start = "Here is a test question"
solution_start = "Here is a detailed solution to the test question"
# Find the question section
q_start = response_text.find(question_start)
q_end = response_text.find(solution_start)
if q_start == -1 or q_end == -1:
logger.error("Could not find question markers")
# Return best effort split - assume first paragraph is question
paragraphs = response_text.split('\n\n')
if len(paragraphs) > 1:
return paragraphs[0].strip(), '\n\n'.join(paragraphs[1:]).strip()
else:
# If we can't even split paragraphs, just return the whole text as both question and solution
return response_text.strip(), response_text.strip()
# Extract question
question = response_text[q_start:q_end].strip()
# If we have a final verification solution, use that
if final_verification:
marker = "Here is the complete verified solution:"
if marker in final_verification:
solution = final_verification.split(marker)[1].strip()
logger.debug("Using final verified solution for verification")
return question, solution
else:
# If marker not found in final verification, extract solution part
logger.debug("Marker not found in final verification, using full verification text")
return question, final_verification.strip()
# Otherwise, extract original solution (before SymPy code if present)
original_solution = response_text[q_end:]
sympy_start = original_solution.find('```python')
if sympy_start != -1:
solution = original_solution[:sympy_start].strip()
else:
solution = original_solution.strip()
logger.debug("Using original solution for verification")
return question, solution
def verify_with_chatgpt(question, solution):
"""
Send the solution to ChatGPT for verification and grading.
Returns the verification response.
"""
try:
# Extract SymPy verification if it exists
sympy_start = solution.find("Here's the SymPy code")
sympy_end = solution.find("Verification Analysis:")
sympy_section = ""
main_solution = solution
if sympy_start != -1 and sympy_end != -1:
sympy_section = solution[sympy_start:sympy_end].strip()
main_solution = solution[:sympy_start].strip()
# Construct the prompt for ChatGPT
verification_prompt = f"""As an expert mathematician, please verify and grade this mathematics solution.
Analyze the following aspects:
1. Mathematical Correctness (50 points):
- Are all calculations correct?
- Are proofs logically sound?
- Are all steps properly justified?
2. Completeness (20 points):
- Are all necessary cases considered?
- Are edge cases addressed?
- Are all required steps shown?
3. Clarity and Presentation (20 points):
- Is the solution well-organized?
- Are steps clearly explained?
- Is mathematical notation used correctly?
4. Mathematical Sophistication (10 points):
- Is the approach elegant?
- Are efficient methods used?
- Is mathematical insight demonstrated?
Question:
{question}
Student's Solution:
{main_solution}
{f"The student also provided the following SymPy verification of their solution: {sympy_section}" if sympy_section else ""}
Please consider this in your assessment of the work.
Please provide:
1. A brief point-by-point analysis of the solution
2. Specific comments on any errors or oversights
3. Suggestions for improvement (if any)
4. A numerical score out of 100 based on the criteria above
5. Finally, if and only if the numerical score is less than 90 out of 100, present a complete revised solution to which you would award a score of 100.
The complete revised solution if you provide one must show ALL steps and be fully self-contained.
Important Formatting Instructions:
- Put each solution step on its own line in $$ $$
- For inline math expressions, always use $ $, do not use the LaTeX bracket notation, do not use backslash ( ), do not use backslash [ ]
- DO NOT use begin{{aligned}} or similar environments
- For currency amounts in dollars, write out the word dollars instead of using $
* Example: 1000 dollars
- make sure there are never unmatched dollar signs as that will ruin the LaTeX rendering
Before marking a mathematical expression as incorrect:
- Check if the expression could be equivalent to what you think it should be through algebraic manipulation
- Write out the full steps to verify non-equivalence
- Consider multiple valid ways to write the same mathematical relationship
- Only mark expressions as wrong if you can prove they are not equivalent to the correct form
- When grading mathematical solutions:
Format your response with clear headers and bullet points."""
# Call OpenAI API
response = openai.chat.completions.create(
model="o1-preview",
messages=[
{
"role": "assistant",
"content": "I am an expert mathematics professor who grades student solutions using LaTeX formatting. When writing inline math, I always write math using LaTeX $ $ delimiters - NEVER the backslash parentheses or backslash brackets approach. I am FORBIDDEN from ever using any notation that uses backslash followed by open parenthesis, backslash followed by open bracket, or backslash followed by square bracket. For displayed equations, I use $$ $$."
},
{
"role": "user",
"content": verification_prompt
}
],
temperature=1
)
# Extract the verification text from the response
verification_text = response.choices[0].message.content
return verification_text
except Exception as e:
logger.error(f"Error in ChatGPT verification: {str(e)}")
return f"Error in ChatGPT verification: {str(e)}"
def append_chatgpt_verification(initial_response, SYMPY_CONFIRMED=None, final_verification=None):
"""
Main function to handle the ChatGPT verification process.
Now handles cases where SymPy verification wasn't performed.
"""
try:
# Get the appropriate solution text for verification
question, solution_text = get_solution_for_verification(initial_response, SYMPY_CONFIRMED, final_verification)
# Get ChatGPT's verification
chatgpt_verification = verify_with_chatgpt(question, solution_text)
if chatgpt_verification:
full_response = f"{initial_response}\n\nChatGPT Verification and Grading:\n{chatgpt_verification}"
return full_response
return initial_response
except Exception as e:
logger.error(f"Error in verification process: {str(e)}")
return initial_response + f"\n\nError in ChatGPT verification: {str(e)}"
def generate_question(subject, difficulty, question_type, subtopic=None, use_enhancement=False, include_chatgpt="no"):
"""Generate a single math question with additional verification"""
try:
logger.debug(f"ChatGPT verification: {'enabled' if include_chatgpt == 'yes' else 'disabled'}")
if not os.environ.get('ANTHROPIC_API_KEY'):
logger.error("Anthropic API key not found")
return "Error: Anthropic API key not configured", None, None
logger.debug(f"Generating {question_type} question for subject: {subject} at difficulty level: {difficulty}")
logger.debug(f"Textbook enhancement: {'enabled' if use_enhancement else 'disabled'}")
# Check rate limit
now = datetime.now()
while request_history and (now - request_history[0]) > timedelta(days=1):
request_history.popleft()
if len(request_history) >= MAX_REQUESTS_PER_DAY:
return "Daily request limit reached. Please try again tomorrow.", None, None
request_history.append(now)
selected_topic = subtopic if subtopic else random.choice(SUBJECT_SUBTOPICS[subject])
logger.debug(f"Using selected subtopic: {selected_topic}")
problem_type_addition = get_problem_type_addition(question_type)
system_prompt = f"""You are a mathematics professor.
Part I. Write 10 {question_type} exam questions that can be solved analytically, without numerical methods,
in increasing order of difficulty that rigorously test a student's mastery on the topic {selected_topic} in {subject}
at the level that would be expected on a final exam at a top tier university (Stanford, Harvard, MIT, Caltech, Oxford, etc).
The easiest question should still require at least one step beyond direct formula recognitition.
The medium questions must require multiple techniques and require mastery of relevant mathematical concents.
The hardest two questions must require advance techniques would be very tricky even for an undergraduate mathematics major at a top university, but still must be analytically solvable.
Observe the following about {question_type} questions: {problem_type_addition}.
Note: If you specify a quadrant restriction (e.g. "in the first quadrant") in a problem with calculating area between lines/curves or volume between surfaces, make sure the question is CLEAR about what regions you intend to be included in the solution, by breaking up the question. Examples:
- NOT CLEAR question: Find the area of the region bounded by the curves y = sin(x), y = cos(x), and x = 7*pi/4 in the first quadrant.
- CLEAR question: Find the area of the region bounded by the curves y = sin(x), y = cos(x), and x = 7*pi/4. Then find the area of that region that intersects with the first quadrant.
Part II. Now select the problem that is number {difficulty} on your exam, state the question again and provide a solution. You MUST do this part as well, do not tell me to prompt again.
1. Begin the output for Part II with the text "Here is a test question that is a {question_type} question on {subject} covering {selected_topic} of difficulty level {difficulty} out of 10."
2. Important LaTeX formatting for both Part I and Part II
- Make sure that the question statement uses proper LaTeX math mode
- Use $ for inline math
- Use $$ on separate lines for equations and solutions
- Put each solution step on its own line in $$ $$
- DO NOT use \\begin{{aligned}} or similar environments
- When writing questions involving currency expressed in dollars NEVER use the `$` symbol as it will be interepreted as math mode. ALWAYS write out the word dollars.
* Example: 1000 dollars
3. For the detailed soltuion
- Begin the solution with "Here is a detailed solution to the test question."
- If the question involves geometry make sure to identify any general geometric formulas that apply, For example:
* Areas/volumes of common shapes and solids
* Cross-sectional areas of geometric figures
* Arc lengths and sector areas
- When setting up differential equations either in calculus or differential equation applications
* carefully consider the direction of change in variables
* ensure integration bounds align with the physical direction of the process being modeled
- The solution must be analytical. It must not rely on numerical methods.
* NO part of the solution may resort to or be based on numerical analysis.
* The only numerical calculations that should be done are those that could be done on a simple scientific calculator.
* Make sure to simplify completely as far as analytical methods will allow
4. Maintain clear formatting
5. For Computation or Application questions (NOT Proof questions): at the end of the solution output, print SymPy code that you would use to solve or verify the main equations in the question.
Observe the folloiwng SymPy Guidelines
{SYMPY_GUIDELINES}
6. For problems where the subject is Real Analysis, observe the following guidelines:
a. **Justify Every Step**
- Provide detailed reasoning for each step and explicitly justify every bounding argument, inequality, or limit claim.
- If concluding that terms vanish in a limit, clearly explain why.
- When using supremum/infimum, justify its behavior under limits, differentiation, or integration, ensuring it does not introduce discontinuities.
b. **Handling Limits and Differentiability**
- In epsilon-delta proofs, clearly explain why the chosen delta works.
- When using limit substitutions, justify why the transformation preserves limit behavior.
- If verifying differentiability, explicitly state why continuity at that point is necessary and confirm that continuity has been established before proceeding.
- If proving continuity or differentiability, check symmetry in approach from both sides (left-hand and right-hand limits or derivatives).
c. **Function Definitions and Explicit Statements**
- When proving continuity, explicitly confirm that f(x) is **defined** at the point of interest and state what its value is.
- If a function is given piecewise, clearly state the function values at transition points before evaluating limits.
d. **Limit Justifications and Transitions**
- When using standard limits briefly justify why it applies
- If a limit is computed informally before a formal epsilon-delta proof, explicitly state that the formal proof serves to confirm the computed limit rigorously.
- Ensure smooth logical transitions between different parts of the proof by briefly explaining why one step leads naturally to the next.
e. **Function Properties and Integrability**
- If stating that a function is Riemann integrable, compact, or uniformly continuous, explain why.
- If claiming a function is continuous for all x not equal to zero, explicitly justify why using function composition, bounded functions, or known theorems.
- When assuming an integral is finite, provide justification based on function class properties (e.g., Riemann integrability implies boundedness).
f. **Inequalities and Asymptotics**
- When using inequalities (e.g., Hölder’s, Jensen’s), explain why they apply and what function properties make them relevant.
- If using factorial ratios or infinite series sums, explicitly state their rate of convergence and reference known bounds (e.g., Stirling’s approximation).
g. **Uniform Convergence and Sequence Behavior**
- When proving uniform convergence, ensure that the bound obtained is independent of x to establish uniform control.
- If using asymptotic behavior (e.g., factorial ratios tending to zero), provide explicit justification rather than just stating the result.
h. **Clarify the Use of Key Theorems (e.g., Squeeze Theorem)**
- When using the squeeze theorem, clearly state why both bounding functions tend to the same limit and explicitly apply the theorem in the conclusion.
i. **Logical Flow and Transitions**
- After major steps (e.g., computing a limit, verifying continuity), summarize why the step was necessary and how it connects to the next part of the proof.
- If transitioning from an informal calculation to a formal proof, explicitly state the purpose of the formal proof in confirming the earlier result.
j. **Concluding and Intuitive Explanations**
- Conclude with an intuitive explanation of why the result makes sense, possibly connecting it to known theorems or simple examples.
- In notes after the proof, highlight potential sources of confusion for students and clarify tricky aspects of the problem.
7. When finding critical points in multivariable calculus:
- Always check what happens when any variable equals zero (except where undefined)
- Just because a point is ruled out of the domain doesn't mean that entire line/curve is ruled out
- When the Hessian is inconclusive, evaluate the function along the critical curves to determine behavior
- Don't rely solely on the Hessian - consider direct function evaluation and nearby points
8. When using symmetry arguments:
- Explicitly state what is symmetric
- Identify the axis/plane of symmetry
9. In calculus do not forget opportunities to apply power-reduction formulas for trig functions
- e.g.: Integral from 0 to pi of [cos(theta)]^(2n) d(theta) = (pi / 2^(2n)) * (2n choose n), where choose is the combinatorial choose function
10. In expanding or factoring polynomial expressions, be careful not to make errors
- (1+u^2)^2 is equal to (1+2u^2+u^4), NOT equal to (1+3u^2+u^4)
11. When finding points where dy/dx = 0 in parametric equations:
- (a) First find the ratio dy/dx = (dy/dt)/(dx/dt)
- (b) Find t-values where this ratio equals 0
- (c) CRITICAL: For any t-values where both dy/dt = 0 AND dx/dt = 0:
- (d) These are potential "corner points" or cusps
- (e) Must apply parametric L'Hôpital's rule:
* Define f(t) = dy/dt and g(t) = dx/dt
* If f(t₀) = g(t₀) = 0, evaluate lim[t→t₀] f'(t)/g'(t)
* This limit, if it exists, gives the actual slope at t₀
Only conclude the tangent is horizontal if this limit equals 0
- (f) For each solution t:
- Calculate the corresponding point (x(t), y(t))
- Verify the point lies on the curve
- State whether it's a regular point or special point (cusp, corner, etc.)
12. Be careful with trigonometric expressions involving powers
- Example: solving sin^2(x)=cos(x) can be solved as 1-cos^2(x)=cos(x)
"""
#Consider
#When writing SymPy code:
#- Use FiniteSet(1, 2, 3) instead of Set([1, 2, 3]) for finite sets
#- Import specific functions instead of using 'from sympy import *'
#- Print results of each calculation step
# Only enhance the prompt if explicitly requested AND it's a Real Analysis proof
if use_enhancement and subject == "Real Analysis" and question_type == "proof":
logger.debug("Applying textbook enhancement to prompt")
system_prompt = enhance_prompt_with_proofs(system_prompt, subject, selected_topic)
else:
logger.debug("Skipping textbook enhancement")
logger.debug("Sending request to Anthropic API")
message = anthropic.messages.create(
model = "claude-3-5-sonnet-20241022",
max_tokens=4096,
temperature=0.9,
messages=[{
"role": "user",
"content": system_prompt
}]
)
if not hasattr(message, 'content') or not message.content:
logger.error("No content received from Anthropic API")
return "Error: No content received from API", None, None
response_text = message.content[0].text
logger.debug("Successfully received response from Anthropic API")
# Initialize verification variables
sympy_output = None
has_discrepancy = False
SYMPY_CONFIRMED = None
revised_solution = None
final_verification = None
# Execute SymPy code if present
sympy_output = extract_and_run_sympy_code_simple(response_text)
# Only proceed with verification if SymPy output exists
if sympy_output is not None: # Add this check
if "Error" in sympy_output:
verification_text = "SymPy verification failed with error. Manual solution must be verified independently."
SYMPY_CONFIRMED = "Inconclusive"
response_text = f"{response_text}\n\nSymPy Verification Results:\n```\n{sympy_output}\n```\n\nVerification Analysis:\n{verification_text}"
else:
resolution_text, has_discrepancy, revised_solution, SYMPY_CONFIRMED = check_and_resolve_discrepancy(response_text, sympy_output)
response_text = f"{response_text}\n\nSymPy Verification Results:\n```\n{sympy_output}\n```\n\nVerification Analysis:\n{resolution_text}"
if has_discrepancy and revised_solution:
final_verification = perform_final_verification(revised_solution, SYMPY_CONFIRMED)
response_text += "\n\nFinal Expert Verification:\n" + final_verification
# add the ChatGPT verification
if include_chatgpt == "yes":
response_text = append_chatgpt_verification(
response_text,
SYMPY_CONFIRMED if sympy_output is not None else None,
final_verification if sympy_output is not None and has_discrepancy else None
)
# Create LaTeX content
questions_latex = create_latex_document(response_text, questions_only=True)
full_latex = create_latex_document(response_text, questions_only=False)
# Save to temporary files
questions_path = save_to_temp_file(questions_latex, "question.tex")
full_path = save_to_temp_file(full_latex, "full_question.tex")
logger.debug("Successfully created temporary files")
return response_text, questions_path, full_path
except Exception as e:
logger.error(f"Error generating question: {str(e)}")
return f"Error: {str(e)}", None, None
def extract_and_run_sympy_code_simple(response_text):
"""
Extract SymPy code from the response and execute it.
"""
try:
# Extract code
sympy_start = response_text.find('```python')
if sympy_start == -1:
return None # Changed from error message to None
code_start = response_text.find('\n', sympy_start) + 1
code_end = response_text.find('```', code_start)
if code_end == -1:
return None # Changed from error message to None
sympy_code = response_text[code_start:code_end].strip()
# Import SymPy at the module level
import sympy
# Create globals dict with all SymPy functions
globals_dict = {}
globals_dict.update(vars(sympy))
globals_dict.update({
'print': print,
'float': float,
'Symbol': sympy.Symbol,
'symbols': sympy.symbols,
'solve': sympy.solve,
'sqrt': sympy.sqrt,
'pi': sympy.pi,
'diff': sympy.diff,
'integrate': sympy.integrate,
'simplify': sympy.simplify,
'Matrix': sympy.Matrix
})
# Remove the sympy import line from the code if present
lines = sympy_code.split('\n')
filtered_lines = [line for line in lines if not line.strip().startswith('from sympy import') and not line.strip().startswith('import sympy')]
modified_code = '\n'.join(filtered_lines)
# Capture output
import io
from contextlib import redirect_stdout
output_buffer = io.StringIO()
with redirect_stdout(output_buffer):
exec(modified_code, globals_dict)
return output_buffer.getvalue().strip() or "No output produced"
except Exception as e:
return f"Error executing SymPy code: {str(e)}"
def check_and_resolve_discrepancy(initial_response, sympy_output):
"""
Compare the SymPy output with the initial response and resolve any discrepancies.
Returns tuple of (resolution_text, has_discrepancy, revised_solution)
"""
has_discrepancy = False #Initialize
resolution_text = ""
revised_solution = None
SYMPY_CONFIRMED = None # Initialize at the start
try:
resolution_prompt = f"""Here is a mathematics question with two answers.
1. First, write out both answers:
- Original solution: [write the final answer]
- SymPy solution: [write the SymPy answer]
2. To prove equivalence, you MUST do at least ONE of the following:
a) Algebraically transform one expression into the other through valid steps
b) Show that they evaluate to the sane number.
NOTE: Before you assert that the SymPy and Original solution evaluate to the same number, CAREFULLY calculate the value of each. If the expressions are not identical in symbolic form, double check: CALCULATE each solution CORRECTLY out to 10 decimal places and compare the results.
3. For special functions (like hypergeometric functions):
- Do not assume equivalence without verification
- Use series expansions or numerical evaluation at test points if needed
- Explicitly state if you cannot verify equivalence
4. After your analysis, conclude ONE of the following:
If equivalence is PROVEN:
- Write "SYMPY_CONFIRMED: True" on its own line (this means SymPy's output CONFIRMS the original solution)
- Explain exactly how you proved equivalence
- Show all steps of the verification
If equivalence CANNOT be proven:
- Write "SYMPY_CONFIRMED: False" on its own line
- Explain why equivalence cannot be established including errors you now detect in the original solution.
- Write "Here is the revised complete solution:" and then write out an ENTIRE correct solution from beginning to end showing all steps.
Do not try to shorten the text by saying that certain steps are the same as in the original solution. Write them out again.
Do not try to shorten the text by saying "the rest remains the same". Write it out again.
Under "Here is the revised complete solution:" there should be a complete solution to the question in full from beginning to end.
If verification is INCONCLUSIVE:
- Write "SYMPY_CONFIRMED: Inconclusive" on its own line
- Explain why equivalence cannot be determined
- Request a new SymPy verification with additional checks
Never claim solutions match without showing explicit mathematical proof of equivalence.
Please maintain the same LaTeX formatting as the original solution.
Original solution:
{initial_response}
SymPy Verification Results:
{sympy_output}
Please maintain the same LaTeX formatting as the original solution."""
# Make API call for resolution
message = anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=4096,
temperature=0.2,
messages=[{
"role": "user",
"content": resolution_prompt
}]
)
resolution_text = message.content[0].text
# Check if resolution contains new SymPy code
if "```python" in resolution_text:
new_sympy_output = extract_and_run_sympy_code_simple(resolution_text)
resolution_text += "\n\nNew SymPy Verification Results:\n```\n" + new_sympy_output + "\n```"
# Determine if there was a discrepancy that required a revised solution
# Check for any indication of inconsistency or error
inconsistency_phrases = [
"inconsistent", "inconsistency", "incorrect", "error", "wrong",
"discrepancy", "mistaken", "mistake"
]
has_discrepancy = any(phrase in resolution_text.lower() for phrase in inconsistency_phrases)
# Look for the required marker phrase and extract the solution after it
marker = "Here is the revised complete solution:"
revised_solution = None
if has_discrepancy:
# Split at the marker
if marker in resolution_text:
parts = resolution_text.split(marker, maxsplit=1)
if len(parts) > 1:
revised_solution = parts[1].strip()
# If the solution seems too short (might be partial), don't accept it
if len(revised_solution) < 100: # Rough minimum length for a complete solution
revised_solution = None
# If we didn't find a complete solution, force a recheck
if not revised_solution:
logger.debug("Initial solution extraction failed, requesting a complete solution")
# Make a new API call specifically requesting a complete solution
complete_solution_prompt = f"""The previous solution had inconsistencies. Please provide a complete solution
from beginning to end. Start your response with exactly this phrase:
"Here is the revised complete solution:"
Then write out the entire solution, including all parts both correct and corrected.
Do not refer to the original solution or say any parts remain the same.
Original problem and verification results:
{initial_response}
SymPy Results:
{sympy_output}"""
try:
message = anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=4096,
temperature=0.2,
messages=[{"role": "user", "content": complete_solution_prompt}]
)
new_response = message.content[0].text
if marker in new_response:
parts = new_response.split(marker, maxsplit=1)
if len(parts) > 1:
revised_solution = parts[1].strip()
except Exception as e:
logger.error(f"Error in solution recheck: {str(e)}")
# Parse whether SymPy was correct
SYMPY_CONFIRMED = None
if "SYMPY_CONFIRMED: True" in resolution_text:
SYMPY_CONFIRMED = True
elif "SYMPY_CONFIRMED: False" in resolution_text:
SYMPY_CONFIRMED = False
return resolution_text, has_discrepancy, revised_solution, SYMPY_CONFIRMED
except Exception as e:
logger.error(f"Error in discrepancy resolution: {str(e)}")
resolution_text = f"Error in resolution: {str(e)}"
has_discrepancy = False # Explicitly set in error case
revised_solution = None
return resolution_text, has_discrepancy, revised_solution, SYMPY_CONFIRMED
def perform_final_verification(revised_solution, SYMPY_CONFIRMED):
"""
Perform a final verification of the revised solution.
"""
verification_prompt = f"""As an expert mathematician, please carefully verify this revised solution to an advanced mathematics problem.
Revised Solution to Verify:
{revised_solution}
Please follow these steps exactly:
1. First, analyze the solution for:
- Mathematical correctness
- Missing cases or assumptions
- Completeness and rigor
- Necessary conditions and edge cases
- Any subtle errors or oversights
2. Write exactly this phrase to begin your analysis:
"Here is the complete verified solution:"
3. Then write out the ENTIRE solution from beginning to end, including:
- All correct parts from the original solution
- All needed corrections
- All additional cases and verifications
- Any missing steps or assumptions
- Any necessary additional proofs or derivations
4. The answer aligns with the {'SymPy' if SYMPY_CONFIRMED else 'original'} answer proven correct
Your complete solution must:
- Be completely self-contained, do not assume the reader has read the previous solution
- Not refer to the original solution, again do not assume the reader has read the previous solution
- Show every step of the calculation
- Include all necessary verifications
- Maintain proper LaTeX formatting with $ for inline math and $ on separate lines
- When referring to the dollar as a currency, never use the `$` symbol but rather write out the word dollar
Remember to write out the complete solution even if you are repeating things from the first solution - the goal is to have a single, complete, verified solution, INCLUDING all details and full mathematical rigor, and WITHOUT assuming the reader has read any of the previous solution material.
"""
try:
# Make API call for final verification
message = anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=4096,
temperature=0.2,
messages=[{
"role": "user",
"content": verification_prompt
}]
)
verification_result = message.content[0].text
# If verification includes new SymPy code, run it
if "```python" in verification_result:
new_sympy_output = extract_and_run_sympy_code_simple(verification_result)
verification_result += "\n\nFinal SymPy Verification:\n```\n" + new_sympy_output + "\n```"
return verification_result
except Exception as e:
logger.error(f"Error in final verification: {str(e)}")
return f"Error in final verification: {str(e)}"
# Add this function to update subtopic choices
def update_subtopics(subject):
logger.debug(f"update_subtopics called with subject: {subject}")
if subject in SUBJECT_SUBTOPICS:
choices = SUBJECT_SUBTOPICS[subject]
logger.debug(f"Found subtopics: {choices}")
return gr.Dropdown(choices=choices, visible=True)
logger.debug("No subtopics found for subject")
return gr.Dropdown(choices=[], visible=False)
# Create Gradio interface
with gr.Blocks() as interface:
gr.Markdown("# Advanced Mathematics Question Generator")
gr.Markdown("""Generates 10 university-level mathematics questions using Claude 3.5 Sonnet.
Solves the question of selected difficulty with Claude 3.5 Sonnet and SymPy verification.
Final verification possible with ChatGPT o1-Preview toggle.
Limited to 25 requests per day.""")
with gr.Row():
with gr.Column():
subject_dropdown = gr.Dropdown(
choices=[""]+list(SUBJECT_SUBTOPICS.keys()), # Use dictionary keys
label="Select Mathematics Subject",
info="Choose a subject for the question"
)
# Add this after subject_dropdown
subtopic_dropdown = gr.Dropdown(
choices=[], # Empty initially
label="Select Subtopic",
info="Choose a specific topic within the subject",
)
# Connect the update function
subject_dropdown.change(
update_subtopics,
inputs=[subject_dropdown],
outputs=[subtopic_dropdown]
)
difficulty_slider = gr.Slider(
minimum=1,
maximum=10,
step=1,
value=5,
label="Difficulty Level",
info="1: Very Easy, 5: Moderate, 10: Very Difficult"
)
question_type = gr.Radio(
choices=["computation", "proof", "application"],
label="Question Type",
info="Select the type of question you want",
value="computation"
)
# Add ChatGPT verification toggle
chatgpt_verify = gr.Radio(
choices=["yes", "no"],
label="Include ChatGPT Verification",
info="Enable/disable ChatGPT grading (disable saves credits)",
value="yes"
)
# enhancement checkbox
use_enhancement = gr.Radio(
choices=["yes", "no"],
label="Enhance with Textbook Material if Real Analysis",
info="Include relevant textbook examples to guide question generation",
value="no"
)
generate_btn = gr.Button("Generate Question")
output_text = gr.Markdown(
label="Generated Question Preview",
latex_delimiters=[
{"left": "$$", "right": "$$", "display": True},
{"left": "$", "right": "$", "display": False}
]
)
with gr.Row():
questions_file = gr.File(label="Question Only (LaTeX)")
full_file = gr.File(label="Question with Solution (LaTeX)")
# Update the click event to include the new parameter
generate_btn.click(
generate_question,
inputs=[
subject_dropdown,
difficulty_slider,
question_type,
subtopic_dropdown, # Add this
use_enhancement,
chatgpt_verify
],
outputs=[output_text, questions_file, full_file]
)
if __name__ == "__main__":
logger.info("Starting application")
interface.launch() |