# Docstrings and Documentations

## Summary

For a detailed description of the numpydoc standard, see numpy’s actual description of the style. This should be read by each developer to make sure we understand exactly what is required and what other options we have while documenting. Here is a summary of the key components of that document:

numpydoc style docstrings are written in restructured text. They are composed of a short description of the object followed by a few required and optional sections. For functions and methods, these sections are:

Required

1. Parameters
2. Returns

Optional

1. Raises
3. Notes
4. References
5. Examples

The sections for a class are the same, except instead of a Returns section there is an Attribute section that defines all attributes of the class.

## What is a docstring?

docstring is a string that starts on the first new line immediately after the declaration of a function or a class. Like the body of the function or class, the docstring must be indented 4 spaces. Typically, a docstring is contained within a block string, set off by tripe quotes “”” as shown below:

def func(a, b):
"""
This is a docstring for a function
""""

class MyNewClass(object):
"""
This is a docstring for this class.
"""

## What are docstrings used for?

Docstrings provide a way for developers to document their code inline. This is very useful for many reasons, among which are:

1. Writing “”” docstrings (where “”” means following the numpydoc standard desribed below) force developers to provide a concise explanation of what the code is supposed to do. This leads to code that is more focused, efficient, and clear.
2. A good docstring serves as the starting point for other developers who work on code someone else has written (or for the original author who comes back to the code after initially writing it). If the intent of the code is clearly defined in the docstring, then all who work on this code later know exactly what the purpose of the code is.
3. As hinted at above, docstrings are the code printed at the interpreter when users ask for help on a particular object.

## numpydoc style docstrings

numpydoc style docstrings are written using restructured text (aka rst: the markup used in sphinx documents), using a special structure. While any restructured text is valid in a numpydoc style docstring, the only required restructured text element is knowing how to define a section. In our docstrings, we will denote sections using the following syntax:

Section name
------------

The key elements are a line containing only the name of the section followed be another line containing only minus signs (-) underneath each character of the section name.

### Documenting functions and methods

Using this syntax, a numpydoc style docstring for a function or method is made up of the following elements:

1. A short description of what the function or class does
2. A section named Parameters that explains the object’s input parameters
3. A section named Returns that explains that function’s output
4. Any of the following “optional sections”; (in any order):
1. Examples: This section is optional, but strongly recommended and encouraged within QuantEcon (it may be required at some time in the future). Within the section we would include one or more examples for how to use the obejct.
2. Notes: This section provides additional details that help the user or fellow developer understand the function, but were not included in the short description at the beginning. This might include a description of the algorithm or mathematics underlying the function.
3. References: If any journal articles, books, or other publications or 3rd party code were consulted when writing the function, this should be documented in this section.
4. Raises: if the function/class might raise an exception when called, the exception along with cases under which the exception is raised should be documented.
5. See Also: if the function/class is related to another object, we should specify this relation in a See Also section.

Below is an example of such a docstring

def func(a, b, c=3):
"""
Short description of what the function accomplishes

Parameters
----------
a : scalar(float)
Explanation of what a is used for within the function

b : array_like(float)
Explanation of what a is used for within the function

c : scalar(int), optional(default=3)
Explanation of what c is used for within the function

Returns
-------
ret1 : type of ret1
Explanation of the first object returned

ret2 : type of ret2
Explanation of the second object returned

Notes
-----
Any implementation notes that help understand how/why the function
was written the way it was. This section is optional.

References
----------
In this section we include any references to papers/articles/other
reading material or other code that we referenced when writing the
function

Examples
--------
>>> a = 130; b = [10, 20, 30]
>>> func(a, b)
&lt;function output copied and pasted&gt;

Raises
------
ValueError
If  any parameters are equal to None.

"""

There are a few additional points to mention here:

• When listing parameters and return types, we start by providing the variable name, then space-colon-space, then the type of the object. Additionaly, if the parameter is optional, we say this. I also like to give the default value in parenthsis after the word optional (see the line for the parameter c above)
• We should make sure that each line in a docstring is no longer than 72 characters (as specified in the official python style guide, PEP8)
• The examples section should feel just like a copy/paste from an interactive python session where the example’s code was executed.
• There should be a blank line at the end of the docstring.
• When documenting a method, do not include self in the list of parameters.

### Documenting classes

The numpydoc standard for documenting classes is very similar. The key components are (this all goes the first line of the docstring that is below the line containing the class keyword)

1. A short description of what the class does
2. Parameters section that describes the parameters for the class’s __init__ function
3. An Attributes section that describes all the attributes of the class. This replaces the Returns section we saw above for functions and methods.
4. Any of the optional sections outlined above in addition to an optional section describing the Methodsthat are implemented specifically for this class.

Remark There is often overlap between the parameter list and the attribute list. To avoid repetition attributes already discussed in the parameter list can be entered in the attributes list in the form a, b, c : seeParameters. An example is given below.

## Examples

Below I will provide some examples. These are copied and pasted from the source of some code we are already using, or will soon be using from within QuantEcon. For additional examples, see these two references:

### Function example

def poly_inds(d, mu, inds=None):
"""
Build indices specifying all the Cartesian products of Chebychev
polynomials needed to build Smolyak polynomial

Parameters
----------
d : scalar(int)
The number of dimensions in grid / polynomial

mu : scalar(int)
The parameter mu defining the density of the grid

inds : list(list(int)), optional(default=None)
The Smolyak indices for parameters d and mu. Should be computed
by calling smol_inds(d, mu). If None is given, the indices
are computed using this function call

Returns
-------
phi_inds : array_like(int, ndim=2)
A two dimensional array of integers where each row specifies a
new set of indices needed to define a Smolyak basis polynomial

Notes
-----
This function uses smol_inds and phi_chain. The output of this
function is used by build_B to construct the B matrix

"""

### Class example

class SmolyakGrid(object):
"""
This class currently takes a dimension and a degree of polynomial
and builds the Smolyak Sparse grid.  We base this on the work by
Judd, Maliar, Maliar, and Valero (2013).

Parameters
----------
d : scalar(int)
The number of dimensions in the grid

mu : scalar(int) or array_like(int, ndim=1, length=d)
The &quot;density&quot; parameter for the grid

Attributes
----------
d, mu : see Parameters

lb : array_like(float, ndim=2)
This is an array of the lower bounds for each dimension

ub : array_like(float, ndim=2)
This is an array of the upper bounds for each dimension

cube_grid : array_like(float, ndim=2)
The Smolyak sparse grid on the domain :math:[-1, 1]^d

grid: : array_like(float, ndim=2)
The sparse grid, transformed to the user-specified bounds for
the domain

inds : list(list(int))
This is a lists of lists that contains all of the indices

B : array_like(float, ndim=2)
This is the B matrix that is used to do lagrange interpolation

B_L : array_like(float, ndim=2)
Lower triangle matrix of the decomposition of B

B_U : array_like(float, ndim=2)
Upper triangle matrix of the decomposition of B

Examples
--------
>>> s = SmolyakGrid(3, 2)
>>> s
Smolyak Grid:
d: 3
mu: 2
npoints: 25
B: 0.65% non-zero
>>> ag = SmolyakGrid(3, [1, 2, 3])
>>> ag
Anisotropic Smolyak Grid:
d: 3
mu: 1 x 2 x 3
npoints: 51
B: 0.68% non-zero

"""

Method example
--------------
def interpolate(self, pts, interp=True, deriv=False):
"""
Basic Lagrange interpolation, with optional first derivatives

Parameters
----------
pts : array_like(float, ndim=2)
A 2d array of points on which to evaluate the function. Each
row is assumed to be a new d-dimensional point. Therefore, pts
must have the same number of columns as si.SGrid.d

interp : bool, optional(default=false)
Whether or not to compute the actual interpolation values at pts

deriv : bool, optional(default=false)
Whether or not to compute the gradient of the function at each
of the points. This will have the same dimensions as pts, where
each column represents the partial derivative with respect to
a new dimension.

Returns
-------
rets : list(array(float))
A list of arrays containing the objects asked for. There are 2
possible objects that can be computed in this function. They will,
if they are called for, always be in the following order:

1. Interpolation values at pts
This is a stripped down port of dolo.SmolyakBasic.interpolate
"""