# Memoisation

## Across metrics

deterministic probabilistic

Since certain evaluation metrics require the same intermediate computations,
there is scope for some optimisation by storing these intermediate computations
so that they can be used by all the evaluation metrics requiring them (i.e.
the concept of *memoisation* in computing).

For example, the deterministic metrics *NSE* and *KGE* both require to compute
the quadratic error between the observations and their arithmetic mean, so it
is more efficient to compute this quadratic error only once, and reuse it in
the computation of both *NSE* and *KGE*.

`evalhyd`

implements such approach to compute its evaluation metrics, this is
why it is recommended to ask for all evaluation metrics of interest at once in
a single call to `evalhyd`

rather than ask for them separately in several
calls.

That is to say, prefer:

```
>>> evalhyd.evald(obs, prd, ["NSE", "KGE"])
```

```
> evalhyd::evald(obs, prd, c("NSE", "KGE"))
```

```
$ ./evalhyd evald "obs.csv" "prd.csv" "NSE" "KGE"
```

over:

```
>>> evalhyd.evald(obs, prd, ["NSE"])
>>> evalhyd.evald(obs, prd, ["KGE"])
```

```
> evalhyd::evald(obs, prd, c("NSE"))
> evalhyd::evald(obs, prd, c("KGE"))
```

```
$ ./evalhyd evald "obs.csv" "prd.csv" "NSE"
$ ./evalhyd evald "obs.csv" "prd.csv" "KGE"
```

## Across masks

deterministic probabilistic

In addition, most evaluation metrics first perform intermediate computations on each time step individually (e.g. errors between individual observations and their corresponding predictions), before performing some reduction across all time steps (e.g. arithmetic mean of these individual errors).

If different subset periods of the entire study period are needed (i.e.
using the temporal masking or the
conditional masking functionalities), and these
sub-periods happen to overlap, it is recommended to provide several masks
at once to `evalhyd`

rather than one mask at a time. Indeed, `evalhyd`

applies the masks only after the intermediate computations on individual
time steps are computed, thus optimising the computation time by avoiding
performing these intermediate computations on the same time steps several
times.

That is to say, prefer:

```
>>> res = evalhyd.evald(
... obs, prd, ["NSE"],
... t_msk=np.array([[[True, True, False, True, False, True],
... [False, True, True, True, False, True]]])
... )
```

```
> evalhyd::evald(
+ obs, prd, c("NSE")
+ t_msk = array(
+ data = rbind(c(TRUE, TRUE, FALSE, TRUE, FALSE, TRUE),
+ c(FALSE, TRUE, TRUE, TRUE, FALSE, TRUE)),
+ dim = c(1, 2, 6)
+ )
+ )
```

over:

```
>>> res = evalhyd.evald(
... obs, prd, ["NSE"],
... t_msk=np.array([[[True, True, False, True, False, True]]])
... )
>>> res = evalhyd.evald(
... obs, prd, ["NSE"],
... t_msk=np.array([[[False, True, True, True, False, True]]])
... )
```

```
> evalhyd::evald(
+ obs, prd, c("NSE")
+ t_msk = array(
+ data = c(TRUE, TRUE, FALSE, TRUE, FALSE, TRUE),
+ dim = c(1, 1, 6)
+ )
+ )
> evalhyd::evald(
+ obs, prd, c("NSE")
+ t_msk = array(
+ data = c(FALSE, TRUE, TRUE, TRUE, FALSE, TRUE),
+ dim = c(1, 1, 6)
+ )
+ )
```