Compound Exam¶
This example demonstrates pygacity’s primary strength: generating multiple,
individually unique exam versions from a single set of source files. Numerical
questions are parameterized — each serial number gets a different set of values
drawn from a defined pick-list — and question pools (short-answer, fill-in-the-blank,
true/false, and multiple-choice) are randomly sampled and shuffled per serial.
All files are in the examples/compound_exam/ directory.
compound_exam/
├── CompoundExam.yaml # pygacity configuration
├── compound_arithmetic.tex # Problem 1: parameterized numerics
├── simple_integration.tex # Problem 3: static with answer registration
├── derivatives_short_answer.yaml # Problem 2: short-answer pool
├── geography_fill_in_the_blank.yaml # Problem 4: fill-in-the-blank pool
├── true_false_questions.yaml # Problem 5: true/false pool
└── multiple_choice_questions.yaml # Problem 6: multiple-choice pool
The Configuration File¶
document:
preamble:
pagestyle: ~
font: |
\setmainfont{Lato}
commands:
Universityname: University of Nowhere
Departmentname: Mechanical Engineering
Instructorname: Eustace T. Smartypants
Instructoremail: ets@unow.edu
Termcode: 202525
Termname: Winter 2025-2026
Coursename: BIO 5678 - Applied Pyrohydrodynamics
structure:
- pythontex:
- setup
- text: |
\examheader{Exam I}{January 13, 2026}
\noindent\textbf{Instructions:} This exam is closed book.
- question_number: 1
source: compound_arithmetic.tex
points: 30
- question_number: 2
source: short.tex
points: 30
config: derivatives_short_answer.yaml
- question_number: 3
source: simple_integration.tex
points: 20
- question_number: 4
source: short.tex
points: 20
config: geography_fill_in_the_blank.yaml
- question_number: 5
source: short.tex
points: 10
config: true_false_questions.yaml
- question_number: 6
source: short.tex
points: 10
config: multiple_choice_questions.yaml
- pythontex:
- teardown
build:
seed: 12345
copies: 3
job-name: ExamI
paths:
build-dir: ./build
Several features here go beyond the Simple Assignment example.
build.seed and build.copies — instead of one document, pygacity generates
copies: 3 independently seeded versions. The seed value seeds the
random number generator used to derive each serial’s unique seed, making the
set of exams fully reproducible. Each version receives a unique serial number
embedded in the header and filename.
pythontex blocks — the first pythontex: [setup] block initializes
pygacity’s pythontex session: it creates the Pick, AnsSet, and rng
objects that parameterized source files use. The matching
pythontex: [teardown] block at the end persists the collected answers so
that the answer set document can be assembled afterwards.
question blocks — each block with a question_number key is a numbered
question rendered as an \item in the compiled document. The points
key sets the point value displayed next to each question. The optional
config key points to a YAML question-pool file used by short.tex.
short.tex reuse — the same packaged short.tex template appears four
times, each time with a different config: YAML file specifying the question
pool, question type, count, and instructions for that section.
Parameterized Problem: compound_arithmetic.tex¶
\begin{pycode}
part_a = Pick.pick_state({'A': {'default': 12, 'pick': {'pickfrom': [10, 12, 14]}},
'B': {'default': 3, 'pick': {'pickfrom': [2, 3, 4]}}})
part_a.answer = part_a.A * part_a.B
part_b = Pick.pick_state({'C': {'default': 45, 'pick': {'pickfrom': [40, 45, 50]}},
'D': 5})
part_b.answer = part_b.C // part_b.D
part_c = Pick.pick_state({'E': 7,
'F': {'pick': {'pickfrom': [8, 9, 10]}}})
part_c.answer = part_c.E + part_c.F
AnsSet.register(qno, label='a', value=part_a.answer)
AnsSet.register(qno, label='b', value=part_b.answer)
AnsSet.register(qno, label='c', value=part_c.answer)
\end{pycode}
\textbf{Simple Arithmetic.} Solve the following arithmetic problems:
\begin{enumerate}
\item[a.] What is \py{part_a.A} \(\times\) \py{part_a.B}?
\ifshowsolutions\ \ \ \textcolor{blue}{\py{part_a.answer}}
\else
\vspace{1cm}
\fi
\item[b.] What is \py{part_b.C} \(\div\) \py{part_b.D}?
\ifshowsolutions\ \ \ \textcolor{blue}{\py{part_b.answer}}
\else
\vspace{1cm}
\fi
\item[c.] What is \py{part_c.E} \(+\) \py{part_c.F}?
\ifshowsolutions\ \ \ \textcolor{blue}{\py{part_c.answer}}
\else
\vspace{1cm}
\fi
\end{enumerate}
This is the heart of pygacity’s compound-exam capability. Pick.pick_state
draws one value for each named parameter from its pickfrom list (or uses
the default if no pick list is given). The draws are seeded by the
serial number, so the same serial always produces the same values. Pick.pick_state returns a Namespace whose
members are accessed via dot notation.
AnsSet.register records each sub-part answer (labeled a, b, c)
together with the problem index, so the answer set document can tabulate all
answers across all serials automatically.
The \py{...} commands embed the drawn values directly in the typeset
question text, so no manual editing of question numbers is ever needed.
Static Problem with Answer Registration: simple_integration.tex¶
\textbf{Integration Problems.} Compute the following integrals:
\begin{enumerate}
\item[a.] What is the indefinite integral of \(f(x)=3x^2+2x+1\)?
\ifshowsolutions\ \ \ \textcolor{blue}{\(\int f(x)dx = x^3 + x^2 + x + C\)}
\else
\vspace{1.4cm}
\fi
\begin{pycode}
if 'AnsSet' in locals():
AnsSet.register(qno, label='a', value=r'\(x^3 + x^2 + x + C\)')
\end{pycode}
\item[b.] What is the definite integral of \(f(x)=\cos(x)\) from \(0\) to \(\pi/2\)?
\ifshowsolutions\ \ \ \textcolor{blue}{\(\int_0^{\pi/2} \cos(x) dx = 1\)}
\else
\vspace{1.4cm}
\fi
\begin{pycode}
if 'AnsSet' in locals():
AnsSet.register(qno, label='b', value=r'\(1\)')
\end{pycode}
\item[c.] What is the indefinite integral of \(f(x)=e^{3x}\)?
\ifshowsolutions\ \ \ \textcolor{blue}{\(\int f(x)dx = \frac{1}{3} e^{3x} + C\)}
\else
\vspace{1.4cm}
\fi
\begin{pycode}
if 'AnsSet' in locals():
AnsSet.register(qno, label='c', value=r'\(\frac{1}{3} e^{3x} + C\)')
\end{pycode}
\end{enumerate}
Static problems — whose content does not vary between serials — still need to
register their answers so they appear in the answer set. The AnsSet.register
calls are guarded by if 'AnsSet' in locals() so the file also works in
contexts where no answer set is being built (e.g. the Simple Assignment example).
Question Pool Files¶
Each short.tex section is driven by a YAML file that specifies the question
pool. The count and shuffle: true config keys mean each serial draws a
random subset in a random order from the pool.
derivatives_short_answer.yaml — 8 derivatives, 3 drawn per serial:
config:
count: 3
shuffle: true
instructions: |
\textbf{Derivatives.} Provide the correct derivative for each of the following functions.
type: short_answer
content:
- Q: \(f(x) = x^2\)
A: \(f^\prime(x) = 2x\)
text: The power rule states that the derivative of \(x^n\) is \(nx^{n-1}\).
- Q: \(f(x) = 2x^3 + 3x^2\)
A: \(f^\prime(x) = 6x^2 + 6x\)
text: The power rule states that the derivative of \(x^n\) is \(nx^{n-1}\), and the derivative of a sum is the sum of the derivatives.
- Q: \(f(x) = \sin(x)\)
A: \(f^\prime(x) = \cos(x)\)
text: |
The derivative of \(\sin(x)\) is \(\cos(x)\) according to standard differentiation rules. Or, you can consult the
series expansion of \(\sin(x)\) and differentiate term by term.
- Q: \(f(x) = \ln(x)\)
A: \(f^\prime(x) = \frac{1}{x}\)
text: The derivative of \(\ln(x)\) is \(\frac{1}{x}\) according to standard differentiation rules.
- Q: \(f(x) = e^{2x}\)
A: \(f^\prime(x) = 2e^{2x}\)
text: The derivative of \(e^{kx}\) is \(ke^{kx}\) according to standard differentiation rules.
- Q: \(f(x) = x^4 - 4x^3 + 6x^2 - 4x + 1\)
A: \(f^\prime(x) = 4x^3 - 12x^2 + 12x - 4\)
text: The power rule states that the derivative of \(x^n\) is \(nx^{n-1}\), and the derivative of a sum is the sum of the derivatives.
- Q: \(f(x) = \cos(x)\)
A: \(f^\prime(x) = -\sin(x)\)
text: The derivative of \(\cos(x)\) is \(-\sin(x)\) according to standard differentiation rules.
- Q: \(f(x) = \tan(x)\)
A: \(f^\prime(x) = \sec^2(x)\)
text: The derivative of \(\tan(x)\) is \(\sec^2(x)\) according to standard differentiation rules.
geography_fill_in_the_blank.yaml — 11 fill-in-the-blank geography questions, 4 drawn per serial:
config:
count: 4
shuffle: True
instructions: |
\textbf{State and World Capitals.} Write the correct US State, Canadian Province, or World Country or its capital in the blank provided.
type: fill_in_the_blank
content:
- Q: The capital of Texas is ___.
A: Austin
text: Austin became the capital of Texas in 1839.
- Q: The capital of Ontario is ___.
A: Toronto
text: Toronto is the capital city of the province of Ontario in Canada.
- Q: The capital of France is ___.
A: Paris
text: Paris has been the capital of France since the 10th century.
- Q: The capital of Japan is ___.
A: Tokyo
text: Tokyo has been the capital of Japan since 1868.
- Q: The capital of Germany is ___.
A: Berlin
text: Berlin has been the capital of Germany since 1990 following reunification.
- Q: The capital of ___ is Ottawa.
A: Canada
text: Ottawa is the capital city of Canada.
- Q: The capital of ___ is Canberra.
A: Australia
text: Canberra is the capital city of Australia.
- Q: The capital of ___ is Bras\'ilia.
A: Brazil
text: Bras\'ilia has been the capital of Brazil since 1960.
- Q: The capital of ___ is New Delhi.
A: India
text: New Delhi has been the capital of India since 1911.
- Q: The capital of ___ is Moscow.
A: Russia
text: Moscow has been the capital of Russia since 1918.
- Q: The capital of ___ is Cairo.
A: Egypt
text: Cairo has been the capital of Egypt since 972 AD.
true_false_questions.yaml — 8 true/false statements, 4 drawn per serial:
config:
count: 4
shuffle: true
instructions: |
\textbf{True/False Questions.} Indicate whether each of the following statements is true (``T'') or false (``F'').
type: true_false
content:
- Q: On a piano, the frequency of middle C is approximately 261.63 Hz.
A: T
text: The frequency of middle C (C4) is indeed approximately 261.63 Hz.
- Q: The chemical symbol for gold is Ag.
A: F
text: The chemical symbol for gold is Au; Ag is the symbol for silver.
- Q: The Great Wall of China is visible from space with the naked eye.
A: F
text: This is a common myth; the Great Wall is not easily visible from space without aid.
- Q: Water boils at 100 degrees Celsius at standard atmospheric pressure.
A: T
text: At standard atmospheric pressure (1 atm), water boils at 100 degrees Celsius.
- Q: The capital of Australia is Sydney.
A: F
text: The capital of Australia is Canberra, not Sydney.
- Q: Humans have four lungs.
A: F
text: Humans have two lungs, not four.
- Q: The speed of light in a vacuum is approximately \(3.00 \times 10^8\) meters per second.
A: T
text: The speed of light in a vacuum is approximately 299,792,458 meters per second, often rounded to \(3.00 \times 10^8\) m/s.
- Q: The largest planet in our solar system is Saturn.
A: F
text: The largest planet in our solar system is Jupiter, not Saturn.
multiple_choice_questions.yaml — 13 multiple-choice questions, 4 drawn per
serial with choices shuffled. Two questions illustrate per-question shuffle
control: one uses shuffle_choices: false because a choice cross-references
others by label; another uses fixed_last: true to pin a None of the
above option while freely shuffling the remaining choices:
config:
count: 4
shuffle: true
instructions: |
\textbf{Multiple choice questions.} Circle the letter of the correct answer from the choices provided.
type: multiple_choice
shuffle_choices: true
content:
- Q: What is the capital of Italy?
choices:
A: Madrid
B: Rome
C: Berlin
D: Lisbon
A: B
text: Rome is the capital city of Italy.
- Q: Which element has the chemical symbol 'O'?
choices:
a: Gold
b: Oxygen
c: Osmium
d: Silver
A: b
text: Oxygen has the chemical symbol 'O'. Note that the case of the choice keys are preserved in the final document.
- Q: Who wrote "Romeo and Juliet"?
choices:
A: Charles Dickens
B: Mark Twain
C: William Shakespeare
D: Jane Austen
A: C
text: William Shakespeare is the author of "Romeo and Juliet".
- Q: What is the largest mammal in the world?
choices:
A: African Elephant
B: Blue Whale
C: Giraffe
D: Great White Shark
A: B
text: The Blue Whale is the largest mammal in the world.
- Q: What is the boiling point of water at standard atmospheric pressure?
choices:
A: 90 degrees Celsius
B: 100 degrees Celsius
C: 110 degrees Celsius
D: 120 degrees Celsius
A: B
text: Water boils at 100 degrees Celsius at standard atmospheric pressure (1 atm).
- Q: Which planet is known as the Red Planet?
choices:
A: Venus
B: Mars
C: Jupiter
D: Saturn
A: B
text: Mars is known as the Red Planet due to its reddish appearance caused by iron oxide
- Q: Who painted the Mona Lisa?
choices:
A: Vincent van Gogh
B: Pablo Picasso
C: Leonardo da Vinci
D: Claude Monet
A: C
text: The Mona Lisa was painted by Leonardo da Vinci.
- Q: What is the smallest prime number?
choices:
A: 0
B: 1
C: 2
D: 3
A: C
text: The smallest prime number is 2, which is the only even prime number.
- Q: In which year did the Titanic sink?
choices:
A: 1910
B: 1912
C: 1914
D: 1916
A: B
text: The Titanic sank in the North Atlantic Ocean on April 15, 1912, after hitting an iceberg.
- Q: What is the chemical formula for table salt?
choices:
A: H2O
B: CO2
C: NaCl
D: C6H12O6
A: C
text: The chemical formula for table salt is NaCl, which stands for sodium chloride.
- Q: Who is known as the "Father of Modern Physics"?
choices:
A: Isaac Newton
B: Albert Einstein
C: Galileo Galilei
D: Niels Bohr
A: B
text: Albert Einstein is often referred to as the "Father of Modern Physics" for his contributions to the field, including the theory of relativity.
- Q: "Which of the following statements about Newton's laws of motion are correct?
(Select the best answer.)"
shuffle_choices: false
choices:
A: The first law states that an object at rest stays at rest unless acted on by a net force.
B: The second law states that F = ma.
C: Both A and B are correct.
D: Neither A nor B is correct.
A: C
text: Both statements are correct. Choice C cross-references A and B by label,
so shuffling choices would make the cross-references wrong. \texttt{shuffle\_choices} is
set to false for this question to override the global \texttt{shuffle\_choices} setting.
- Q: Which of the following is a fossil fuel?
fixed_last: true
choices:
A: Solar energy
B: Wind energy
C: Natural gas
D: None of the above.
A: C
text: Natural gas is a fossil fuel. The other options are renewable energy sources.
The last choice ``None of the above''' is pinned in place with \texttt{fixed\_last}, while
choices A-C are shuffled freely.
Building the Document¶
From inside the compound_exam/ directory, run:
pygacity build CompoundExam.yaml
Pygacity generates one student PDF and one solutions PDF per serial, then assembles a single answer set PDF covering all serials.
Output¶
The build/ directory contains:
ExamI-{serial}.pdf— student exam for each serialExamI_soln-{serial}.pdf— instructor copy with solutions for each serialanswerset.pdf— consolidated answer key for all serialsbuildfiles.zip/solnbuildfiles.zip— zipped LaTeX sourcestex_artifacts.zip— intermediate LaTeX artifacts
Two serials side by side — the arithmetic problem values differ because each
serial’s Pick.pick_state draws independently:
Solutions for serial 11364882 (page 1):
Answer set — one table per question, one row per serial: