This merge request implements a Learner2D that can learn averages on the points, the `AverageLearner2D`

.

When chosing points the learner can either

- add more values at an existing points
- add more triangles

The learner compares the loss of potential new triangles with the standard error of an existing point.

The relative importance of both can be adjusted by a hyperparameter `learner.weight`

.
From the doc-string:

When

`weight > 1`

adding more points to existing points will be prioritized (making the standard error of a point more imporant,) otherwise adding new triangles will be prioritized (making the loss of a triangle more important.)

All tests that pass for the `Learner2D`

currently pass for the `AvererageLearner2D`

too.

Run with:

```
import adaptive
adaptive.notebook_extension()
def ring(xy_seed):
import numpy as np
import random
(x, y), seed = xy_seed
random.seed(seed)
noise = 0.5 * (random.random() - 1)
a = 0.2
return x + np.exp(-(x**2 + y**2 - 0.75**2)**2/a**4) + noise
learner = adaptive.AverageLearner2D(ring, bounds=[(-1, 1), (-1, 1)], weight=.1)
runner = adaptive.Runner(learner, goal=lambda l: l.loss() < 0.01, log=True)
runner.live_info()
```

which results in:

```
>>> print(learner.mean_values_per_point())
5.2737642585551328
```

and