[Tex/LaTex] Autocalculate result in math-mode

calculationsmath-mode

Are there any available programs/software/etc. that allow you to autocalculate and then autocomplete TeX equations? It would be great if for example, I were doing really long messy equations like

120 000 \, inches \times \frac{1 \, mile}{63360 \, inches} =

and the program could just fill in the blank with

1.89 \, miles

Best Answer

Here's an approach based on my pythontex package, which provides access to SymPy. I've written some code that takes an expression written with siunitx, converts it to SymPy format (if it's not too complex and doesn't break the regular expressions), and then calculates the result. The SymPy code is accessed with a macro I've called \autocalc, which takes two arguments: an expression written with siunitx, and the desired units for the answer (also written with siunitx). This won't handle everything without some additional regex work, but it should handle most one-line unit conversions.

enter image description here

\documentclass{article}

\usepackage{pythontex}
\usepackage[per-mode=fraction]{siunitx}
% Define some English units
\DeclareSIUnit\inch{in}
\DeclareSIUnit\inches{in}
\DeclareSIUnit\ft{ft}
\DeclareSIUnit\foot{ft}
\DeclareSIUnit\feet{ft}
\DeclareSIUnit\mi{mi}
\DeclareSIUnit\mile{mi}
\DeclareSIUnit\miles{mi}

\begin{sympycode}
from sympy.physics.units import *
import re

def process_units(units):
    ''' Take units in siunitx form, and convert to SymPy format '''
    # Take care of powers
    units = units.replace(r'\squared', '**2')
    units = re.sub(r'\\square(\\[a-z]+)', r'\1**2', units)
    units = units.replace(r'\cubed', '**3')
    units = re.sub(r'\\cubic(\\[a-z]+)', r'\1**3', units)
    # Take care of \per
    units = re.sub(r'\\per(\\[a-z]+\**\d*)', r'/(\1)', units)
    # Add in multiplication as needed
    units = re.sub(r'(\(*\\[a-z]+(?:\*\*\d+)*\)*)(\\[a-z]+)', r'\1*\2', units)
    units = re.sub(r'(\(*\\[a-z]+(?:\*\*\d+)*\)*)(\\[a-z]+)', r'\1*\2', units)
    # Finally, remove backslashes
    units = units.replace('\\', '')
    return units

def process_SI(matchobj):
    ''' Take an \SI macro, and convert it to SymPy form '''
    num, units = matchobj.groups()
    units = process_units(units)
    result = num + '*' + units
    return result

def calc_SI_expression(expr, final_units):
    ''' Turn a series of siunitx expressions into SymPy form, perform
    the calculation, and return the answer in the desired units '''
    # Get rid of spaces in code from TeX
    expr = expr.replace(' ', '')
    final_units = final_units.replace(' ', '')
    # Start assembling the final result
    result = expr + ' = '
    # Take care of multiplication and \left and \right
    expr = expr.replace('^', '**')
    expr = expr.replace(r'\left', '').replace(r'\right', '')
    expr = expr.replace('\\times', '*')
    # Convert all \SI macros to SymPy form
    expr = re.sub(r'\\SI\{(.+?)\}\{(.+?)\}', process_SI, expr)
    expr = re.sub(r'\\frac\{(.+?)\}\{(.+?)\}', r'(\1)/(\2)', expr)
    # Evaluate the string that is now in SymPy form
    expr = eval(expr + '/(' + process_units(final_units) + ')')
    # Round the result to a desired number of places
    expr = round(float(expr), 6)
    # Return original equation plus calculated result
    return result + '\SI{{{0}}}{{{1}}}'.format(expr, final_units)
\end{sympycode}

% Define a shortcut for accessing the Python function
\newcommand{\autocalc}[2]{\sympy{calc_SI_expression(r"#1", r"#2")}}

\begin{document}

\[
\autocalc{
  \SI{120000}{\inches}
  \times
  \frac{\SI{1}{\mile}}{\SI{63360}{\inches}}
}{\miles}
\]

\[
\autocalc{
  \SI{2}{\square\feet\per\second}
  \times
  \left(
  \frac{\SI{12}{\inches}}{\SI{1}{\foot}}
  \right)^2
  \times
  \frac{\SI{3600}{\s}}{\SI{1}{\hour}}
}{\square\inches\per\hour}
\]

\end{document}
Related Question