Skip to content

Commit 04ec3f7

Browse files
authored
Standardise docstrings + make a note in contributing.md (#203)
* Establish docstring convention * do file * algorithms module * causal_problem class * graph.node docstrings * graph module * MLP submodule docstrings * Quadrature submodule * solvers submodule * Combine tests with contributing docs * Fix redirect link * I can't write Python * Move loose test into subfolder * Remove reference to ricardo in tests, use CTS treatment again
1 parent 4aba6d0 commit 04ec3f7

20 files changed

Lines changed: 280 additions & 163 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ pytest tests
126126

127127
again from the root of the repository.
128128

129-
For more information about the testing suite, please see [the documentation page](./docs/developers/tests.md).
129+
For more information about the testing suite, please see [the contributing page](./docs/developers/contributing.md#testing-suite).
130130

131131
### Building documentation
132132

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,62 @@
1-
# Testing Suite
1+
# Contributing and Style Guide
2+
3+
## Docstring Style
4+
5+
### Functions and Methods
6+
7+
`causalprog` uses [Google-style docstrings](https://mkdocstrings.github.io/python/usage/docstrings/google/), which should be formatted as
8+
9+
```python
10+
def my_function(arg1, arg2):
11+
"""
12+
Summary line.
13+
14+
Further information in prose / paragraph format, mathematical notation is also supported here.
15+
If some of the function arguments require detailed explanation, this explanation should be placed here.
16+
17+
Args:
18+
arg1: Description of the first argument
19+
arg2: Description of the second argument.
20+
21+
Returns:
22+
Description of the object(s) that are returned by the method.
23+
24+
Raises:
25+
ExceptionType: Conditions under which this is raised.
26+
ExceptionType: Conditions under which this is raised.
27+
28+
"""
29+
```
30+
31+
`mkdocs` also supports the `Tip:` and `Note:` syntax within docstrings too, which should appear within the further information section of the docstring.
32+
33+
If a function's purpose, return type, and inputs are clear from it's definition and name, then the docstring may consist of a single summary line instead:
34+
35+
```python
36+
def sum_items(item1, item2):
37+
"""Return the sum of two items."""
38+
return item1 + item2
39+
```
40+
41+
### Classes and Modules
42+
43+
Classes and modules should also obey Google-style docstring conventions where possible, but there is no need to provide an explicit listing of the methods (and / or attributes) that such objects provide in the docstrings themselves.
44+
However, docstrings for classes and modules should still provide an adequate level of detail about what the module does / class represents, and the components that a user will typically be interacting with.
45+
46+
### Docstrings in the Tests and Examples
47+
48+
Outside the package source code, the docstring format is much more loose, though developers should try to stick to the Google-style when possible.
49+
50+
In the test suite; docstrings are typically used to describe the steps in longer, more involved tests, as well as the actual comparisons or `assert`ions that are made to ensure object being tested is functioning correctly.
51+
52+
In the examples; docstrings are typically provided in the summary format, relying on the surrounding prose to provide context for the reader.
53+
54+
## Testing Suite
255

356
`causalprog`'s test suite is written using [`pytest`](https://docs.pytest.org/en/stable/).
457
The package can be installed with its developer dependencies, including `pytest`, by specifying the `[dev]` optional dependency when installing the package.
558

6-
## Running the tests
59+
### Running the tests
760

861
To run the test suite, you will need to clone the `causalprog` repository and then install `causalprog` into your developer environment with the `[dev]` optional dependencies.
962
We recommend specifying an editable installation if you intend to make contributions to the package.
@@ -29,7 +82,7 @@ Running
2982

3083
in the repository root will do so.
3184

32-
## Organisation of the test suite
85+
### Organisation of the test suite
3386

3487
The test suite contains a `fixtures` subdirectory, which is loaded as a `pytest` plugin when the tests are run.
3588
All `pytest.fixture` objects defined inside the `fixtures` subdirectory (and subdirectories therein) are discovered by `pytest`, and available for use by individual tests.
@@ -49,7 +102,7 @@ Our general guidelines for organising unit tests are:
49102
Any integration tests should be placed into the `test_integration` subfolder.
50103
Again, this directory should contain a single file per integration test.
51104

52-
## Useful fixtures
105+
### Useful fixtures
53106

54107
Some useful fixtures that are included in the `fixtures` directory;
55108

mkdocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ nav:
4545
- Users:
4646
- Graph: users/graph.md
4747
- Developers:
48-
- Testing suite: developers/tests.md
48+
- Contributing: developers/contributing.md
4949
- Graph: developers/graph.md
5050
- API reference: api.md
5151
- License: LICENSE.md

src/causalprog/algorithms/do.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ def get_included_excluded_successors(
1111
"""
1212
Split successors of a node into nodes included and not included in a list.
1313
14-
Split the successorts of a node into a list of nodes that are included in
14+
Split the successors of a node into a list of nodes that are included in
1515
the input node list and a list of nodes that are not in the list.
1616
1717
Args:
18-
graph: The graph
19-
node_list: A dictionary of nodes, indexed by label
20-
successors_of: The node to check the successors of
18+
graph: The graph.
19+
node_list: A dictionary of nodes, indexed by label.
20+
successors_of: The node to check the successors of.
2121
2222
Returns:
23-
Lists of included and excluded nodes
23+
Lists of included and excluded nodes.
2424
2525
"""
2626
included = []
@@ -38,11 +38,11 @@ def removable_nodes(graph: Graph, nodes: dict[str, Node]) -> tuple[str, ...]:
3838
Generate list of nodes that can be removed from the graph.
3939
4040
Args:
41-
graph: The graph
42-
nodes: A dictionary of nodes, indexed by label
41+
graph: The graph.
42+
nodes: A dictionary of nodes, indexed by label.
4343
4444
Returns:
45-
List of labels of removable nodes
45+
List of labels of removable nodes.
4646
4747
"""
4848
removable: list[str] = []
@@ -55,16 +55,16 @@ def removable_nodes(graph: Graph, nodes: dict[str, Node]) -> tuple[str, ...]:
5555

5656
def do(graph: Graph, node: str, value: float, *, label: str | None = None) -> Graph:
5757
"""
58-
Apply do to a graph.
58+
Apply `do` to a graph.
5959
6060
Args:
6161
graph: The graph to apply do to. This will be copied.
6262
node: The label of the node to apply do to.
6363
value: The value to set the node to.
64-
label: The label of the new graph
64+
label: The label of the new graph.
6565
6666
Returns:
67-
A copy of the graph with do applied
67+
A copy of the graph with do applied.
6868
6969
"""
7070
if label is None:

src/causalprog/algorithms/moments.py

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,20 @@ def sample(
1414
parameter_values: dict[str, float] | None = None,
1515
rng_key: jax.Array,
1616
) -> npt.NDArray[float]:
17-
"""Sample data from (a random variable attached to) a node in a graph."""
17+
"""
18+
Sample data from (a random variable attached to) a node in a graph.
19+
20+
Args:
21+
graph: The graph from which to sample.
22+
outcome_node_label: The label of the node to sample from.
23+
samples: Number of desired samples.
24+
parameter_values: Values to be taken by node parameters.
25+
rng_key: PRNG key to use to generate samples.
26+
27+
Returns:
28+
Array of `samples` elements, containing the random samples.
29+
30+
"""
1831
nodes = graph.roots_down_to_outcome(outcome_node_label)
1932

2033
values: dict[str, npt.NDArray[float]] = {}
@@ -38,7 +51,20 @@ def expectation(
3851
parameter_values: dict[str, float] | None = None,
3952
rng_key: jax.Array,
4053
) -> float:
41-
"""Estimate the expectation of (a random variable attached to) a node in a graph."""
54+
"""
55+
Estimate the expectation of (a random variable attached to) a node in a graph.
56+
57+
Args:
58+
graph: The graph containing the node.
59+
outcome_node_label: The label of the node to compute the expectation of.
60+
samples: Number of samples to use to estimate the expectation.
61+
parameter_values: Values to be taken by node parameters.
62+
rng_key: PRNG key to use to generate samples.
63+
64+
Returns:
65+
Approximation to the expectation of `outcome_node_label`.
66+
67+
"""
4268
return moment(
4369
1,
4470
graph,
@@ -58,7 +84,26 @@ def standard_deviation(
5884
rng_key: jax.Array,
5985
rng_key_first_moment: jax.Array | None = None,
6086
) -> float:
61-
"""Estimate the standard deviation of (a RV attached to) a node in a graph."""
87+
r"""
88+
Estimate the standard deviation of (a RV attached to) a node in a graph.
89+
90+
The method computes the standard deviation of node $X$ via the formula
91+
92+
$$ \sqrt{\mathrm{Var}(X)} = \sqrt{\mathbb{E}[X^2] - \mathbb{E}[X]^2}. $$
93+
94+
Args:
95+
graph: The graph containing the node.
96+
outcome_node_label: The label of the node to compute the standard deviation of.
97+
samples: Number of samples to use to estimate the standard deviation.
98+
parameter_values: Values to be taken by node parameters.
99+
rng_key: PRNG key to use to generate samples.
100+
rng_key_first_moment: PRNG key that will be used to approximate the expectation,
101+
used in the formula to calculate the standard deviation.
102+
103+
Returns:
104+
Approximation to the standard deviation of `outcome_node_label`.
105+
106+
"""
62107
return (
63108
moment(
64109
2,
@@ -89,7 +134,21 @@ def moment(
89134
parameter_values: dict[str, float] | None = None,
90135
rng_key: jax.Array,
91136
) -> float:
92-
"""Estimate a moment of (a random variable attached to) a node in a graph."""
137+
"""
138+
Estimate a moment of (a random variable attached to) a node in a graph.
139+
140+
Args:
141+
order: Order of the moment to estimate.
142+
graph: The graph containing the node.
143+
outcome_node_label: The label of the node to compute the moment of.
144+
samples: Number of samples to be used to estimate the moment.
145+
parameter_values: Values to be taken by node parameters.
146+
rng_key: PRNG key to use to generate samples.
147+
148+
Returns:
149+
Approximation to the `order` moment of `outcome_node_label`.
150+
151+
"""
93152
return (
94153
sum(
95154
sample(

src/causalprog/causal_problem/causal_problem.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from causalprog.graph import Graph
1515

1616

17-
# TODO: https://github.com/UCL/causalprog/issues/88
1817
def sample_model(
1918
model: Predictive, rng_key: jax.Array, parameter_values: dict[str, npt.ArrayLike]
2019
) -> dict[str, npt.ArrayLike]:

src/causalprog/graph/continuous_treatment.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -255,19 +255,14 @@ def build_causal_response_function(
255255
`xl` contains the fixed values of `x` and `l`. The latent variable
256256
`u_y` is supplied internally by the quadrature rule.
257257
258-
Parameters
259-
----------
260-
graph : Graph
261-
Ricardo's causal graph.
262-
quadrature : QuadratureMethod
263-
Quadrature rule used to evaluate the expectation over the
264-
standard-normal latent variable $U_Y$.
265-
266-
Returns
267-
-------
268-
Callable
258+
Args:
259+
graph: Graph representing a continuous treatment model.
260+
quadrature: Quadrature rule used to evaluate the expectation over the
261+
standard-normal latent variable $U_Y$.
262+
263+
Returns:
269264
A callable that evaluates the causal response function
270-
$d(x, l; \theta)$.
265+
$d(x, l; \theta)$.
271266
272267
"""
273268
if not isinstance(quadrature, UWMCGQuad):
@@ -304,7 +299,7 @@ def _d(
304299
$$
305300
d(x, l; \theta)
306301
=
307-
\mathbb{E}[Y \mid \operatorname{do}(X=x), L=l].
302+
\mathbb{E}[Y \mid \mathrm{do}(X=x), L=l].
308303
$$
309304
"""
310305
return quadrature.integrate(
@@ -391,6 +386,9 @@ def build_loss_function(
391386
evaluation_points_axes_mapping: Axes to vectorise over when evaluating $r$
392387
at the `evaluation_points`.
393388
389+
Returns:
390+
Callable that evaluates $B(\theta)$.
391+
394392
"""
395393
if r_hat_i.ndim != 1:
396394
msg = f"`r_hat_i` must be a 1D array (got {r_hat_i.shape})"

0 commit comments

Comments
 (0)