Code#
Our convention is to follow PEP8 unless there is a good reason to do otherwise.
One good reason is to get closer to mathematical notation in a given lecture.
Hence it’s fine to use capitals for matrices, etc.
Operators are typically surrounded by spaces, as in a * b
and a +
b, but we write a**b
for \(a^b\).
Variable Naming Conventions#
Unicode Variable Names#
Prefer Unicode symbols for Greek letters commonly used in economics:
Use
α
instead ofalpha
Use
β
instead ofbeta
Use
γ
instead ofgamma
Use
δ
instead ofdelta
Use
ε
instead ofepsilon
Use
σ
instead ofsigma
Use
θ
instead oftheta
Use
ρ
instead ofrho
This makes the code more readable and closer to mathematical notation used in economic literature.
Example:
# ✅ Preferred: Unicode variables
def utility_function(c, α=0.5, β=0.95):
"""CRRA utility function with discount factor."""
return (c**(1-α) - 1) / (1-α) * β
# ❌ Avoid: Spelled-out Greek letters
def utility_function(c, alpha=0.5, beta=0.95):
"""CRRA utility function with discount factor."""
return (c**(1-alpha) - 1) / (1-alpha) * beta
Guiding principle
QuantEcon lecture’s should run in a base installation of Anaconda python.
Any packages (that are not included in anaconda) need to be installed at the top of the lecture.
An example:
In addition to what’s in Anaconda, this lecture will need the following libraries:
In the example above we install the quantecon
and yfinance
packages.
We use tags: [hide-output]
as the output is not central to the lecture.
There are a couple of exceptions to this guideline.
when the software involves specific configuration for the hardware (i.e.
gpu
computing), orif additional software needs to be installed on your system via
apt
or some other binary source.
JAX#
When using jax
you should not install jax
at the top of your lecture.
This may install jax[cpu]
which will run but is not the optimal configuration for executing the lecture.
The following admonition can be used.
```{admonition} GPU
:class: warning
This lecture is accelerated via [hardware](status:machine-details) that has access to a GPU and JAX for GPU programming.
Free GPUs are available on Google Colab. To use this option, please click on the play icon top right, select Colab, and set the runtime environment to include a GPU.
Alternatively, if you have your own GPU, you can follow the [instructions](https://github.com/google/jax) for installing JAX with GPU support. If you would like to install JAX running on the `cpu` only you can use `pip install jax[cpu]`
```
which will render as
GPU
This lecture is accelerated via hardware that has access to a GPU and JAX for GPU programming.
Free GPUs are available on Google Colab. To use this option, please click on the play icon top right, select Colab, and set the runtime environment to include a GPU.
Alternatively, if you have your own GPU, you can follow the instructions for installing JAX with GPU support. If you would like to install jax running on the cpu
only you can use pip install jax[cpu]
The jax[gpu]
package needs to be properly installed via Docker
or GitHub Actions
.
See also
Make sure the repo is compliant with the following configuration requirements such as Support files
Please consult with Matt McKay should you need to update these settings.
Binary packages with Python frontends#
The graphviz package is a python interface
to a local installation of graphviz and is useful
for rendering DOT
source code.
If you need to use graphviz
you should:
Install
pip install graphviz
at the top of your lectureCheck if
graphviz
is getting installed in.github/workflows/ci.yml
for preview buildsAdd the below
note
admonition to your lecture.
which will render as
Performance Timing Patterns#
Timer Context Manager#
Use the modern qe.Timer()
context manager instead of manual timing patterns.
The QuantEcon library provides a Timer
context manager that replaces older timing approaches like tic
/tac
/toc
functions with cleaner, more reliable syntax.
❌ Avoid: Manual timing patterns
import time
# Old pattern - verbose and error-prone
start_time = time.time()
result = expensive_computation()
end_time = time.time()
print(f"Elapsed time: {(end_time - start_time) * 1000:.6f} ms")
✅ Preferred: Timer context manager
import quantecon as qe
# Modern pattern - clean and reliable
with qe.Timer():
result = expensive_computation()
# Output: 0.05 seconds elapsed
Timer Features and Usage Patterns#
The Timer
context manager supports various usage patterns:
Basic Timing#
import quantecon as qe
with qe.Timer():
result = expensive_computation()
# Output: 0.05 seconds elapsed
Custom Messages and Units#
# Custom message with milliseconds
with qe.Timer("Computing eigenvalues", unit="milliseconds"):
eigenvals = compute_eigenvalues(matrix)
# Output: Computing eigenvalues: 50.25 ms elapsed
# Microseconds for very fast operations
with qe.Timer("Quick calculation", unit="microseconds"):
result = simple_operation()
# Output: Quick calculation: 125.4 μs elapsed
Silent Mode for Method Comparison#
# Store timing without printing for performance comparisons
timer = qe.Timer(silent=True)
with timer:
result = expensive_computation()
elapsed_time = timer.elapsed # Access stored time
# Compare multiple methods
methods = [method_a, method_b, method_c]
timers = []
for method in methods:
timer = qe.Timer(f"{method.__name__}", silent=True)
with timer:
method(data)
timers.append((method.__name__, timer.elapsed))
# Find fastest method
fastest = min(timers, key=lambda x: x[1])
print(f"Fastest method: {fastest[0]} ({fastest[1]:.6f}s)")
Precision Control#
# Control decimal places in output
with qe.Timer("High precision timing", precision=8):
result = computation()
# Output: High precision timing: 0.12345678 seconds elapsed
Migration from Legacy Patterns#
Replace tic
/tac
/toc
patterns:
# Old approach
from quantecon.util.timing import tic, tac, toc
tic()
result = computation()
toc()
# New approach
with qe.Timer():
result = computation()
Note
The tic
/tac
/toc
functions remain available for backward compatibility, but new code should use the Timer
context manager for better readability and reliability.
Benchmarking with timeit
#
Use qe.timeit()
for statistical performance analysis across multiple runs.
The QuantEcon library provides a timeit
function that performs multiple runs of code and computes statistical measures, making it ideal for rigorous performance benchmarking.
Key Features#
Multiple runs: Automatically executes code multiple times to reduce noise
Statistical analysis: Computes mean, standard deviation, min, and max execution times
Flexible input: Accepts functions, callables, or lambda expressions
Customizable: Control number of runs and other parameters
Basic Usage#
import quantecon as qe
import numpy as np
# Define a function to benchmark
def matrix_multiplication():
A = np.random.rand(100, 100)
B = np.random.rand(100, 100)
return A @ B
# Benchmark with multiple runs
result = qe.timeit(matrix_multiplication, number=100)
print(result)
# Output: Statistical summary with mean, std, min, max times
Using Lambda Functions#
Tip
Use lambda:
to pass functions with arguments or create inline benchmarks:
# Benchmark function with arguments
data = np.random.rand(1000, 1000)
result = qe.timeit(lambda: np.linalg.eig(data), number=50)
# Benchmark inline code
result = qe.timeit(lambda: sorted([random.random() for _ in range(1000)]), number=100)
This pattern is particularly useful when you need to pass specific arguments or test variations of the same algorithm.
### Comparison with Jupyter `%timeit`
While Jupyter's `%timeit` magic command is convenient for interactive exploration, `qe.timeit()` offers several advantages for programmatic benchmarking:
| Feature | `%timeit` | `qe.timeit()` |
|---------|-----------|---------------|
| **Environment** | Jupyter only | Any Python environment |
| **Return value** | Prints only | Returns statistical object |
| **Integration** | Interactive | Programmatic workflows |
| **Automation** | Manual | Scriptable comparisons |
| **Statistics** | Basic timing | Comprehensive statistics |