2

I realize this is a fairly open-ended question, so it's okay to delete it if someone doesn't think it conforms to StackOverflow guidelines.

One of the most tedious and error-prone components to writing a math textbook is checking answers to exercises. I was wondering if anyone has come across anything that resembles a unit testing framework for latex to check answers (numerical for now) to exercise problems.

One thing I've thought of is having the exercises stored elsewhere where they are checked by other software, then when the latex document is compiled, the exercises and solutions get merged with the textbook. Does anyone know of such a thing?

rhody
  • 1,020
  • 1
    Are you using LuaLaTeX? If so, you can include full Lua programs inside your LaTeX files and have them output results directly inside your LaTeX code. That's what I'm using for numerical computations in my physics exercise collection. – Miyase Feb 27 '23 at 18:11
  • I hadn't thought of that. – rhody Feb 27 '23 at 18:20
  • 1
    even in classic tex you can do a lot of arithmetic in tex without lua, the latex format includes l3fp so you have powers, log, square roots etc. – David Carlisle Feb 27 '23 at 19:58

2 Answers2

1

Here's a skeleton example of what I'm using.

I keep a few generic functions in a separate file mylib.lua:

function math.round_int(x)
    return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
end

function printint(x) return tex.print(math.round_int(x)) end

function math.round(x, n) return math.round_int(x*10^n)/10^n end

function printrd(x, n) y = math.round(x, n) return tex.print(string.format("%." .. n .. "f", y)) end

function printe(s, x) return tex.print(string.format("%" .. s .. "e", x)) end

Keeping the functions in a separate file is cleaner, but it also avoids problems with characters like % which have different meaning in Lua and in LaTeX.

And here's a sample file that uses those functions:

\documentclass{article}
\usepackage{mathtools}
\usepackage{luacode}
\directlua{require("mylib.lua")}
\usepackage{siunitx}

\directlua{ eps0 = 8.85e-12 S = 1e-4 e = 0.012 }

\begin{document} Compute the capacitance of a capacitor with surface $S=\qty{\directlua{printint(S1e4)}}{mm^2}$ and thickness $e=\qty{\directlua{printrd(e, 3)}}{m}$. \begin{align} C &=\frac{\varepsilon_0S}{e}\ &=\qty{\directlua{ C = eps0S/e printe("1.1e", C) }}{F} \end{align}

\end{document}

result

Miyase
  • 2,544
  • 2
  • 12
  • 26
1

You just described the sagetex package, documentation here, which gives you access to the Python programming along with a free open source Computer Algebra System (CAS) called Sage, documentation here. Sage can handle algebra, calculus, matrices, vectors, polynomials, graph theory, and a lot more.

Here's a sample test I designed years ago in trying to learn sagetex. The problems are created randomly, according to my specifications, along with an answer key.

\documentclass[a4paper,addpoints,12point]{exam}%
% -----------------------------The preamble ------------------------------------
\usepackage{xcolor}
\usepackage{fix-cm}% allow for a bigger font
%  mathematical typesetting, fonts, and symbols
\usepackage{amsmath, amsfonts, amssymb}
\usepackage[T1]{fontenc}% change the fonts
\usepackage[margin=.5in]{geometry}%  sets the margins
\usepackage{sagetex}%  use Sage for it's math ability
\usepackage{graphicx}%  for including pictures
\usepackage{tikz} %tikz pictures, graph paper.
\pagestyle{empty} % remove the page numbers
\pagestyle{head}
\printanswers % If you want to print answers
%\noprintanswers % If you don't want to print answers
\addpoints % if you want to count the points
% \noaddpoints % if you don't want to count the points
% Specifies the way question are displayed:
%\qformat{\textbf{\thequestion.}   \quad(\thepoints)\hfill}
\shadedsolutions % defines the style of the solution environment
% \framedsolutions % defines the style of the solution environment
\definecolor{SolutionColor}{rgb}{0.8,0.9,1}    % light blue
% Defines the title of the solution environment:
\renewcommand{\solutiontitle}{\noindent\textbf{Solution:}\par\noindent}
% **************** BEGIN INPUT ASSIGNMENT INFO *******************
\newcommand{\course}{My Math Course}
\newcommand{\assignment}{My Test}
\newcommand{\teacher}{Mr. Ima Putz}
% **************** END INPUT ASSIGNMENT INFO *******************
\runningheadrule
\runningheader{\course}{\assignment}{\teacher}
\setlength{\parskip}{1.2ex} %space between paragraphs
\setlength{\parindent}{1em} %Paragraph indentation
\linespread{1.2} %spacing between lines
\widowpenalty = 10000   %penalty for a broken page
\newcommand\T{\rule{0pt}{2ex}} % \T will create extra space above (used to fix tables)
\newcommand\B{\rule[-1.5ex]{0pt}{0pt}}% \B will create extra space below (used to fix tables)
 % -----------------------------End of the preamble ------------------------------
 \begin{document}
 \fontsize{17pt}{20pt}\selectfont %sets font size and line spacing
 \noindent
 \begin{tabular}{@{}l p{1.6in}r @{}}
Name: \vrule height 0 pt depth 0.4 pt width 3.0 in & & Period: \vrule height 0 pt depth 0.4 pt width .75 in \\\\
 \teacher &  &Date: \hrulefill \\
 \end{tabular}
 \begin{center}
 The point value is given for each problem.\\ \vspace{.1in}
 {\bf \assignment}
 \end{center}
 \noindent Show the solution to each  problem. Put your answer in the space provided. If there isn't enough space, circle your answer.
 % *********** BEGIN DEFINE VARIABLES ****************
 \begin{sagesilent}
 a = Integer(randint(6,9))
 b = Integer(randint(7,10))
 c = Integer(randint(2,5))
 d = Integer(randint(2,5))
 e = Integer(randint(1,4))
 f = Integer(randint(1,4))
 q1 = expand((x-a)*(x-c))
 q2 = expand((x-b)*(x-d))
 \end{sagesilent}
 % *********** END DEFINE VARIABLES ****************
 \begin{questions}
 \bracketedpoints
 \question[4]
 Factor $\sage{q1}$.
 \answerline
 \vspace{0.85in}
 \question[4]
 Determine  $\int_{0}^{\sage{e}}\sage{q1}\,dx$.
 \answerline
 \vspace{0.85in}
 \question[5]
 Factor $\sage{q2}$.
 \answerline
 \vspace{0.85in}
 \question[6]
 Find $\frac{d}{dx}\left(\sage{q2}\right)$.
 \answerline
 \newpage
 \question[5]
 Factor $\sage{a*b*c}$.
 \answerline
 \vspace{0.85in}
 \question[4]
 The table shows the number of voters in  Mickey Mouse U. who were polled on their vote  for Alex and Bob in the school election. Find  the probability that a randomly selected person  voted for Alex.
 \\\\
 \begin{sagesilent}
 g=Integer(randint(23,37))
 h=Integer(randint(31,42))
 i=Integer(randint(19,45))
 j=Integer(randint(19,40))
 k=Integer(randint(15,36))
 l=Integer(randint(24,34))
 \end{sagesilent}

\begin{tabular}{c|c|c|c} & Sophomores & Juniors & Seniors\ \hline Alex & $\sage{g}$ & $\sage{h}$ & $\sage{i}$\ \hline Bob & $\sage{j}$ & $\sage{k}$ & $\sage{l}$\ \hline \end{tabular} \answerline \vspace{0.85in} \question[6] Find the distance between the points $(\sage{a},\sage{b})$ and $(\sage{c},\sage{e})$. \newpage \begin{center} {\large Answers} \end{center} \begin{solution} $x=\sage{a}$, and $x=\sage{c}$ \end{solution} \begin{solution} $\sage{integrate(q1,(x,0,e))}$ \end{solution} \begin{solution} $x=\sage{b}$, and $x=\sage{d}$ \end{solution} \begin{solution} $\sage{diff(q2)}$ \end{solution} \begin{solution} $\sage{factor(abc)}$ \end{solution} \begin{solution} $\sage{(g+h+i)/(g+h+i+j+k+l)}$ \end{solution} \begin{solution} $\sage{sqrt((a-c)^2+(b-e)^2)}$ \end{solution} \end{questions} \end{document}

The result running in Cocalc is:

enter image description here

The answer key on the third page is:

enter image description here

Note the answer to the integration problem: $\sage{integrate(q1,(x,0,e))}$. Sage figures it out thereby preventing mistakes. It also gave the answer as a fraction, instead of a decimal.

Sage is not part of your LaTeX distribution. There are 2 main ways to access it. One is to download it to your computer and get it to work with LaTeX. The second way is the easiest: Get a free Cocalc account.

Search this site for sagetex examples. My answer to getting the matrix transpose here shows how easily you can get the correct answer and calculate with it. Sage can plot graphs and it's possible to force the output through tikz to make it look better: see my answer for the Riemann Zeta plot here.

DJP
  • 12,451