cmdstanpy 1.3.0__tar.gz → 1.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cmdstanpy-1.3.0/cmdstanpy.egg-info → cmdstanpy-1.3.2}/PKG-INFO +3 -3
- cmdstanpy-1.3.2/cmdstanpy/_version.py +3 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/install_cxx_toolchain.py +5 -7
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/model.py +29 -8
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/progress.py +1 -2
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/gq.py +1 -1
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/laplace.py +2 -1
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/mcmc.py +2 -1
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/runset.py +18 -12
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/utils/__init__.py +3 -5
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/utils/cmdstan.py +4 -3
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/utils/data_munging.py +2 -1
- cmdstanpy-1.3.2/cmdstanpy/utils/deprecation.py +58 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2/cmdstanpy.egg-info}/PKG-INFO +3 -3
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy.egg-info/SOURCES.txt +2 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy.egg-info/requires.txt +2 -2
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/pyproject.toml +2 -2
- cmdstanpy-1.3.2/test/test_deprecations.py +140 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_generate_quantities.py +4 -4
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_logging.py +1 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_optimize.py +13 -13
- cmdstanpy-1.3.0/cmdstanpy/_version.py +0 -3
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/LICENSE.md +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/MANIFEST.in +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/README.md +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/__init__.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/cmdstan_args.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/compilation.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/install_cmdstan.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/py.typed +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/__init__.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/metadata.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/mle.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/pathfinder.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/vb.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/utils/command.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/utils/filesystem.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/utils/json.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/utils/logging.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy/utils/stancsv.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy.egg-info/dependency_links.txt +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy.egg-info/entry_points.txt +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/cmdstanpy.egg-info/top_level.txt +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/setup.cfg +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_cmdstan_args.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_compiler_opts.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_compliance.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_cxx_installation.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_install_cmdstan.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_laplace.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_log_prob.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_metadata.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_model.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_pathfinder.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_runset.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_sample.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_stancsv.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_utils.py +0 -0
- {cmdstanpy-1.3.0 → cmdstanpy-1.3.2}/test/test_variational.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cmdstanpy
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: Python interface to CmdStan
|
|
5
5
|
Author: Stan Dev Team
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -34,8 +34,8 @@ Requires-Dist: mypy; extra == "test"
|
|
|
34
34
|
Requires-Dist: xarray; extra == "test"
|
|
35
35
|
Requires-Dist: polars>=1.8.2; extra == "test"
|
|
36
36
|
Provides-Extra: docs
|
|
37
|
-
Requires-Dist: sphinx<
|
|
38
|
-
Requires-Dist: pydata-sphinx-theme<0.
|
|
37
|
+
Requires-Dist: sphinx<9; extra == "docs"
|
|
38
|
+
Requires-Dist: pydata-sphinx-theme<0.17; extra == "docs"
|
|
39
39
|
Requires-Dist: nbsphinx; extra == "docs"
|
|
40
40
|
Requires-Dist: ipython; extra == "docs"
|
|
41
41
|
Requires-Dist: ipykernel<7; extra == "docs"
|
|
@@ -33,16 +33,14 @@ IS_64BITS = sys.maxsize > 2**32
|
|
|
33
33
|
|
|
34
34
|
def usage() -> None:
|
|
35
35
|
"""Print usage."""
|
|
36
|
-
print(
|
|
37
|
-
"""Arguments:
|
|
36
|
+
print("""Arguments:
|
|
38
37
|
-v (--version) :CmdStan version
|
|
39
38
|
-d (--dir) : install directory
|
|
40
39
|
-s (--silent) : install with /VERYSILENT instead of /SILENT for RTools
|
|
41
40
|
-m (--no-make) : don't install mingw32-make (Windows RTools 4.0 only)
|
|
42
41
|
--progress : flag, when specified show progress bar for RTools download
|
|
43
42
|
-h (--help) : this message
|
|
44
|
-
"""
|
|
45
|
-
)
|
|
43
|
+
""")
|
|
46
44
|
|
|
47
45
|
|
|
48
46
|
def get_config(dir: str, silent: bool) -> list[str]:
|
|
@@ -244,11 +242,11 @@ def get_url(version: str) -> str:
|
|
|
244
242
|
if version == '4.0':
|
|
245
243
|
# pylint: disable=line-too-long
|
|
246
244
|
if IS_64BITS:
|
|
247
|
-
url = 'https://cran.r-project.org/bin/windows/Rtools/rtools40-x86_64.exe' # noqa: disable=E501
|
|
245
|
+
url = 'https://cran-archive.r-project.org/bin/windows/Rtools/rtools40-x86_64.exe' # noqa: disable=E501
|
|
248
246
|
else:
|
|
249
|
-
url = 'https://cran.r-project.org/bin/windows/Rtools/rtools40-i686.exe' # noqa: disable=E501
|
|
247
|
+
url = 'https://cran-archive.r-project.org/bin/windows/Rtools/rtools40-i686.exe' # noqa: disable=E501
|
|
250
248
|
elif version == '3.5':
|
|
251
|
-
url = 'https://cran.r-project.org/bin/windows/Rtools/Rtools35.exe'
|
|
249
|
+
url = 'https://cran-archive.r-project.org/bin/windows/Rtools/Rtools35.exe' # noqa: disable=E501
|
|
252
250
|
return url
|
|
253
251
|
|
|
254
252
|
|
|
@@ -55,6 +55,7 @@ from cmdstanpy.utils import (
|
|
|
55
55
|
do_command,
|
|
56
56
|
get_logger,
|
|
57
57
|
)
|
|
58
|
+
from cmdstanpy.utils.deprecation import deprecate_positional_args
|
|
58
59
|
from cmdstanpy.utils.filesystem import (
|
|
59
60
|
temp_inits,
|
|
60
61
|
temp_metrics,
|
|
@@ -468,9 +469,11 @@ class CmdStanModel:
|
|
|
468
469
|
user_header=self._compiler_options.user_header,
|
|
469
470
|
)
|
|
470
471
|
|
|
472
|
+
@deprecate_positional_args(last_deprecated="jacobian")
|
|
471
473
|
def optimize(
|
|
472
474
|
self,
|
|
473
475
|
data: Union[Mapping[str, Any], str, os.PathLike, None] = None,
|
|
476
|
+
*,
|
|
474
477
|
seed: Optional[int] = None,
|
|
475
478
|
inits: Union[Mapping[str, Any], float, str, os.PathLike, None] = None,
|
|
476
479
|
output_dir: OptionalPath = None,
|
|
@@ -492,7 +495,6 @@ class CmdStanModel:
|
|
|
492
495
|
time_fmt: str = "%Y%m%d%H%M%S",
|
|
493
496
|
timeout: Optional[float] = None,
|
|
494
497
|
jacobian: bool = False,
|
|
495
|
-
# would be nice to move this further up, but that's a breaking change
|
|
496
498
|
) -> CmdStanMLE:
|
|
497
499
|
"""
|
|
498
500
|
Run the specified CmdStan optimize algorithm to produce a
|
|
@@ -660,9 +662,11 @@ class CmdStanModel:
|
|
|
660
662
|
return mle
|
|
661
663
|
|
|
662
664
|
# pylint: disable=too-many-arguments
|
|
665
|
+
@deprecate_positional_args(last_deprecated="timeout")
|
|
663
666
|
def sample(
|
|
664
667
|
self,
|
|
665
668
|
data: Union[Mapping[str, Any], str, os.PathLike, None] = None,
|
|
669
|
+
*,
|
|
666
670
|
chains: Optional[int] = None,
|
|
667
671
|
parallel_chains: Optional[int] = None,
|
|
668
672
|
threads_per_chain: Optional[int] = None,
|
|
@@ -693,14 +697,13 @@ class CmdStanModel:
|
|
|
693
697
|
fixed_param: bool = False,
|
|
694
698
|
output_dir: OptionalPath = None,
|
|
695
699
|
sig_figs: Optional[int] = None,
|
|
696
|
-
|
|
700
|
+
save_diagnostics: bool = False,
|
|
697
701
|
save_profile: bool = False,
|
|
698
702
|
show_progress: bool = True,
|
|
699
703
|
show_console: bool = False,
|
|
700
704
|
refresh: Optional[int] = None,
|
|
701
705
|
time_fmt: str = "%Y%m%d%H%M%S",
|
|
702
706
|
timeout: Optional[float] = None,
|
|
703
|
-
*,
|
|
704
707
|
force_one_process_per_chain: Optional[bool] = None,
|
|
705
708
|
inv_metric: Union[
|
|
706
709
|
str,
|
|
@@ -709,6 +712,7 @@ class CmdStanModel:
|
|
|
709
712
|
list[Union[str, np.ndarray, Mapping[str, Any]]],
|
|
710
713
|
None,
|
|
711
714
|
] = None,
|
|
715
|
+
save_latent_dynamics: Optional[bool] = None,
|
|
712
716
|
) -> CmdStanMCMC:
|
|
713
717
|
"""
|
|
714
718
|
Run or more chains of the NUTS-HMC sampler to produce a set of draws
|
|
@@ -907,6 +911,13 @@ class CmdStanModel:
|
|
|
907
911
|
if fixed_param is None:
|
|
908
912
|
fixed_param = self._fixed_param
|
|
909
913
|
|
|
914
|
+
if save_latent_dynamics is not None:
|
|
915
|
+
get_logger().warning(
|
|
916
|
+
"Argument name `save_latent_dynamics` is deprecated, please "
|
|
917
|
+
"rename to `save_diagnostics`."
|
|
918
|
+
)
|
|
919
|
+
save_diagnostics = save_latent_dynamics # type: ignore
|
|
920
|
+
|
|
910
921
|
if chains is None:
|
|
911
922
|
chains = 4
|
|
912
923
|
if chains < 1:
|
|
@@ -1092,7 +1103,7 @@ class CmdStanModel:
|
|
|
1092
1103
|
inits=cmdstan_inits,
|
|
1093
1104
|
output_dir=output_dir,
|
|
1094
1105
|
sig_figs=sig_figs,
|
|
1095
|
-
save_latent_dynamics=
|
|
1106
|
+
save_latent_dynamics=save_diagnostics,
|
|
1096
1107
|
save_profile=save_profile,
|
|
1097
1108
|
method_args=sampler_args,
|
|
1098
1109
|
refresh=refresh,
|
|
@@ -1190,10 +1201,12 @@ class CmdStanModel:
|
|
|
1190
1201
|
mcmc = CmdStanMCMC(runset)
|
|
1191
1202
|
return mcmc
|
|
1192
1203
|
|
|
1204
|
+
@deprecate_positional_args(last_deprecated="timeout")
|
|
1193
1205
|
def generate_quantities(
|
|
1194
1206
|
self,
|
|
1195
1207
|
data: Union[Mapping[str, Any], str, os.PathLike, None] = None,
|
|
1196
1208
|
previous_fit: Union[Fit, list[str], None] = None,
|
|
1209
|
+
*,
|
|
1197
1210
|
seed: Optional[int] = None,
|
|
1198
1211
|
gq_output_dir: OptionalPath = None,
|
|
1199
1212
|
sig_figs: Optional[int] = None,
|
|
@@ -1201,7 +1214,6 @@ class CmdStanModel:
|
|
|
1201
1214
|
refresh: Optional[int] = None,
|
|
1202
1215
|
time_fmt: str = "%Y%m%d%H%M%S",
|
|
1203
1216
|
timeout: Optional[float] = None,
|
|
1204
|
-
*,
|
|
1205
1217
|
mcmc_sample: Union[CmdStanMCMC, list[str], None] = None,
|
|
1206
1218
|
) -> CmdStanGQ[Fit]:
|
|
1207
1219
|
"""
|
|
@@ -1383,14 +1395,16 @@ class CmdStanModel:
|
|
|
1383
1395
|
quantities = CmdStanGQ(runset=runset, previous_fit=fit_object)
|
|
1384
1396
|
return quantities
|
|
1385
1397
|
|
|
1398
|
+
@deprecate_positional_args(last_deprecated="timeout")
|
|
1386
1399
|
def variational(
|
|
1387
1400
|
self,
|
|
1388
1401
|
data: Union[Mapping[str, Any], str, os.PathLike, None] = None,
|
|
1402
|
+
*,
|
|
1389
1403
|
seed: Optional[int] = None,
|
|
1390
1404
|
inits: Optional[float] = None,
|
|
1391
1405
|
output_dir: OptionalPath = None,
|
|
1392
1406
|
sig_figs: Optional[int] = None,
|
|
1393
|
-
|
|
1407
|
+
save_diagnostics: bool = False,
|
|
1394
1408
|
save_profile: bool = False,
|
|
1395
1409
|
algorithm: Optional[str] = None,
|
|
1396
1410
|
iter: Optional[int] = None,
|
|
@@ -1407,8 +1421,8 @@ class CmdStanModel:
|
|
|
1407
1421
|
refresh: Optional[int] = None,
|
|
1408
1422
|
time_fmt: str = "%Y%m%d%H%M%S",
|
|
1409
1423
|
timeout: Optional[float] = None,
|
|
1410
|
-
*,
|
|
1411
1424
|
output_samples: Optional[int] = None,
|
|
1425
|
+
save_latent_dynamics: Optional[bool] = None,
|
|
1412
1426
|
) -> CmdStanVB:
|
|
1413
1427
|
"""
|
|
1414
1428
|
Run CmdStan's variational inference algorithm to approximate
|
|
@@ -1520,6 +1534,13 @@ class CmdStanModel:
|
|
|
1520
1534
|
|
|
1521
1535
|
draws = output_samples
|
|
1522
1536
|
|
|
1537
|
+
if save_latent_dynamics is not None:
|
|
1538
|
+
get_logger().warning(
|
|
1539
|
+
"Argument name `save_latent_dynamics` is deprecated, please "
|
|
1540
|
+
"rename to `save_diagnostics`."
|
|
1541
|
+
)
|
|
1542
|
+
save_diagnostics = save_latent_dynamics # type: ignore
|
|
1543
|
+
|
|
1523
1544
|
variational_args = VariationalArgs(
|
|
1524
1545
|
algorithm=algorithm,
|
|
1525
1546
|
iter=iter,
|
|
@@ -1546,7 +1567,7 @@ class CmdStanModel:
|
|
|
1546
1567
|
inits=_inits,
|
|
1547
1568
|
output_dir=output_dir,
|
|
1548
1569
|
sig_figs=sig_figs,
|
|
1549
|
-
save_latent_dynamics=
|
|
1570
|
+
save_latent_dynamics=save_diagnostics,
|
|
1550
1571
|
save_profile=save_profile,
|
|
1551
1572
|
method_args=variational_args,
|
|
1552
1573
|
refresh=refresh,
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
Container for the result of running a laplace approximation.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from collections.abc import Hashable
|
|
6
|
+
from typing import Any, MutableMapping, Optional, Union
|
|
6
7
|
|
|
7
8
|
import numpy as np
|
|
8
9
|
import pandas as pd
|
|
@@ -4,8 +4,9 @@ Container for the result of running the sample (MCMC) method
|
|
|
4
4
|
|
|
5
5
|
import math
|
|
6
6
|
import os
|
|
7
|
+
from collections.abc import Hashable
|
|
7
8
|
from io import StringIO
|
|
8
|
-
from typing import Any,
|
|
9
|
+
from typing import Any, MutableMapping, Optional, Sequence, Union
|
|
9
10
|
|
|
10
11
|
import numpy as np
|
|
11
12
|
import pandas as pd
|
|
@@ -162,23 +162,29 @@ class RunSet:
|
|
|
162
162
|
return self._args.compose_command(
|
|
163
163
|
idx,
|
|
164
164
|
csv_file=self.csv_files[idx],
|
|
165
|
-
diagnostic_file=
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
165
|
+
diagnostic_file=(
|
|
166
|
+
self.diagnostic_files[idx]
|
|
167
|
+
if self._args.save_latent_dynamics
|
|
168
|
+
else None
|
|
169
|
+
),
|
|
170
|
+
profile_file=(
|
|
171
|
+
self.profile_files[idx] if self._args.save_profile else None
|
|
172
|
+
),
|
|
171
173
|
)
|
|
172
174
|
else:
|
|
173
175
|
return self._args.compose_command(
|
|
174
176
|
idx,
|
|
175
177
|
csv_file=self.file_path('.csv'),
|
|
176
|
-
diagnostic_file=
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
178
|
+
diagnostic_file=(
|
|
179
|
+
self.file_path(".csv", extra="-diagnostic")
|
|
180
|
+
if self._args.save_latent_dynamics
|
|
181
|
+
else None
|
|
182
|
+
),
|
|
183
|
+
profile_file=(
|
|
184
|
+
self.file_path(".csv", extra="-profile")
|
|
185
|
+
if self._args.save_profile
|
|
186
|
+
else None
|
|
187
|
+
),
|
|
182
188
|
)
|
|
183
189
|
|
|
184
190
|
@property
|
|
@@ -42,7 +42,7 @@ def show_versions(output: bool = True) -> str:
|
|
|
42
42
|
|
|
43
43
|
deps_info = []
|
|
44
44
|
try:
|
|
45
|
-
|
|
45
|
+
sysname, _, release, _, machine, processor = platform.uname()
|
|
46
46
|
deps_info.extend(
|
|
47
47
|
[
|
|
48
48
|
("python", sys.version),
|
|
@@ -57,15 +57,13 @@ def show_versions(output: bool = True) -> str:
|
|
|
57
57
|
("LOCALE", f"{locale.getlocale()}"),
|
|
58
58
|
]
|
|
59
59
|
)
|
|
60
|
-
# pylint: disable=broad-
|
|
61
|
-
except Exception:
|
|
60
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
62
61
|
pass
|
|
63
62
|
|
|
64
63
|
try:
|
|
65
64
|
deps_info.append(('cmdstan_folder', cmdstan_path()))
|
|
66
65
|
deps_info.append(('cmdstan', str(cmdstan_version())))
|
|
67
|
-
# pylint: disable=broad-
|
|
68
|
-
except Exception:
|
|
66
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
69
67
|
deps_info.append(('cmdstan', 'NOT FOUND'))
|
|
70
68
|
|
|
71
69
|
deps = ['cmdstanpy', 'pandas', 'xarray', 'tqdm', 'numpy']
|
|
@@ -14,6 +14,7 @@ from tqdm.auto import tqdm
|
|
|
14
14
|
from cmdstanpy import _DOT_CMDSTAN
|
|
15
15
|
|
|
16
16
|
from .. import progress as progbar
|
|
17
|
+
from .deprecation import deprecate_positional_args
|
|
17
18
|
from .logging import get_logger
|
|
18
19
|
|
|
19
20
|
EXTENSION = '.exe' if platform.system() == 'Windows' else ''
|
|
@@ -433,15 +434,16 @@ def cxx_toolchain_path(
|
|
|
433
434
|
return compiler_path, tool_path
|
|
434
435
|
|
|
435
436
|
|
|
437
|
+
@deprecate_positional_args(last_deprecated="cores")
|
|
436
438
|
def install_cmdstan(
|
|
437
439
|
version: Optional[str] = None,
|
|
440
|
+
*,
|
|
438
441
|
dir: Optional[str] = None,
|
|
439
442
|
overwrite: bool = False,
|
|
440
443
|
compiler: bool = False,
|
|
441
444
|
progress: bool = False,
|
|
442
445
|
verbose: bool = False,
|
|
443
446
|
cores: int = 1,
|
|
444
|
-
*,
|
|
445
447
|
interactive: bool = False,
|
|
446
448
|
) -> bool:
|
|
447
449
|
"""
|
|
@@ -523,8 +525,7 @@ def install_cmdstan(
|
|
|
523
525
|
cores=cores,
|
|
524
526
|
)
|
|
525
527
|
run_install(args)
|
|
526
|
-
# pylint: disable=broad-
|
|
527
|
-
except Exception as e:
|
|
528
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
528
529
|
logger.warning('CmdStan installation failed.\n%s', str(e))
|
|
529
530
|
return False
|
|
530
531
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Temporary helpers for deprecations in the 1.x series.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
import inspect
|
|
7
|
+
from typing import Any, Callable, TypeVar
|
|
8
|
+
|
|
9
|
+
from .logging import get_logger
|
|
10
|
+
|
|
11
|
+
F = TypeVar('F', bound=Callable[..., Any])
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def deprecate_positional_args(last_deprecated: str) -> Callable[[F], F]:
|
|
15
|
+
"""
|
|
16
|
+
Allow keyword-only arguments up to and including ``last_deprecated``
|
|
17
|
+
to still be passed by position, logging a deprecation warning.
|
|
18
|
+
|
|
19
|
+
Positional use of these arguments becomes an error in CmdStanPy 2.0.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def decorator(func: F) -> F:
|
|
23
|
+
params = inspect.signature(func).parameters.values()
|
|
24
|
+
n_positional = sum(
|
|
25
|
+
p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD for p in params
|
|
26
|
+
)
|
|
27
|
+
kw_only = [
|
|
28
|
+
p.name for p in params if p.kind == inspect.Parameter.KEYWORD_ONLY
|
|
29
|
+
]
|
|
30
|
+
deprecated = kw_only[: kw_only.index(last_deprecated) + 1]
|
|
31
|
+
|
|
32
|
+
@functools.wraps(func)
|
|
33
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
34
|
+
extra = args[n_positional:]
|
|
35
|
+
if not extra or len(extra) > len(deprecated):
|
|
36
|
+
# either nothing to do, or let Python raise its usual error
|
|
37
|
+
return func(*args, **kwargs)
|
|
38
|
+
names = deprecated[: len(extra)]
|
|
39
|
+
for name in names:
|
|
40
|
+
if name in kwargs:
|
|
41
|
+
raise TypeError(
|
|
42
|
+
f"{func.__qualname__}() got multiple values for "
|
|
43
|
+
f"argument '{name}'"
|
|
44
|
+
)
|
|
45
|
+
quoted = ", ".join("`" + name + "`" for name in names)
|
|
46
|
+
get_logger().warning(
|
|
47
|
+
"Passing %s to %s() by position is deprecated and will raise "
|
|
48
|
+
"an error in CmdStanPy 2.0. Please pass them as keyword "
|
|
49
|
+
"arguments instead.",
|
|
50
|
+
quoted,
|
|
51
|
+
func.__qualname__,
|
|
52
|
+
)
|
|
53
|
+
kwargs.update(zip(names, extra))
|
|
54
|
+
return func(*args[:n_positional], **kwargs)
|
|
55
|
+
|
|
56
|
+
return wrapper # type: ignore[return-value]
|
|
57
|
+
|
|
58
|
+
return decorator
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cmdstanpy
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: Python interface to CmdStan
|
|
5
5
|
Author: Stan Dev Team
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -34,8 +34,8 @@ Requires-Dist: mypy; extra == "test"
|
|
|
34
34
|
Requires-Dist: xarray; extra == "test"
|
|
35
35
|
Requires-Dist: polars>=1.8.2; extra == "test"
|
|
36
36
|
Provides-Extra: docs
|
|
37
|
-
Requires-Dist: sphinx<
|
|
38
|
-
Requires-Dist: pydata-sphinx-theme<0.
|
|
37
|
+
Requires-Dist: sphinx<9; extra == "docs"
|
|
38
|
+
Requires-Dist: pydata-sphinx-theme<0.17; extra == "docs"
|
|
39
39
|
Requires-Dist: nbsphinx; extra == "docs"
|
|
40
40
|
Requires-Dist: ipython; extra == "docs"
|
|
41
41
|
Requires-Dist: ipykernel<7; extra == "docs"
|
|
@@ -30,6 +30,7 @@ cmdstanpy/utils/__init__.py
|
|
|
30
30
|
cmdstanpy/utils/cmdstan.py
|
|
31
31
|
cmdstanpy/utils/command.py
|
|
32
32
|
cmdstanpy/utils/data_munging.py
|
|
33
|
+
cmdstanpy/utils/deprecation.py
|
|
33
34
|
cmdstanpy/utils/filesystem.py
|
|
34
35
|
cmdstanpy/utils/json.py
|
|
35
36
|
cmdstanpy/utils/logging.py
|
|
@@ -38,6 +39,7 @@ test/test_cmdstan_args.py
|
|
|
38
39
|
test/test_compiler_opts.py
|
|
39
40
|
test/test_compliance.py
|
|
40
41
|
test/test_cxx_installation.py
|
|
42
|
+
test/test_deprecations.py
|
|
41
43
|
test/test_generate_quantities.py
|
|
42
44
|
test/test_install_cmdstan.py
|
|
43
45
|
test/test_laplace.py
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Tests for deprecated positional arguments to public functions"""
|
|
2
|
+
|
|
3
|
+
# positional calls below are deliberate
|
|
4
|
+
# pylint: disable=too-many-function-args
|
|
5
|
+
|
|
6
|
+
import inspect
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
import sys
|
|
11
|
+
from test import check_present
|
|
12
|
+
from typing import Any, Callable
|
|
13
|
+
from unittest import mock
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
from cmdstanpy.model import CmdStanModel
|
|
18
|
+
from cmdstanpy.utils import install_cmdstan
|
|
19
|
+
from cmdstanpy.utils.deprecation import deprecate_positional_args
|
|
20
|
+
|
|
21
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
22
|
+
DATAFILES_PATH = os.path.join(HERE, 'data')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Dummy:
|
|
26
|
+
@deprecate_positional_args(last_deprecated="c")
|
|
27
|
+
def method(
|
|
28
|
+
self, a: Any = None, *, b: Any = None, c: Any = None, d: Any = None
|
|
29
|
+
) -> tuple[Any, Any, Any, Any]:
|
|
30
|
+
return a, b, c, d
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_keywords_do_not_warn(caplog: pytest.LogCaptureFixture) -> None:
|
|
34
|
+
with caplog.at_level(logging.WARNING):
|
|
35
|
+
assert Dummy().method(1, b=2, c=3, d=4) == (1, 2, 3, 4)
|
|
36
|
+
assert Dummy().method() == (None, None, None, None)
|
|
37
|
+
assert not caplog.records
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_positional_maps_to_keywords(caplog: pytest.LogCaptureFixture) -> None:
|
|
41
|
+
with caplog.at_level(logging.WARNING):
|
|
42
|
+
assert Dummy().method(1, 2, 3, d=4) == (1, 2, 3, 4)
|
|
43
|
+
check_present(
|
|
44
|
+
caplog,
|
|
45
|
+
(
|
|
46
|
+
'cmdstanpy',
|
|
47
|
+
'WARNING',
|
|
48
|
+
re.compile(r"Passing `b`, `c` to Dummy\.method\(\) by position"),
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_positional_and_keyword_collision() -> None:
|
|
54
|
+
with pytest.raises(TypeError, match="multiple values for argument 'b'"):
|
|
55
|
+
Dummy().method(1, 2, b=3)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_too_many_positional() -> None:
|
|
59
|
+
# `d` was never positional, so it must not be filled by position
|
|
60
|
+
with pytest.raises(TypeError):
|
|
61
|
+
Dummy().method(1, 2, 3, 4)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_signature_is_keyword_only() -> None:
|
|
65
|
+
params = inspect.signature(Dummy.method).parameters
|
|
66
|
+
assert params['a'].kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
|
|
67
|
+
assert params['b'].kind == inspect.Parameter.KEYWORD_ONLY
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@pytest.mark.parametrize(
|
|
71
|
+
'func, positional, last_deprecated',
|
|
72
|
+
[
|
|
73
|
+
(CmdStanModel.sample, ['self', 'data'], 'timeout'),
|
|
74
|
+
(CmdStanModel.optimize, ['self', 'data'], 'jacobian'),
|
|
75
|
+
(CmdStanModel.variational, ['self', 'data'], 'timeout'),
|
|
76
|
+
(
|
|
77
|
+
CmdStanModel.generate_quantities,
|
|
78
|
+
['self', 'data', 'previous_fit'],
|
|
79
|
+
'timeout',
|
|
80
|
+
),
|
|
81
|
+
(install_cmdstan, ['version'], 'cores'),
|
|
82
|
+
],
|
|
83
|
+
)
|
|
84
|
+
def test_public_signatures(
|
|
85
|
+
func: Callable[..., Any], positional: list[str], last_deprecated: str
|
|
86
|
+
) -> None:
|
|
87
|
+
params = inspect.signature(func).parameters
|
|
88
|
+
kinds = {name: p.kind for name, p in params.items()}
|
|
89
|
+
assert [
|
|
90
|
+
n
|
|
91
|
+
for n, k in kinds.items()
|
|
92
|
+
if k == inspect.Parameter.POSITIONAL_OR_KEYWORD
|
|
93
|
+
] == positional
|
|
94
|
+
# guards against a rename silently shifting the positional mapping
|
|
95
|
+
assert kinds[last_deprecated] == inspect.Parameter.KEYWORD_ONLY
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_optimize_positional_seed(caplog: pytest.LogCaptureFixture) -> None:
|
|
99
|
+
stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan')
|
|
100
|
+
jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json')
|
|
101
|
+
model = CmdStanModel(stan_file=stan)
|
|
102
|
+
with caplog.at_level(logging.WARNING):
|
|
103
|
+
mle_pos = model.optimize(jdata, 1234)
|
|
104
|
+
check_present(
|
|
105
|
+
caplog,
|
|
106
|
+
(
|
|
107
|
+
'cmdstanpy',
|
|
108
|
+
'WARNING',
|
|
109
|
+
re.compile(r"Passing `seed` to CmdStanModel\.optimize\(\)"),
|
|
110
|
+
),
|
|
111
|
+
)
|
|
112
|
+
mle_kw = model.optimize(jdata, seed=1234)
|
|
113
|
+
assert mle_pos.metadata.cmdstan_config['seed'] == 1234
|
|
114
|
+
assert mle_pos.optimized_params_dict == mle_kw.optimized_params_dict
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@pytest.mark.skipif(
|
|
118
|
+
sys.version_info < (3, 11),
|
|
119
|
+
reason="mock.patch resolves `cmdstanpy.install_cmdstan` to the "
|
|
120
|
+
"re-exported function, not the module, before Python 3.11",
|
|
121
|
+
)
|
|
122
|
+
def test_install_cmdstan_positional(caplog: pytest.LogCaptureFixture) -> None:
|
|
123
|
+
# stop right after argument handling; nothing is downloaded
|
|
124
|
+
with mock.patch(
|
|
125
|
+
'cmdstanpy.install_cmdstan.run_install', side_effect=RuntimeError
|
|
126
|
+
) as run_install:
|
|
127
|
+
with caplog.at_level(logging.WARNING):
|
|
128
|
+
install_cmdstan("2.36.0", "some_dir", True)
|
|
129
|
+
check_present(
|
|
130
|
+
caplog,
|
|
131
|
+
(
|
|
132
|
+
'cmdstanpy',
|
|
133
|
+
'WARNING',
|
|
134
|
+
re.compile(r"Passing `dir`, `overwrite` to install_cmdstan\(\)"),
|
|
135
|
+
),
|
|
136
|
+
)
|
|
137
|
+
settings = run_install.call_args.args[0]
|
|
138
|
+
assert settings.version == "2.36.0"
|
|
139
|
+
assert settings.dir == "some_dir"
|
|
140
|
+
assert settings.overwrite
|
|
@@ -565,7 +565,7 @@ def test_from_optimization() -> None:
|
|
|
565
565
|
assert os.path.exists(csv_file)
|
|
566
566
|
|
|
567
567
|
assert bern_gqs.draws().shape == (1, 1, 10)
|
|
568
|
-
assert bern_gqs.draws(inc_sample=True).shape == (1, 1,
|
|
568
|
+
assert bern_gqs.draws(inc_sample=True).shape == (1, 1, 13)
|
|
569
569
|
|
|
570
570
|
# draws_pd()
|
|
571
571
|
assert bern_gqs.draws_pd().shape == (1, 13)
|
|
@@ -610,18 +610,18 @@ def test_opt_save_iterations(caplog: pytest.LogCaptureFixture):
|
|
|
610
610
|
assert bern_gqs.draws(inc_warmup=True, inc_sample=True).shape == (
|
|
611
611
|
iters,
|
|
612
612
|
1,
|
|
613
|
-
|
|
613
|
+
13,
|
|
614
614
|
)
|
|
615
615
|
|
|
616
616
|
assert bern_gqs.draws(concat_chains=True).shape == (1, 10)
|
|
617
|
-
assert bern_gqs.draws(concat_chains=True, inc_sample=True).shape == (1,
|
|
617
|
+
assert bern_gqs.draws(concat_chains=True, inc_sample=True).shape == (1, 13)
|
|
618
618
|
assert bern_gqs.draws(concat_chains=True, inc_warmup=True).shape == (
|
|
619
619
|
iters,
|
|
620
620
|
10,
|
|
621
621
|
)
|
|
622
622
|
assert bern_gqs.draws(
|
|
623
623
|
concat_chains=True, inc_warmup=True, inc_sample=True
|
|
624
|
-
).shape == (iters,
|
|
624
|
+
).shape == (iters, 13)
|
|
625
625
|
|
|
626
626
|
# stan_variable
|
|
627
627
|
theta = bern_gqs.stan_variable(var='theta')
|
|
@@ -92,13 +92,13 @@ def test_rosenbrock(caplog: pytest.LogCaptureFixture) -> None:
|
|
|
92
92
|
assert 'CmdStanMLE: model=rosenbrock' in repr(mle)
|
|
93
93
|
assert 'method=optimize' in repr(mle)
|
|
94
94
|
assert mle.converged
|
|
95
|
-
assert mle.column_names == ('lp__', 'x', 'y')
|
|
95
|
+
assert mle.column_names == ('lp__', 'converged__', 'x', 'y')
|
|
96
96
|
np.testing.assert_almost_equal(mle.stan_variable('x'), 1, decimal=3)
|
|
97
97
|
np.testing.assert_almost_equal(mle.stan_variable('y'), 1, decimal=3)
|
|
98
98
|
np.testing.assert_almost_equal(
|
|
99
99
|
mle.optimized_params_pd['x'][0], 1, decimal=3
|
|
100
100
|
)
|
|
101
|
-
np.testing.assert_almost_equal(mle.optimized_params_np[
|
|
101
|
+
np.testing.assert_almost_equal(mle.optimized_params_np[2], 1, decimal=3)
|
|
102
102
|
np.testing.assert_almost_equal(mle.optimized_params_dict['x'], 1, decimal=3)
|
|
103
103
|
with caplog.at_level(logging.WARNING):
|
|
104
104
|
assert mle.optimized_iterations_np is None
|
|
@@ -128,8 +128,8 @@ def test_rosenbrock(caplog: pytest.LogCaptureFixture) -> None:
|
|
|
128
128
|
np.testing.assert_almost_equal(mle.stan_variable('x'), 1, decimal=3)
|
|
129
129
|
np.testing.assert_almost_equal(mle.stan_variable('y'), 1, decimal=3)
|
|
130
130
|
|
|
131
|
-
assert mle.optimized_params_np.shape == (
|
|
132
|
-
np.testing.assert_almost_equal(mle.optimized_params_np[
|
|
131
|
+
assert mle.optimized_params_np.shape == (4,)
|
|
132
|
+
np.testing.assert_almost_equal(mle.optimized_params_np[2], 1, decimal=3)
|
|
133
133
|
np.testing.assert_almost_equal(
|
|
134
134
|
mle.optimized_params_pd['x'][0], 1, decimal=3
|
|
135
135
|
)
|
|
@@ -140,7 +140,7 @@ def test_rosenbrock(caplog: pytest.LogCaptureFixture) -> None:
|
|
|
140
140
|
mle.optimized_iterations_np[0, 1]
|
|
141
141
|
!= mle.optimized_iterations_np[last_iter, 1]
|
|
142
142
|
)
|
|
143
|
-
for i in range(
|
|
143
|
+
for i in range(4):
|
|
144
144
|
assert (
|
|
145
145
|
mle.optimized_params_np[i]
|
|
146
146
|
== mle.optimized_iterations_np[last_iter, i]
|
|
@@ -159,7 +159,7 @@ def test_eight_schools(caplog: pytest.LogCaptureFixture) -> None:
|
|
|
159
159
|
assert 'method=optimize' in repr(mle)
|
|
160
160
|
assert not mle.converged
|
|
161
161
|
with caplog.at_level(logging.WARNING):
|
|
162
|
-
assert mle.optimized_params_pd.shape == (1,
|
|
162
|
+
assert mle.optimized_params_pd.shape == (1, 12)
|
|
163
163
|
check_present(
|
|
164
164
|
caplog,
|
|
165
165
|
(
|
|
@@ -294,15 +294,15 @@ def test_optimize_good() -> None:
|
|
|
294
294
|
# test numpy output
|
|
295
295
|
assert isinstance(mle.optimized_params_np, np.ndarray)
|
|
296
296
|
np.testing.assert_almost_equal(mle.optimized_params_np[0], -5, decimal=2)
|
|
297
|
-
np.testing.assert_almost_equal(mle.optimized_params_np[
|
|
297
|
+
np.testing.assert_almost_equal(mle.optimized_params_np[2], 0.2, decimal=3)
|
|
298
298
|
|
|
299
299
|
# test pandas output
|
|
300
300
|
assert mle.optimized_params_np[0] == mle.optimized_params_pd['lp__'][0]
|
|
301
|
-
assert mle.optimized_params_np[
|
|
301
|
+
assert mle.optimized_params_np[2] == mle.optimized_params_pd['theta'][0]
|
|
302
302
|
|
|
303
303
|
# test dict output
|
|
304
304
|
assert mle.optimized_params_np[0] == mle.optimized_params_dict['lp__']
|
|
305
|
-
assert mle.optimized_params_np[
|
|
305
|
+
assert mle.optimized_params_np[2] == mle.optimized_params_dict['theta']
|
|
306
306
|
|
|
307
307
|
|
|
308
308
|
def test_negative_parameter_values() -> None:
|
|
@@ -530,14 +530,14 @@ def test_optimize_good_dict() -> None:
|
|
|
530
530
|
)
|
|
531
531
|
# test numpy output
|
|
532
532
|
np.testing.assert_almost_equal(mle.optimized_params_np[0], -5, decimal=2)
|
|
533
|
-
np.testing.assert_almost_equal(mle.optimized_params_np[
|
|
533
|
+
np.testing.assert_almost_equal(mle.optimized_params_np[2], 0.2, decimal=3)
|
|
534
534
|
|
|
535
535
|
|
|
536
536
|
def test_optimize_rosenbrock() -> None:
|
|
537
537
|
stan = os.path.join(DATAFILES_PATH, 'optimize', 'rosenbrock.stan')
|
|
538
538
|
rose_model = CmdStanModel(stan_file=stan)
|
|
539
539
|
mle = rose_model.optimize(seed=1239812093, inits=None, algorithm='BFGS')
|
|
540
|
-
assert mle.column_names == ('lp__', 'x', 'y')
|
|
540
|
+
assert mle.column_names == ('lp__', 'converged__', 'x', 'y')
|
|
541
541
|
np.testing.assert_almost_equal(mle.optimized_params_dict['x'], 1, decimal=3)
|
|
542
542
|
np.testing.assert_almost_equal(mle.optimized_params_dict['y'], 1, decimal=3)
|
|
543
543
|
|
|
@@ -546,7 +546,7 @@ def test_optimize_no_data() -> None:
|
|
|
546
546
|
stan = os.path.join(DATAFILES_PATH, 'optimize', 'no_data.stan')
|
|
547
547
|
rose_model = CmdStanModel(stan_file=stan)
|
|
548
548
|
mle = rose_model.optimize(seed=1239812093)
|
|
549
|
-
assert mle.column_names == ('lp__', 'a')
|
|
549
|
+
assert mle.column_names == ('lp__', 'converged__', 'a')
|
|
550
550
|
np.testing.assert_almost_equal(mle.optimized_params_dict['a'], 0, decimal=3)
|
|
551
551
|
|
|
552
552
|
|
|
@@ -600,7 +600,7 @@ def test_exe_only() -> None:
|
|
|
600
600
|
jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json')
|
|
601
601
|
mle = bern2_model.optimize(data=jdata)
|
|
602
602
|
assert mle.optimized_params_np[0] == mle.optimized_params_dict['lp__']
|
|
603
|
-
assert mle.optimized_params_np[
|
|
603
|
+
assert mle.optimized_params_np[2] == mle.optimized_params_dict['theta']
|
|
604
604
|
|
|
605
605
|
|
|
606
606
|
def test_complex_output() -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|