Skip to content

Commit

Permalink
First draft materials for debugging workshop
Browse files Browse the repository at this point in the history
These materials are being prepared for an "Introduction to Debugging"
workshop at the Spectrum Annual Meeting 2024 (23-25 September), as
proposed in our August 2024 meeting.
  • Loading branch information
robmoss committed Aug 22, 2024
1 parent 4f456eb commit 7c218be
Show file tree
Hide file tree
Showing 30 changed files with 1,388 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
site/
venv/
*.pyc
5 changes: 5 additions & 0 deletions docs/community/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ The [three characteristics](https://www.communityofpractice.ca/background/what-i
3. Domain: A shared interest, problem, or concern.

We regularly meet as a community, report [meeting summaries](meetings/README.md), and collect [case studies](case-studies/README.md) that showcase good practices.

## Training events

To support skill development, we have the capacity to prepare and deliver bespoke training events as standalone session and as part of larger meetings and conferences.
See our [Training events](training/README.md) page for further details.
3 changes: 3 additions & 0 deletions docs/community/training/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Training events

We will be running an [Introduction to Debugging](debugging/README.md) workshop at the [SPECTRUM](https://spectrum.edu.au/) Annual Meeting 2024 (23-25 September).
11 changes: 11 additions & 0 deletions docs/community/training/debugging/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Introduction to Debugging

This workshop was prepared for the [SPECTRUM](https://spectrum.edu.au/) Annual Meeting 2024 (23-25 September).

!!! tip

**We all make mistakes** when writing code and introduce errors.

Having good debugging skills means that you can spend **less time fixing your code**.

See the discussion in our [August 2024 meeting](../../meetings/2024-08-08.md#debugging) for further background.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions docs/community/training/debugging/example-perfect-numbers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Perfect numbers

=== "Overview"

[Perfect numbers](https://en.wikipedia.org/wiki/Perfect_number) are positive integers that are equal to the sum of their divisors.
Here we have provided example Python and R scripts that should print all of the perfect numbers up to 1,000.

You can download each script to debug on your own computer:

- [perfect_numbers.py](perfect_numbers.py)
- [perfect_numbers.R](perfect_numbers.R)

=== "Python"

```py title="perfect_numbers.py" linenums="1"
--8<-- "perfect_numbers.py"
```

=== "R"

```R title="perfect_numbers.R" linenums="1"
--8<-- "perfect_numbers.R"
```

??? bug "But there's a problem ..."

If we run these scripts, we see that **they don't print anything**:

<div id="demo" data-cast-file="../perfect-numbers-first-run.cast"></div>

How should we begin investigating?

??? note "Some initial thoughts ..."

- Are we actually running the `main()` function at all?

- The `main()` function is almost certainly not the cause of this error.

- The `is_perfect()` function is very simple, so it's unlikely to be the cause of this error.

- The `divisors_of()` function doesn't look obviously wrong.

- But there must be a mistake **somewhere**!

- Let's **use a debugger** to investigate.
112 changes: 112 additions & 0 deletions docs/community/training/debugging/example-python-vs-r.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Example: Python vs R

=== "Overview"

Here we have provided SIR ODE model implementations in Python and in R.
Each script runs several scenarios and produces a plot of infection prevalence for each scenario.

You can download each script to debug on your computer:

- [sir_ode.py](sir_ode.py)
- [sir_ode.R](sir_ode.R)

=== "Python"

```py title="sir_ode.py" linenums="1"
--8<-- "sir_ode.py"
```

=== "R"

```R title="sir_ode.R" linenums="1"
--8<-- "sir_ode.R"
```

??? bug "The model outputs differ!"

Here are prevalence time-series plots produced by each script:

=== "Python plot"

<figure markdown="span">
![Python outputs](sir_ode_python.png)
<figcaption>Model outputs for the Python script.</figcaption>
</figure>

=== "R plot"

<figure markdown="span">
![R outputs](sir_ode_r.png)
<figcaption>Model outputs for the R script.</figcaption>
</figure>

??? note "Some initial thoughts ..."

- Is it obvious whether one of the figures is correct and the other is wrong?

- The `sir_rhs()` functions in the two scripts appear to be equivalent — but are they?

- The `default_settings()` functions appear to be equivalent — but are they?

- The `run_model_scaled_beta()` and `run_model_scaled_gamma()` functions also appear to be equivalent.

- Where might you begin looking?

??? info "Commentary"

> I've heard that dicts (and other mutable data types) are passed by reference, as such a function could change the “local” value of a variable, which causes an error when using the “global” variable in another function.
>
> This would also be good for showing that your test cases aren’t perfect.
>
> You can run function 1 tests and get good answers, and then run function 2 test and get good answers, but this is because they are not sharing the state of the mutable variable.
> Sounds like a great inclusion as a mini chapter.

These are great ideas! The one drawback of the mutable data example is that it won't work in R, because it's copy-on-write. But that's no big deal. In fact, now that I think about, this difference us surely worth highlighting! People who are familiar with only one of these two languages need to be aware of this important difference if they begin using the other language.

I've come up with an example of shared mutable state resulting in incorrect model outputs.

!!! quote "The R Language Definition"

The semantics of invoking a function in R argument are **call-by-value**.
In general, supplied arguments behave as if they are **local variables** initialized with the value supplied and the name of the corresponding formal argument.
Changing the value of a supplied argument within a function **will not affect** the value of the variable in the calling frame.

— [Argument Evaluation](https://cran.r-project.org/doc/manuals/r-patched/R-lang.html#Argument-evaluation)

!!! quote "Python Programming FAQ"

Remember that arguments are **passed by assignment** in Python.
Since assignment just **creates references** to objects, there's no alias between an argument name in the caller and callee, and so no call-by-reference per se.

— [How do I write a function with output parameters (call by reference)?](https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference)

??? info "Output messages"

Here are the output messages printed by each script:

=== "Python"

```text
beta = 1.0 gamma = 0.5
beta = 1.5 gamma = 0.5
beta = 1.5 gamma = 0.35
```

=== "R"

```text
beta = 1.0 gamma = 0.5
beta = 1.5 gamma = 0.5
beta = 1.0 gamma = 0.35
```

??? bug "Parameters differ!"

There's a difference in the parameter values for the third scenario:

```diff
beta = 1.0 gamma = 0.5
beta = 1.5 gamma = 0.5
- beta = 1.5 gamma = 0.35
+ beta = 1.0 gamma = 0.35
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions docs/community/training/debugging/learning-objectives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Learning objectives

In this workshop, we will introduce the concept of "debugging", and demonstrate techniques and tools that can help us efficiently identify and remove errors from our code.

After completing this workshop, participants will:

+ Understand that debugging can be divided into a sequence of actions;

+ Understand the purpose of each of these actions;

+ Be familiar with techniques and tools that can help perform these actions;

+ Be able to apply these techniques and tools to their own code.

!!! info

By achieving these learning objectives, participants should be able to find and correct errors in their code more quickly and with greater confidence.
12 changes: 12 additions & 0 deletions docs/community/training/debugging/manifesto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Debugging manifesto

<figure markdown="span">
![A debugging manifesto poster.](debugging-manifesto-poster.jpg){ align=left, width="50%" }
<figcaption markdown="span">
[Julia Evans](https://jvns.ca/) and [Tanya Brassie](https://tanyabrassie.com/): [Debugging Manifesto Poster](https://store.wizardzines.com/products/poster-debugging-manifesto), 2024.
</figcaption>
</figure>

!!! info

See the [Resources](resourced.md) page for links to more of Julia Evans' articles, stories, and zines about debugging.
48 changes: 48 additions & 0 deletions docs/community/training/debugging/perfect-numbers-first-run.cast
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{"version": 2, "width": 80, "height": 4, "timestamp": 1723508323, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[0.106254, "o", "\u001b[?2004h\u001b]2;(x1::debugging)\u0007\u001b[32;1m(x1::debugging)\u001b[0m "]
[1.439464, "o", "."]
[1.511348, "o", "/"]
[1.735208, "o", "p"]
[1.815337, "o", "e"]
[1.879152, "o", "r"]
[2.063205, "o", "f"]
[2.151301, "o", "e"]
[2.271034, "o", "c"]
[2.46335, "o", "t"]
[2.663166, "o", "_"]
[2.823218, "o", "n"]
[3.007567, "o", "u"]
[3.199264, "o", "m"]
[3.271363, "o", "b"]
[3.439513, "o", "e"]
[3.487438, "o", "r"]
[3.607177, "o", "s"]
[3.687383, "o", "."]
[3.847072, "o", "p"]
[3.991135, "o", "y"]
[4.967213, "o", "\r\n"]
[4.967292, "o", "\u001b[?2004l\r"]
[5.065072, "o", "\u001b[?2004h\u001b]2;(x1::debugging)\u0007\u001b[32;1m(x1::debugging)\u001b[0m "]
[6.879247, "o", "."]
[7.079536, "o", "/"]
[7.319153, "o", "p"]
[7.415356, "o", "e"]
[7.479395, "o", "r"]
[7.639271, "o", "f"]
[7.727076, "o", "e"]
[7.823333, "o", "c"]
[8.00743, "o", "t"]
[8.151241, "o", "_"]
[8.407055, "o", "n"]
[8.527353, "o", "u"]
[8.71911, "o", "m"]
[8.783422, "o", "b"]
[8.967451, "o", "e"]
[9.015325, "o", "r"]
[9.151235, "o", "s"]
[9.247291, "o", "."]
[9.575237, "o", "R"]
[10.167232, "o", "\r\n"]
[10.167313, "o", "\u001b[?2004l\r"]
[10.450741, "o", "\u001b[?2004h\u001b]2;(x1::debugging)\u0007\u001b[32;1m(x1::debugging)\u001b[0m "]
[12.800104, "o", "\u001b[?2004l"]
40 changes: 40 additions & 0 deletions docs/community/training/debugging/perfect_numbers.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env -S Rscript --vanilla
#
# This script prints perfect numbers.
#
# Perfect numbers are positive integers that are equal to the sum of their
# divisors.
#


main <- function() {
start <- 2
end <- 1000
for (value in seq.int(start, end)) {
if (is_perfect(value)) {
print(value)
}
}
}


is_perfect <- function(value) {
divisors <- divisors_of(value)
sum(divisors) == value
}


divisors_of <- function(value) {
divisors <- c()
candidate <- 2
while (candidate < value) {
if (value %% candidate == 0) {
divisors <- c(divisors, candidate)
}
candidate <- candidate + 1
}
divisors
}


main()
34 changes: 34 additions & 0 deletions docs/community/training/debugging/perfect_numbers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env python3
"""
This script prints perfect numbers.
Perfect numbers are positive integers that are equal to the sum of their
divisors.
"""


def main():
start = 2
end = 1_000
for value in range(start, end + 1):
if is_perfect(value):
print(value)


def is_perfect(value):
divisors = divisors_of(value)
return sum(divisors) == value


def divisors_of(value):
divisors = []
candidate = 2
while candidate < value:
if value % candidate == 0:
divisors.append(candidate)
candidate += 1
return divisors


if __name__ == '__main__':
main()
10 changes: 10 additions & 0 deletions docs/community/training/debugging/resources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Resources

- Julie Evans:
- [Debugging articles](https://jvns.ca/#debugging)
- [Debugging stories](https://jvns.ca/#debugging-stories)
- [The Pocket Guide to Debugging](https://jvns.ca/blog/2022/12/21/new-zine--the-pocket-guide-to-debugging/) (see the [table of contents](https://jvns.ca/images/debugging-guide-toc.png))
- [Notes on building debugging puzzles](https://jvns.ca/blog/2021/04/16/notes-on-debugging-puzzles/)
- [Learn Debugging](https://www.cse.unsw.edu.au/~learn/debugging/)
- [Debug with pdb and breakpoint](https://hamatti.org/posts/debug-with-pdb-and-breakpoint/)
- [Advanced R: Debugging](https://adv-r.hadley.nz/debugging.html)
Loading

0 comments on commit 7c218be

Please sign in to comment.