codecov-io: # [Codecov](https://codecov.io/gh/ICB-DCM/pyABC/pull/279?src=pr&el=h1) Report
> Merging [#279](https://codecov.io/gh/ICB-DCM/pyABC/pull/279?src=pr&el=desc) into [develop](https://codecov.io/gh/ICB-DCM/pyABC/commit/f03104337cff23cfdbcdaf3c7bc31cf8a9c7ec8b?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/ICB-DCM/pyABC/pull/279/graphs/tree.svg?width=650&token=GcyGdqSygr&height=150&src=pr)](https://codecov.io/gh/ICB-DCM/pyABC/pull/279?src=pr&el=tree)

```diff
@@           Coverage Diff            @@
##           develop     #279   +/-   ##
========================================
  Coverage    83.76%   83.76%           
========================================
  Files           88       88           
  Lines         4871     4871           
========================================
  Hits          4080     4080           
  Misses         791      791
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/ICB-DCM/pyABC/pull/279?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/ICB-DCM/pyABC/pull/279?src=pr&el=footer). Last update [f031043...70afb26](https://codecov.io/gh/ICB-DCM/pyABC/pull/279?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

diff --git a/pyabc/distance/distance.py b/pyabc/distance/distance.py
index b9cb65f..245de38 100644
--- a/pyabc/distance/distance.py
+++ b/pyabc/distance/distance.py
@@ -143,6 +143,9 @@ class AdaptivePNormDistance(PNormDistance):
 
     p: float, optional (default = 2)
         p for p-norm. Required p >= 1, p = np.inf allowed (infinity-norm).
+    initial_weights: dict, optional
+        Weights to be used in the initial iteration. Dictionary with
+        observables as keys and weights as values.
     factors: dict, optional
         As in PNormDistance.
     adaptive: bool, optional (default = True)
@@ -165,13 +168,13 @@ class AdaptivePNormDistance(PNormDistance):
         smallest non-zero absolute weight. In practice usually not necessary,
         it is theoretically required to ensure convergence.
 
-
     .. [#prangle] Prangle, Dennis. "Adapting the ABC Distance Function".
                 Bayesian Analysis, 2017. doi:10.1214/16-BA1002.
     """
 
     def __init__(self,
                  p: float = 2,
+                 initial_weights: dict = None,
                  factors: dict = None,
                  adaptive: bool = True,
                  scale_function=None,
@@ -180,6 +183,7 @@ class AdaptivePNormDistance(PNormDistance):
         # call p-norm constructor
         super().__init__(p=p, weights=None, factors=factors)
 
+        self.initial_weights = initial_weights
         self.factors = factors
         self.adaptive = adaptive
 
@@ -218,6 +222,11 @@ class AdaptivePNormDistance(PNormDistance):
         super().initialize(t, get_all_sum_stats, x_0)
         self.x_0 = x_0
 
+        # initial weights pre-defined
+        if self.initial_weights is not None:
+            self.weights[t] = self.initial_weights
+            return
+
         # execute function
         all_sum_stats = get_all_sum_stats()
 
@@ -488,6 +497,9 @@ class AdaptiveAggregatedDistance(AggregatedDistance):
 
     distances: List[Distance]
         As in AggregatedDistance.
+    initial_weights: list, optional
+        Weights to be used in the initial iteration. List with
+        a weight for each distance function.
     factors: Union[List, dict]
         As in AggregatedDistance.
     adaptive: bool, optional (default = True)
@@ -504,10 +516,12 @@ class AdaptiveAggregatedDistance(AggregatedDistance):
     def __init__(
             self,
             distances: List[Distance],
+            initial_weights: List = None,
             factors: Union[List, dict] = None,
             adaptive: bool = True,
             scale_function: Callable = None):
         super().__init__(distances=distances)
+        self.initial_weights = initial_weights
         self.factors = factors
         self.adaptive = adaptive
         self.x_0 = None
@@ -525,6 +539,10 @@ class AdaptiveAggregatedDistance(AggregatedDistance):
         super().initialize(t, get_all_sum_stats, x_0)
         self.x_0 = x_0
 
+        if self.initial_weights is not None:
+            self.weights[t] = self.initial_weights
+            return
+
         # execute function
         all_sum_stats = get_all_sum_stats()
 

Allow for initial weights in AdaptiveAggregatedDistance
Use case is, if one has a reasonable set of first weights but would want to have them changed adaptively over the following populations. 
Initializing with weights will also skip the prepopulation.
diff --git a/test/test_distance_function.py b/test/test_distance_function.py
index d3f8f1f..e40c2fc 100644
--- a/test/test_distance_function.py
+++ b/test/test_distance_function.py
@@ -84,7 +84,7 @@ def test_pnormdistance():
     # call distance function, also to initialize w
     d = dist_f(abc.sample_from_prior()[0], abc.sample_from_prior()[1], t=0)
 
-    expected = pow(1**2 + 2**2, 1 / 2)
+    expected = pow(1**2 + 2**2, 1/2)
     assert expected == d
 
     assert sum(abs(a - b) for a, b in
@@ -117,6 +117,7 @@ def test_adaptivepnormdistance():
             scale_function=scale_function)
         dist_f.initialize(0, abc.sample_from_prior, x_0=x_0)
         dist_f(abc.sample_from_prior()[0], abc.sample_from_prior()[1], t=0)
+        assert dist_f.weights[0] != {'s1': 1, 's2': 1, 's3': 1}
 
     # test max weight ratio
     for scale_function in scale_functions:
@@ -125,6 +126,28 @@ def test_adaptivepnormdistance():
             max_weight_ratio=20)
         dist_f.initialize(0, abc.sample_from_prior, x_0=x_0)
         dist_f(abc.sample_from_prior()[0], abc.sample_from_prior()[1], t=0)
+        assert dist_f.weights[0] != {'s1': 1, 's2': 1, 's3': 1}
+
+
+def test_adaptivepnormdistance_initial_weights():
+    abc = MockABC([{'s1': -1, 's2': -1, 's3': -1},
+                   {'s1': -1, 's2': 0, 's3': 1}])
+    x_0 = {'s1': 0, 's2': 0, 's3': 1}
+
+    # first test that for PNormDistance, the weights stay constant
+    initial_weights = {'s1': 1, 's2': 2, 's3': 3}
+    dist_f = AdaptivePNormDistance(initial_weights=initial_weights)
+    dist_f.initialize(0, abc.sample_from_prior, x_0=x_0)
+    assert dist_f.weights[0] == initial_weights
+
+    # call distance function
+    d = dist_f(abc.sample_from_prior()[0], abc.sample_from_prior()[1], t=0)
+    expected = pow(sum([(2*1)**2, (3*2)**2]), 1/2)
+    assert expected == d
+
+    # check updating works
+    dist_f.update(1, abc.sample_from_prior)
+    assert dist_f.weights[1] != dist_f.weights[0]
 
 
 def test_aggregateddistance():
@@ -165,6 +188,33 @@ def test_adaptiveaggregateddistance():
         val = distance(
             abc.sample_from_prior()[0], abc.sample_from_prior()[1], t=0)
         assert isinstance(val, float)
+        assert (distance.weights[0] != [1, 1]).any()
+
+
+def test_adaptiveaggregateddistance_calibration():
+    abc = MockABC([{'s0': -1, 's1': -1},
+                   {'s0': -1, 's1': 0}])
+    x_0 = {'s0': 0, 's1': 0}
+
+    def distance0(x, x_0):
+        return abs(x['s0'] - x_0['s0'])
+
+    def distance1(x, x_0):
+        return np.sqrt((x['s1'] - x_0['s1'])**2)
+
+    scale_functions = [span, mean, median]
+    initial_weights = np.array([2, 3])
+    for scale_function in scale_functions:
+        distance = AdaptiveAggregatedDistance(
+            [distance0, distance1], scale_function=scale_function,
+            initial_weights=initial_weights)
+        distance.initialize(0, abc.sample_from_prior, x_0=x_0)
+        val = distance(
+            abc.sample_from_prior()[0], abc.sample_from_prior()[1], t=0)
+        assert isinstance(val, float)
+        assert (distance.weights[0] == initial_weights).all()
+        distance.update(1, abc.sample_from_prior)
+        assert (distance.weights[1] != distance.weights[0]).all()
 
 
 def test_normalkernel():

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
asttokens==3.0.0
async-timeout==5.0.1
bkcharts==0.2
blinker==1.9.0
bokeh==3.4.3
click==8.1.8
cloudpickle==3.1.1
contourpy==1.3.0
coverage==7.8.0
cycler==0.12.1
dask==2024.8.0
decorator==5.2.1
dill==0.3.9
distributed==2024.8.0
dominate==2.9.1
exceptiongroup==1.2.2
executing==2.2.0
feather-format==0.4.1
Flask==3.1.0
Flask-Bootstrap==3.3.7.1
fonttools==4.56.0
fsspec==2025.3.1
gitdb==4.0.12
GitPython==3.1.44
greenlet==3.1.1
importlib_metadata==8.6.1
importlib_resources==6.5.2
iniconfig==2.1.0
ipython==8.18.1
itsdangerous==2.2.0
jedi==0.19.2
Jinja2==3.1.6
joblib==1.4.2
kiwisolver==1.4.7
locket==1.0.0
MarkupSafe==3.0.2
matplotlib==3.9.4
matplotlib-inline==0.1.7
msgpack==1.1.0
numpy==2.0.2
packaging==24.2
pandas==2.2.3
parso==0.8.4
partd==1.4.2
pexpect==4.9.0
pillow==11.1.0
pluggy==1.5.0
prompt_toolkit==3.0.50
psutil==7.0.0
ptyprocess==0.7.0
pure_eval==0.2.3
-e git+https://github.com/ICB-DCM/pyABC.git@e3d4f3c73286fa44b092782d56cf517470fac33a#egg=pyabc
pyarrow==19.0.1
Pygments==2.19.1
pyparsing==3.2.3
pytest==8.3.5
pytest-cov==6.0.0
python-dateutil==2.9.0.post0
pytz==2025.2
PyYAML==6.0.2
redis==5.2.1
scikit-learn==1.6.1
scipy==1.13.1
six==1.17.0
smmap==5.0.2
sortedcontainers==2.4.0
SQLAlchemy==2.0.40
stack-data==0.6.3
tblib==3.1.0
threadpoolctl==3.6.0
tomli==2.2.1
toolz==1.0.0
tornado==6.4.2
traitlets==5.14.3
typing_extensions==4.13.0
tzdata==2025.2
urllib3==2.3.0
visitor==0.1.3
wcwidth==0.2.13
Werkzeug==3.1.3
xyzservices==2025.1.0
zict==3.0.0
zipp==3.21.0

name: pyABC
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - asttokens==3.0.0
      - async-timeout==5.0.1
      - bkcharts==0.2
      - blinker==1.9.0
      - bokeh==3.4.3
      - click==8.1.8
      - cloudpickle==3.1.1
      - contourpy==1.3.0
      - coverage==7.8.0
      - cycler==0.12.1
      - dask==2024.8.0
      - decorator==5.2.1
      - dill==0.3.9
      - distributed==2024.8.0
      - dominate==2.9.1
      - exceptiongroup==1.2.2
      - executing==2.2.0
      - feather-format==0.4.1
      - flask==3.1.0
      - flask-bootstrap==3.3.7.1
      - fonttools==4.56.0
      - fsspec==2025.3.1
      - gitdb==4.0.12
      - gitpython==3.1.44
      - greenlet==3.1.1
      - importlib-metadata==8.6.1
      - importlib-resources==6.5.2
      - iniconfig==2.1.0
      - ipython==8.18.1
      - itsdangerous==2.2.0
      - jedi==0.19.2
      - jinja2==3.1.6
      - joblib==1.4.2
      - kiwisolver==1.4.7
      - locket==1.0.0
      - markupsafe==3.0.2
      - matplotlib==3.9.4
      - matplotlib-inline==0.1.7
      - msgpack==1.1.0
      - numpy==2.0.2
      - packaging==24.2
      - pandas==2.2.3
      - parso==0.8.4
      - partd==1.4.2
      - pexpect==4.9.0
      - pillow==11.1.0
      - pluggy==1.5.0
      - prompt-toolkit==3.0.50
      - psutil==7.0.0
      - ptyprocess==0.7.0
      - pure-eval==0.2.3
      - pyarrow==19.0.1
      - pygments==2.19.1
      - pyparsing==3.2.3
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyyaml==6.0.2
      - redis==5.2.1
      - scikit-learn==1.6.1
      - scipy==1.13.1
      - six==1.17.0
      - smmap==5.0.2
      - sortedcontainers==2.4.0
      - sqlalchemy==2.0.40
      - stack-data==0.6.3
      - tblib==3.1.0
      - threadpoolctl==3.6.0
      - tomli==2.2.1
      - toolz==1.0.0
      - tornado==6.4.2
      - traitlets==5.14.3
      - typing-extensions==4.13.0
      - tzdata==2025.2
      - urllib3==2.3.0
      - visitor==0.1.3
      - wcwidth==0.2.13
      - werkzeug==3.1.3
      - xyzservices==2025.1.0
      - zict==3.0.0
      - zipp==3.21.0
prefix: /opt/conda/envs/pyABC

test/test_distance_function.py::test_adaptivepnormdistance_initial_weights test/test_distance_function.py::test_adaptiveaggregateddistance_calibration
test/test_distance_function.py::test_single_parameter test/test_distance_function.py::test_two_parameters_but_only_one_used test/test_distance_function.py::test_two_parameters_and_two_used test/test_distance_function.py::test_single_parameter_percentile test/test_distance_function.py::test_pnormdistance test/test_distance_function.py::test_adaptivepnormdistance test/test_distance_function.py::test_aggregateddistance test/test_distance_function.py::test_adaptiveaggregateddistance test/test_distance_function.py::test_normalkernel test/test_distance_function.py::test_independentnormalkernel test/test_distance_function.py::test_independentlaplacekernel test/test_distance_function.py::test_binomialkernel test/test_distance_function.py::test_poissonkernel test/test_distance_function.py::test_negativebinomialkernel
pyabc/distance/distance.py
pyabc/distance/distance.py