Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import sympy as sp | |
| import numpy as np | |
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| from mpmath import mp | |
| from io import BytesIO | |
| import base64 | |
| import ast | |
| st.set_page_config(page_title="Advanced Math Calculator", layout="wide") | |
| # -------------------------- | |
| # Utilities | |
| # -------------------------- | |
| def parse_sympy(expr_str): | |
| """Parse a user's expression into a SymPy expression, with safe locals.""" | |
| try: | |
| # Add common symbols/functions | |
| local_dict = {name: getattr(sp, name) for name in ['sin','cos','tan','asin','acos','atan','log','ln','exp','sqrt','pi','E'] if hasattr(sp, name)} | |
| # allow 'x' as default symbol | |
| x = sp.symbols('x') | |
| local_dict['x'] = x | |
| expr = sp.sympify(expr_str, locals=local_dict) | |
| return expr, None | |
| except Exception as e: | |
| return None, str(e) | |
| def pretty(e): | |
| try: | |
| return sp.pretty(e) | |
| except Exception: | |
| return str(e) | |
| def download_link(content: str, filename: str, mode='text'): | |
| b = content.encode('utf-8') | |
| b64 = base64.b64encode(b).decode() | |
| href = f"data:application/octet-stream;base64,{b64}" | |
| return href | |
| # -------------------------- | |
| # App UI | |
| # -------------------------- | |
| st.title("🧮 Advanced Math Calculator — Streamlit") | |
| st.write("Solve symbolic & numeric math: algebra, calculus, matrices, plotting, and more.") | |
| col1, col2 = st.columns([2,1]) | |
| with col2: | |
| st.header("Options") | |
| mode = st.selectbox("Operation type", [ | |
| "Evaluate Expression", | |
| "Solve Equation (single)", | |
| "Solve System of Equations", | |
| "Derivative", | |
| "Integral", | |
| "Limit", | |
| "Series Expansion", | |
| "Simplify / Factor / Expand", | |
| "Matrix Operations", | |
| "Numerical Solve (nsolve)", | |
| "Plot Function" | |
| ]) | |
| show_steps = st.checkbox("Show symbolic steps / details (when available)", value=True) | |
| extra_precision = st.number_input("Numeric precision (mpmath) (digits)", min_value=15, max_value=80, value=30) | |
| with col1: | |
| st.header(mode) | |
| user_input = st.text_area("Enter expression / equation / matrix (use Python-like syntax)", height=140) | |
| if st.button("Compute"): | |
| mp.mp.dps = int(extra_precision) | |
| if mode == "Evaluate Expression": | |
| expr, err = parse_sympy(user_input) | |
| if err: | |
| st.error(f"Parse error: {err}") | |
| else: | |
| st.subheader("Symbolic Result") | |
| st.code(pretty(expr)) | |
| try: | |
| numeric = sp.N(expr) | |
| st.subheader("Numeric Approximation") | |
| st.write(numeric) | |
| except Exception as e: | |
| st.write("Numeric eval error:", e) | |
| elif mode == "Solve Equation (single)": | |
| # Expecting something like '2*x+1=0' or 'sin(x)-1/2=0' | |
| if '=' not in user_input: | |
| st.error("Please provide an equation with '=' (for example: 2*x+1=0)") | |
| else: | |
| left, right = user_input.split('=',1) | |
| L, errL = parse_sympy(left) | |
| R, errR = parse_sympy(right) | |
| if errL or errR: | |
| st.error(errL or errR) | |
| else: | |
| eq = sp.Eq(L, R) | |
| x = list(eq.free_symbols) | |
| try: | |
| if len(x) == 0: | |
| st.write("No variables found in equation. Result:") | |
| st.write(sp.solve(eq)) | |
| elif len(x) == 1: | |
| sol = sp.solve(eq, x[0]) | |
| st.subheader("Solutions") | |
| st.write(sol) | |
| if show_steps: | |
| st.subheader("SymPy representation") | |
| st.code(sp.srepr(sol)) | |
| else: | |
| st.info("Multiple variables detected; consider using 'Solve System of Equations'") | |
| except Exception as e: | |
| st.error(f"Solve error: {e}") | |
| elif mode == "Solve System of Equations": | |
| # Expect lines of equations separated by newline | |
| lines = [ln.strip() for ln in user_input.splitlines() if ln.strip()] | |
| eqs = [] | |
| errors = [] | |
| for ln in lines: | |
| if '=' not in ln: | |
| errors.append(f"Line missing '=': {ln}") | |
| continue | |
| Ls, Rs = ln.split('=',1) | |
| L, e1 = parse_sympy(Ls) | |
| R, e2 = parse_sympy(Rs) | |
| if e1 or e2: | |
| errors.append(e1 or e2) | |
| else: | |
| eqs.append(sp.Eq(L, R)) | |
| if errors: | |
| st.error(' | |
| '.join(errors)) | |
| else: | |
| vars = sorted(list({s for eq in eqs for s in eq.free_symbols}), key=lambda s: str(s)) | |
| try: | |
| sol = sp.solve(eqs, vars, dict=True) | |
| st.subheader("Solutions") | |
| st.write(sol) | |
| except Exception as e: | |
| st.error(f"System solve error: {e}") | |
| elif mode == "Derivative": | |
| # Expect 'd/dx: expr' or 'expr' with variable selection | |
| var = st.text_input("Variable for differentiation (single) — default 'x'", value='x') | |
| expr, err = parse_sympy(user_input) | |
| if err: | |
| st.error(err) | |
| else: | |
| try: | |
| dv = sp.diff(expr, sp.symbols(var)) | |
| st.subheader("Derivative") | |
| st.code(pretty(dv)) | |
| if show_steps: | |
| st.write("SymPy diff repr:") | |
| st.code(sp.srepr(dv)) | |
| except Exception as e: | |
| st.error(str(e)) | |
| elif mode == "Integral": | |
| var = st.text_input("Variable for integration (single) — default 'x'", value='x') | |
| definite = st.checkbox("Definite integral?", value=False) | |
| if definite: | |
| bounds = st.text_input("Bounds as two values separated by comma (a,b)") | |
| expr, err = parse_sympy(user_input) | |
| if err: | |
| st.error(err) | |
| else: | |
| try: | |
| a_str,b_str = bounds.split(',') | |
| a, _ = parse_sympy(a_str.strip()) | |
| b, _ = parse_sympy(b_str.strip()) | |
| res = sp.integrate(expr, (sp.symbols(var), a, b)) | |
| st.subheader("Integral result") | |
| st.code(pretty(res)) | |
| except Exception as e: | |
| st.error(f"Integration error: {e}") | |
| else: | |
| expr, err = parse_sympy(user_input) | |
| if err: | |
| st.error(err) | |
| else: | |
| try: | |
| res = sp.integrate(expr, sp.symbols(var)) | |
| st.subheader("Indefinite Integral") | |
| st.code(pretty(res)) | |
| except Exception as e: | |
| st.error(f"Integration error: {e}") | |
| elif mode == "Limit": | |
| # format: expression, point [, dir] | |
| text = user_input | |
| try: | |
| parts = [p.strip() for p in text.split(',')] | |
| expr, err = parse_sympy(parts[0]) | |
| if err: raise Exception(err) | |
| pt = parse_sympy(parts[1])[0] if len(parts) > 1 else sp.oo | |
| dir = parts[2] if len(parts) > 2 else None | |
| if dir: | |
| res = sp.limit(expr, sp.symbols('x'), pt, dir) | |
| else: | |
| res = sp.limit(expr, sp.symbols('x'), pt) | |
| st.subheader("Limit") | |
| st.code(pretty(res)) | |
| except Exception as e: | |
| st.error(f"Limit parse/eval error: {e}") | |
| elif mode == "Series Expansion": | |
| # format: expr, x, x0, n | |
| try: | |
| parts = [p.strip() for p in user_input.split(',')] | |
| expr, err = parse_sympy(parts[0]) | |
| if err: raise Exception(err) | |
| var = sp.symbols(parts[1]) if len(parts) > 1 else sp.symbols('x') | |
| x0 = parse_sympy(parts[2])[0] if len(parts) > 2 else 0 | |
| n = int(parts[3]) if len(parts) > 3 else 6 | |
| ser = sp.series(expr, var, x0, n).removeO() | |
| st.subheader("Series Expansion") | |
| st.code(pretty(ser)) | |
| except Exception as e: | |
| st.error(f"Series parse/eval error: {e}") | |
| elif mode == "Simplify / Factor / Expand": | |
| option = st.selectbox("Choose", ["simplify","factor","expand","trigsimp","ratsimp"], index=0) | |
| expr, err = parse_sympy(user_input) | |
| if err: | |
| st.error(err) | |
| else: | |
| try: | |
| if option == 'simplify': res = sp.simplify(expr) | |
| elif option == 'factor': res = sp.factor(expr) | |
| elif option == 'expand': res = sp.expand(expr) | |
| elif option == 'trigsimp': res = sp.trigsimp(expr) | |
| elif option == 'ratsimp': res = sp.ratsimp(expr) | |
| st.subheader(option.capitalize()) | |
| st.code(pretty(res)) | |
| except Exception as e: | |
| st.error(e) | |
| elif mode == "Matrix Operations": | |
| st.info("Matrix format: use Python-like lists, e.g. [[1,2],[3,4]]") | |
| try: | |
| # Use ast.literal_eval for safe parsing of Python literals | |
| parsed = ast.literal_eval(user_input) | |
| M = sp.Matrix(parsed) | |
| except Exception as e: | |
| st.error(f"Matrix parse error: {e}") | |
| M = None | |
| if M is not None: | |
| st.subheader("Matrix") | |
| st.write(M) | |
| op = st.selectbox("Matrix op", ['determinant','inv','eigen','rank','rref','transpose']) | |
| try: | |
| if op == 'determinant': st.write(M.det()) | |
| elif op == 'inv': st.write(M.inv()) | |
| elif op == 'eigen': st.write(M.eigenvects()) | |
| elif op == 'rank': st.write(M.rank()) | |
| elif op == 'rref': st.write(M.rref()) | |
| elif op == 'transpose': st.write(M.T) | |
| except Exception as e: | |
| st.error(f"Matrix operation error: {e}") | |
| elif mode == "Numerical Solve (nsolve)": | |
| # Expect equation and initial guess(s) | |
| try: | |
| # format: equation , guess | |
| eq_part, guess_part = [p.strip() for p in user_input.split(',')] | |
| if '=' in eq_part: | |
| L, R = eq_part.split('=',1) | |
| expr = parse_sympy(L)[0] - parse_sympy(R)[0] | |
| else: | |
| expr = parse_sympy(eq_part)[0] | |
| # guess can be single number or comma-separated for multivariate | |
| guesses = [float(g) for g in guess_part.replace(';',',').split(',') if g.strip()] | |
| if len(guesses) == 0: | |
| st.error('Please provide at least one initial guess after a comma') | |
| else: | |
| if len(guesses) == 1: | |
| sol = sp.nsolve(expr, guesses[0]) | |
| else: | |
| sol = sp.nsolve(expr, guesses) | |
| st.write(sol) | |
| except Exception as e: | |
| st.error(f"nsolve error: {e}") | |
| elif mode == "Plot Function": | |
| # allow plotting single-variable functions in variable x | |
| expr, err = parse_sympy(user_input) | |
| if err: | |
| st.error(err) | |
| else: | |
| try: | |
| var = sp.symbols('x') | |
| f = sp.lambdify(var, expr, modules=['numpy']) | |
| rng = st.text_input('Enter x-range as a,b', value='-10,10') | |
| a_str,b_str = [s.strip() for s in rng.split(',')] | |
| a = float(a_str); b = float(b_str) | |
| xs = np.linspace(a,b,1000) | |
| ys = f(xs) | |
| fig, ax = plt.subplots() | |
| ax.plot(xs, ys) | |
| ax.set_xlabel('x') | |
| ax.set_ylabel('f(x)') | |
| ax.grid(True) | |
| st.pyplot(fig) | |
| # allow download as png | |
| buf = BytesIO() | |
| fig.savefig(buf, format='png', bbox_inches='tight') | |
| buf.seek(0) | |
| st.download_button('Download plot PNG', data=buf, file_name='plot.png') | |
| except Exception as e: | |
| st.error(f"Plot error: {e}") | |
| # Footer / helpers | |
| st.sidebar.header('Examples') | |
| st.sidebar.markdown(""" | |
| **Evaluate:** `sin(pi/4) + log(10)` | |
| **Solve:** `x**2 - 5*x + 6 = 0` | |
| **System:** | |
| `x + y = 3` | |
| `x - y = 1` | |
| **Derivative:** `sin(x)*exp(x)` | |
| **Integral:** `x**2` (use definite checkbox + bounds `0,2`) | |
| **Series:** `exp(x), x, 0, 6` | |
| **Matrix:** `[[1,2],[3,4]]` | |
| **Plot:** `sin(x)/x` | |
| """) | |
| st.sidebar.markdown('---') | |
| st.sidebar.markdown('Built with SymPy + Streamlit. For deployment, add `app.py` and `requirements.txt` to a Hugging Face Space (Streamlit).') | |
| # End of app.py | |