cmdstanpy 1.3.1__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.1/cmdstanpy.egg-info → cmdstanpy-1.3.2}/PKG-INFO +1 -1
- cmdstanpy-1.3.2/cmdstanpy/_version.py +3 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/cmdstan_args.py +7 -3
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/compilation.py +82 -19
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/model.py +211 -47
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2/cmdstanpy.egg-info}/PKG-INFO +1 -1
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_compiler_opts.py +106 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_model.py +144 -69
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_sample.py +5 -3
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_utils.py +1 -0
- cmdstanpy-1.3.1/cmdstanpy/_version.py +0 -3
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/LICENSE.md +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/MANIFEST.in +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/README.md +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/__init__.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/install_cmdstan.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/install_cxx_toolchain.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/progress.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/py.typed +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/__init__.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/gq.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/laplace.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/mcmc.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/metadata.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/mle.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/pathfinder.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/runset.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/stanfit/vb.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/utils/__init__.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/utils/cmdstan.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/utils/command.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/utils/data_munging.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/utils/deprecation.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/utils/filesystem.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/utils/json.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/utils/logging.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy/utils/stancsv.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy.egg-info/SOURCES.txt +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy.egg-info/dependency_links.txt +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy.egg-info/entry_points.txt +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy.egg-info/requires.txt +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/cmdstanpy.egg-info/top_level.txt +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/pyproject.toml +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/setup.cfg +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_cmdstan_args.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_compliance.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_cxx_installation.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_deprecations.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_generate_quantities.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_install_cmdstan.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_laplace.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_log_prob.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_logging.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_metadata.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_optimize.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_pathfinder.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_runset.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_stancsv.py +0 -0
- {cmdstanpy-1.3.1 → cmdstanpy-1.3.2}/test/test_variational.py +0 -0
|
@@ -632,7 +632,7 @@ class CmdStanArgs:
|
|
|
632
632
|
def __init__(
|
|
633
633
|
self,
|
|
634
634
|
model_name: str,
|
|
635
|
-
model_exe:
|
|
635
|
+
model_exe: OptionalPath,
|
|
636
636
|
chain_ids: Optional[list[int]],
|
|
637
637
|
method_args: Union[
|
|
638
638
|
SamplerArgs,
|
|
@@ -692,6 +692,10 @@ class CmdStanArgs:
|
|
|
692
692
|
* if no seed specified, set random seed.
|
|
693
693
|
* length of per-chain lists equals specified # of chains
|
|
694
694
|
"""
|
|
695
|
+
if self.model_name is None:
|
|
696
|
+
raise ValueError('no stan model specified')
|
|
697
|
+
if self.model_exe is None:
|
|
698
|
+
raise ValueError('model not compiled')
|
|
695
699
|
|
|
696
700
|
if self.chain_ids is not None:
|
|
697
701
|
for chain_id in self.chain_ids:
|
|
@@ -853,10 +857,10 @@ class CmdStanArgs:
|
|
|
853
857
|
idx, len(self.chain_ids)
|
|
854
858
|
)
|
|
855
859
|
)
|
|
856
|
-
cmd.append(self.model_exe)
|
|
860
|
+
cmd.append(self.model_exe) # type: ignore # guaranteed by validate
|
|
857
861
|
cmd.append(f'id={self.chain_ids[idx]}')
|
|
858
862
|
else:
|
|
859
|
-
cmd.append(self.model_exe)
|
|
863
|
+
cmd.append(self.model_exe) # type: ignore # guaranteed by validate
|
|
860
864
|
|
|
861
865
|
if self.seed is not None:
|
|
862
866
|
if not isinstance(self.seed, list):
|
|
@@ -8,6 +8,7 @@ import os
|
|
|
8
8
|
import platform
|
|
9
9
|
import shutil
|
|
10
10
|
import subprocess
|
|
11
|
+
from copy import copy
|
|
11
12
|
from datetime import datetime
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
from typing import Any, Iterable, Optional, Union
|
|
@@ -36,6 +37,12 @@ STANC_OPTS = [
|
|
|
36
37
|
'warn-pedantic',
|
|
37
38
|
]
|
|
38
39
|
|
|
40
|
+
# TODO(2.0): remove
|
|
41
|
+
STANC_DEPRECATED_OPTS = {
|
|
42
|
+
'allow_undefined': 'allow-undefined',
|
|
43
|
+
'include_paths': 'include-paths',
|
|
44
|
+
}
|
|
45
|
+
|
|
39
46
|
STANC_IGNORE_OPTS = [
|
|
40
47
|
'debug-lex',
|
|
41
48
|
'debug-parse',
|
|
@@ -60,6 +67,7 @@ STANC_IGNORE_OPTS = [
|
|
|
60
67
|
OptionalPath = Union[str, os.PathLike, None]
|
|
61
68
|
|
|
62
69
|
|
|
70
|
+
# TODO(2.0): can remove add function and other logic
|
|
63
71
|
class CompilerOptions:
|
|
64
72
|
"""
|
|
65
73
|
User-specified flags for stanc and C++ compiler.
|
|
@@ -87,6 +95,26 @@ class CompilerOptions:
|
|
|
87
95
|
self._stanc_options, self._cpp_options
|
|
88
96
|
)
|
|
89
97
|
|
|
98
|
+
def __eq__(self, other: Any) -> bool:
|
|
99
|
+
"""Overrides the default implementation"""
|
|
100
|
+
if self.is_empty() and other is None: # equiv w/r/t compiler
|
|
101
|
+
return True
|
|
102
|
+
if not isinstance(other, CompilerOptions):
|
|
103
|
+
return False
|
|
104
|
+
return (
|
|
105
|
+
self._stanc_options == other.stanc_options
|
|
106
|
+
and self._cpp_options == other.cpp_options
|
|
107
|
+
and self._user_header == other.user_header
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def is_empty(self) -> bool:
|
|
111
|
+
"""True if no options specified."""
|
|
112
|
+
return (
|
|
113
|
+
self._stanc_options == {}
|
|
114
|
+
and self._cpp_options == {}
|
|
115
|
+
and self._user_header == ''
|
|
116
|
+
)
|
|
117
|
+
|
|
90
118
|
@property
|
|
91
119
|
def stanc_options(self) -> dict[str, Union[bool, int, str, Iterable[str]]]:
|
|
92
120
|
"""Stanc compiler options."""
|
|
@@ -116,12 +144,31 @@ class CompilerOptions:
|
|
|
116
144
|
Check stanc compiler args and consistency between stanc and C++ options.
|
|
117
145
|
Raise ValueError if bad config is found.
|
|
118
146
|
"""
|
|
147
|
+
# pylint: disable=no-member
|
|
119
148
|
if self._stanc_options is None:
|
|
120
149
|
return
|
|
121
150
|
ignore = []
|
|
122
151
|
paths = None
|
|
123
152
|
has_o_flag = False
|
|
124
153
|
|
|
154
|
+
for deprecated, replacement in STANC_DEPRECATED_OPTS.items():
|
|
155
|
+
if deprecated in self._stanc_options:
|
|
156
|
+
if replacement:
|
|
157
|
+
get_logger().warning(
|
|
158
|
+
'compiler option "%s" is deprecated, use "%s" instead',
|
|
159
|
+
deprecated,
|
|
160
|
+
replacement,
|
|
161
|
+
)
|
|
162
|
+
self._stanc_options[replacement] = copy(
|
|
163
|
+
self._stanc_options[deprecated]
|
|
164
|
+
)
|
|
165
|
+
del self._stanc_options[deprecated]
|
|
166
|
+
else:
|
|
167
|
+
get_logger().warning(
|
|
168
|
+
'compiler option "%s" is deprecated and should '
|
|
169
|
+
'not be used',
|
|
170
|
+
deprecated,
|
|
171
|
+
)
|
|
125
172
|
for key, val in self._stanc_options.items():
|
|
126
173
|
if key in STANC_IGNORE_OPTS:
|
|
127
174
|
get_logger().info('ignoring compiler option: %s', key)
|
|
@@ -220,6 +267,37 @@ class CompilerOptions:
|
|
|
220
267
|
|
|
221
268
|
self._cpp_options['USER_HEADER'] = self._user_header
|
|
222
269
|
|
|
270
|
+
def add(self, new_opts: "CompilerOptions") -> None: # noqa: disable=Q000
|
|
271
|
+
"""Adds options to existing set of compiler options."""
|
|
272
|
+
if new_opts.stanc_options is not None:
|
|
273
|
+
if self._stanc_options is None:
|
|
274
|
+
self._stanc_options = new_opts.stanc_options
|
|
275
|
+
else:
|
|
276
|
+
for key, val in new_opts.stanc_options.items():
|
|
277
|
+
if key == 'include-paths':
|
|
278
|
+
if isinstance(val, Iterable) and not isinstance(
|
|
279
|
+
val, str
|
|
280
|
+
):
|
|
281
|
+
for path in val:
|
|
282
|
+
self.add_include_path(str(path))
|
|
283
|
+
else:
|
|
284
|
+
self.add_include_path(str(val))
|
|
285
|
+
else:
|
|
286
|
+
self._stanc_options[key] = val
|
|
287
|
+
if new_opts.cpp_options is not None:
|
|
288
|
+
for key, val in new_opts.cpp_options.items():
|
|
289
|
+
self._cpp_options[key] = val
|
|
290
|
+
if new_opts._user_header != '' and self._user_header == '':
|
|
291
|
+
self._user_header = new_opts._user_header
|
|
292
|
+
|
|
293
|
+
def add_include_path(self, path: str) -> None:
|
|
294
|
+
"""Adds include path to existing set of compiler options."""
|
|
295
|
+
path = os.path.abspath(os.path.expanduser(path))
|
|
296
|
+
if 'include-paths' not in self._stanc_options:
|
|
297
|
+
self._stanc_options['include-paths'] = [path]
|
|
298
|
+
elif path not in self._stanc_options['include-paths']:
|
|
299
|
+
self._stanc_options['include-paths'].append(path)
|
|
300
|
+
|
|
223
301
|
def compose_stanc(self, filename_in_msg: Optional[str]) -> list[str]:
|
|
224
302
|
opts = []
|
|
225
303
|
|
|
@@ -265,8 +343,7 @@ class CompilerOptions:
|
|
|
265
343
|
|
|
266
344
|
|
|
267
345
|
def src_info(
|
|
268
|
-
stan_file: str,
|
|
269
|
-
stanc_options: Optional[dict[str, Any]] = None,
|
|
346
|
+
stan_file: str, compiler_options: CompilerOptions
|
|
270
347
|
) -> dict[str, Any]:
|
|
271
348
|
"""
|
|
272
349
|
Get source info for Stan program file.
|
|
@@ -277,7 +354,7 @@ def src_info(
|
|
|
277
354
|
cmd = (
|
|
278
355
|
[stanc_path()]
|
|
279
356
|
# handle include-paths, allow-undefined etc
|
|
280
|
-
+
|
|
357
|
+
+ compiler_options.compose_stanc(None)
|
|
281
358
|
+ ['--info', str(stan_file)]
|
|
282
359
|
)
|
|
283
360
|
proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
@@ -330,26 +407,12 @@ def compile_stan_file(
|
|
|
330
407
|
)
|
|
331
408
|
compiler_options.validate()
|
|
332
409
|
|
|
333
|
-
# if program has include directives, record path
|
|
334
|
-
if '#include' in src.read_text():
|
|
335
|
-
path = os.fspath(src.parent.resolve())
|
|
336
|
-
if 'include-paths' not in compiler_options.stanc_options:
|
|
337
|
-
compiler_options.stanc_options['include-paths'] = [path]
|
|
338
|
-
else:
|
|
339
|
-
paths: list[str] = compiler_options.stanc_options[
|
|
340
|
-
'include-paths'
|
|
341
|
-
] # type: ignore
|
|
342
|
-
if path not in paths:
|
|
343
|
-
paths.append(path)
|
|
344
|
-
|
|
345
410
|
exe_target = src.with_suffix(EXTENSION)
|
|
346
411
|
if exe_target.exists():
|
|
347
412
|
exe_time = os.path.getmtime(exe_target)
|
|
348
413
|
included_files = [src]
|
|
349
414
|
included_files.extend(
|
|
350
|
-
src_info(str(src), compiler_options
|
|
351
|
-
'included_files', []
|
|
352
|
-
)
|
|
415
|
+
src_info(str(src), compiler_options).get('included_files', [])
|
|
353
416
|
)
|
|
354
417
|
out_of_date = any(
|
|
355
418
|
os.path.getmtime(included_file) > exe_time
|
|
@@ -419,7 +482,7 @@ def compile_stan_file(
|
|
|
419
482
|
raise ValueError(
|
|
420
483
|
f"Failed to compile Stan model '{src}'. Console:\n{console}"
|
|
421
484
|
)
|
|
422
|
-
return
|
|
485
|
+
return str(exe_target)
|
|
423
486
|
|
|
424
487
|
|
|
425
488
|
def format_stan_file(
|
|
@@ -13,7 +13,16 @@ from collections import OrderedDict
|
|
|
13
13
|
from concurrent.futures import ThreadPoolExecutor
|
|
14
14
|
from io import StringIO
|
|
15
15
|
from multiprocessing import cpu_count
|
|
16
|
-
from typing import
|
|
16
|
+
from typing import (
|
|
17
|
+
Any,
|
|
18
|
+
Callable,
|
|
19
|
+
Iterable,
|
|
20
|
+
Literal,
|
|
21
|
+
Mapping,
|
|
22
|
+
Optional,
|
|
23
|
+
TypeVar,
|
|
24
|
+
Union,
|
|
25
|
+
)
|
|
17
26
|
|
|
18
27
|
import numpy as np
|
|
19
28
|
import pandas as pd
|
|
@@ -69,6 +78,11 @@ class CmdStanModel:
|
|
|
69
78
|
Stan program source file or the compiled executable, or both.
|
|
70
79
|
This will compile the model if provided a Stan file and no executable,
|
|
71
80
|
|
|
81
|
+
:param model_name: Model name, used for output file names.
|
|
82
|
+
Optional, default is the base filename of the Stan program file.
|
|
83
|
+
Deprecated: In version 2.0.0, model name cannot be
|
|
84
|
+
specified and will always be taken from executable.
|
|
85
|
+
|
|
72
86
|
:param stan_file: Path to Stan program file.
|
|
73
87
|
|
|
74
88
|
:param exe_file: Path to compiled executable file. Optional, unless
|
|
@@ -90,20 +104,31 @@ class CmdStanModel:
|
|
|
90
104
|
:param user_header: A path to a header file to include during C++
|
|
91
105
|
compilation.
|
|
92
106
|
Optional.
|
|
107
|
+
|
|
108
|
+
:param compile: Whether or not to compile the model. Default is ``True``.
|
|
109
|
+
If set to the string ``"force"``, it will always compile even if
|
|
110
|
+
an existing executable is found.
|
|
111
|
+
Deprecated: Use ``force_compile`` instead. The ability to instantiate
|
|
112
|
+
a CmdStanModel without an executable will be removed in version 2.0.0.
|
|
113
|
+
|
|
93
114
|
"""
|
|
94
115
|
|
|
95
116
|
def __init__(
|
|
96
117
|
self,
|
|
118
|
+
model_name: Optional[str] = None,
|
|
97
119
|
stan_file: OptionalPath = None,
|
|
98
120
|
exe_file: OptionalPath = None,
|
|
99
121
|
force_compile: bool = False,
|
|
100
122
|
stanc_options: Optional[dict[str, Any]] = None,
|
|
101
123
|
cpp_options: Optional[dict[str, Any]] = None,
|
|
102
124
|
user_header: OptionalPath = None,
|
|
125
|
+
*,
|
|
126
|
+
compile: Union[bool, Literal['force'], None] = None,
|
|
103
127
|
) -> None:
|
|
104
128
|
"""
|
|
105
129
|
Initialize object given constructor args.
|
|
106
130
|
|
|
131
|
+
:param model_name: Deprecated. Model name, used for output file names.
|
|
107
132
|
:param stan_file: Path to Stan program file.
|
|
108
133
|
:param exe_file: Path to compiled executable file.
|
|
109
134
|
:param force_compile: Whether or not to force recompilation if
|
|
@@ -112,19 +137,46 @@ class CmdStanModel:
|
|
|
112
137
|
:param cpp_options: Options for C++ compiler.
|
|
113
138
|
:param user_header: A path to a header file to include during C++
|
|
114
139
|
compilation.
|
|
140
|
+
:param compile: Deprecated. Whether or not to compile the model.
|
|
115
141
|
"""
|
|
116
142
|
self._name = ''
|
|
117
143
|
self._stan_file = None
|
|
118
|
-
self.
|
|
144
|
+
self._exe_file = None
|
|
145
|
+
self._compiler_options = compilation.CompilerOptions(
|
|
146
|
+
stanc_options=stanc_options,
|
|
147
|
+
cpp_options=cpp_options,
|
|
148
|
+
user_header=user_header,
|
|
149
|
+
)
|
|
150
|
+
self._compiler_options.validate()
|
|
119
151
|
|
|
120
152
|
self._fixed_param = False
|
|
121
153
|
|
|
122
|
-
if
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
154
|
+
if compile is None:
|
|
155
|
+
compile = True
|
|
156
|
+
else:
|
|
157
|
+
get_logger().warning(
|
|
158
|
+
"CmdStanModel(compile=...) is deprecated and will be "
|
|
159
|
+
"removed in the next major version. The constructor will "
|
|
160
|
+
"always ensure a model has a compiled executable.\n"
|
|
161
|
+
"If you wish to force recompilation, use force_compile=True "
|
|
162
|
+
"instead."
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
if force_compile:
|
|
166
|
+
compile = 'force'
|
|
167
|
+
|
|
168
|
+
if model_name is not None:
|
|
169
|
+
get_logger().warning(
|
|
170
|
+
"CmdStanModel(model_name=...) is deprecated and will be "
|
|
171
|
+
"removed in the next major version."
|
|
172
|
+
)
|
|
173
|
+
if not model_name.strip():
|
|
174
|
+
raise ValueError(
|
|
175
|
+
'Invalid value for argument model name, found "{}"'.format(
|
|
176
|
+
model_name
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
self._name = model_name.strip()
|
|
128
180
|
|
|
129
181
|
if stan_file is None:
|
|
130
182
|
if exe_file is None:
|
|
@@ -132,11 +184,6 @@ class CmdStanModel:
|
|
|
132
184
|
'Missing model file arguments, you must specify '
|
|
133
185
|
'either Stan source or executable program file or both.'
|
|
134
186
|
)
|
|
135
|
-
if force_compile:
|
|
136
|
-
raise ValueError(
|
|
137
|
-
'Cannot force compilation when no Stan source file '
|
|
138
|
-
'is specified.'
|
|
139
|
-
)
|
|
140
187
|
else:
|
|
141
188
|
self._stan_file = os.path.realpath(os.path.expanduser(stan_file))
|
|
142
189
|
if not os.path.exists(self._stan_file):
|
|
@@ -148,29 +195,41 @@ class CmdStanModel:
|
|
|
148
195
|
)
|
|
149
196
|
if not self._name:
|
|
150
197
|
self._name, _ = os.path.splitext(filename)
|
|
151
|
-
else:
|
|
152
|
-
if self._name != os.path.splitext(filename)[0]:
|
|
153
|
-
raise ValueError(
|
|
154
|
-
'Name mismatch between Stan file and compiled'
|
|
155
|
-
' executable, expecting basename: {}'
|
|
156
|
-
' found: {}.'.format(self._name, filename)
|
|
157
|
-
)
|
|
158
198
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
)
|
|
199
|
+
# if program has include directives, record path
|
|
200
|
+
with open(self._stan_file, 'r') as fd:
|
|
201
|
+
program = fd.read()
|
|
202
|
+
if '#include' in program:
|
|
203
|
+
path, _ = os.path.split(self._stan_file)
|
|
204
|
+
self._compiler_options.add_include_path(path)
|
|
166
205
|
|
|
167
206
|
# try to detect models w/out parameters, needed for sampler
|
|
168
207
|
if (not cmdstan_version_before(2, 27)) and cmdstan_version_before(
|
|
169
208
|
2, 36
|
|
170
209
|
):
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
210
|
+
try:
|
|
211
|
+
model_info = self.src_info()
|
|
212
|
+
if 'parameters' in model_info:
|
|
213
|
+
self._fixed_param |= len(model_info['parameters']) == 0
|
|
214
|
+
except ValueError as e:
|
|
215
|
+
if compile:
|
|
216
|
+
raise
|
|
217
|
+
get_logger().debug(e)
|
|
218
|
+
|
|
219
|
+
if exe_file is not None:
|
|
220
|
+
self._exe_file = os.path.realpath(os.path.expanduser(exe_file))
|
|
221
|
+
if not os.path.exists(self._exe_file):
|
|
222
|
+
raise ValueError('no such file {}'.format(self._exe_file))
|
|
223
|
+
_, exename = os.path.split(self._exe_file)
|
|
224
|
+
if not self._name:
|
|
225
|
+
self._name, _ = os.path.splitext(exename)
|
|
226
|
+
else:
|
|
227
|
+
if self._name != os.path.splitext(exename)[0]:
|
|
228
|
+
raise ValueError(
|
|
229
|
+
'Name mismatch between Stan file and compiled'
|
|
230
|
+
' executable, expecting basename: {}'
|
|
231
|
+
' found: {}.'.format(self._name, exename)
|
|
232
|
+
)
|
|
174
233
|
|
|
175
234
|
if platform.system() == 'Windows':
|
|
176
235
|
try:
|
|
@@ -193,16 +252,22 @@ class CmdStanModel:
|
|
|
193
252
|
else:
|
|
194
253
|
get_logger().debug("TBB already found in load path")
|
|
195
254
|
|
|
255
|
+
if compile and self._exe_file is None:
|
|
256
|
+
self.compile(force=str(compile).lower() == 'force', _internal=True)
|
|
257
|
+
|
|
196
258
|
def __repr__(self) -> str:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
)
|
|
259
|
+
repr = 'CmdStanModel: name={}'.format(self._name)
|
|
260
|
+
repr = '{}\n\t stan_file={}'.format(repr, self._stan_file)
|
|
261
|
+
repr = '{}\n\t exe_file={}'.format(repr, self._exe_file)
|
|
262
|
+
repr = '{}\n\t compiler_options={}'.format(repr, self._compiler_options)
|
|
263
|
+
return repr
|
|
201
264
|
|
|
202
265
|
@property
|
|
203
266
|
def name(self) -> str:
|
|
204
267
|
"""
|
|
205
|
-
Model name used in output filename templates.
|
|
268
|
+
Model name used in output filename templates. Default is basename
|
|
269
|
+
of Stan program or exe file, unless specified in call to constructor
|
|
270
|
+
via argument ``model_name``.
|
|
206
271
|
"""
|
|
207
272
|
return self._name
|
|
208
273
|
|
|
@@ -212,7 +277,7 @@ class CmdStanModel:
|
|
|
212
277
|
return self._stan_file
|
|
213
278
|
|
|
214
279
|
@property
|
|
215
|
-
def exe_file(self) ->
|
|
280
|
+
def exe_file(self) -> OptionalPath:
|
|
216
281
|
"""Full path to Stan exe file."""
|
|
217
282
|
return self._exe_file
|
|
218
283
|
|
|
@@ -220,17 +285,25 @@ class CmdStanModel:
|
|
|
220
285
|
"""
|
|
221
286
|
Run model with option 'info'. Parse output statements, which all
|
|
222
287
|
have form 'key = value' into a Dict.
|
|
288
|
+
If exe file compiled with CmdStan < 2.27, option 'info' isn't
|
|
289
|
+
available and the method returns an empty dictionary.
|
|
223
290
|
"""
|
|
224
291
|
result: dict[str, str] = {}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
292
|
+
if self.exe_file is None:
|
|
293
|
+
return result
|
|
294
|
+
try:
|
|
295
|
+
info = StringIO()
|
|
296
|
+
do_command(cmd=[str(self.exe_file), 'info'], fd_out=info)
|
|
297
|
+
lines = info.getvalue().split('\n')
|
|
298
|
+
for line in lines:
|
|
299
|
+
kv_pair = [x.strip() for x in line.split('=')]
|
|
300
|
+
if len(kv_pair) != 2:
|
|
301
|
+
continue
|
|
302
|
+
result[kv_pair[0]] = kv_pair[1]
|
|
303
|
+
return result
|
|
304
|
+
except RuntimeError as e:
|
|
305
|
+
get_logger().debug(e)
|
|
306
|
+
return result
|
|
234
307
|
|
|
235
308
|
def src_info(self) -> dict[str, Any]:
|
|
236
309
|
"""
|
|
@@ -241,7 +314,7 @@ class CmdStanModel:
|
|
|
241
314
|
"""
|
|
242
315
|
if self.stan_file is None or cmdstan_version_before(2, 27):
|
|
243
316
|
return {}
|
|
244
|
-
return compilation.src_info(str(self.stan_file), self.
|
|
317
|
+
return compilation.src_info(str(self.stan_file), self._compiler_options)
|
|
245
318
|
|
|
246
319
|
# TODO(2.0) remove
|
|
247
320
|
def format(
|
|
@@ -287,9 +360,24 @@ class CmdStanModel:
|
|
|
287
360
|
max_line_length=max_line_length,
|
|
288
361
|
canonicalize=canonicalize,
|
|
289
362
|
backup=backup,
|
|
290
|
-
stanc_options=self.
|
|
363
|
+
stanc_options=self.stanc_options,
|
|
291
364
|
)
|
|
292
365
|
|
|
366
|
+
@property
|
|
367
|
+
def stanc_options(self) -> dict[str, Union[bool, int, str]]:
|
|
368
|
+
"""Options to stanc compilers."""
|
|
369
|
+
return self._compiler_options._stanc_options
|
|
370
|
+
|
|
371
|
+
@property
|
|
372
|
+
def cpp_options(self) -> dict[str, Union[bool, int]]:
|
|
373
|
+
"""Options to C++ compilers."""
|
|
374
|
+
return self._compiler_options._cpp_options
|
|
375
|
+
|
|
376
|
+
@property
|
|
377
|
+
def user_header(self) -> str:
|
|
378
|
+
"""The user header file if it exists, otherwise empty"""
|
|
379
|
+
return self._compiler_options._user_header
|
|
380
|
+
|
|
293
381
|
def code(self) -> Optional[str]:
|
|
294
382
|
"""Return Stan program as a string."""
|
|
295
383
|
if not self._stan_file:
|
|
@@ -305,6 +393,82 @@ class CmdStanModel:
|
|
|
305
393
|
)
|
|
306
394
|
return code
|
|
307
395
|
|
|
396
|
+
# TODO(2.0): remove
|
|
397
|
+
def compile(
|
|
398
|
+
self,
|
|
399
|
+
force: bool = False,
|
|
400
|
+
stanc_options: Optional[dict[str, Any]] = None,
|
|
401
|
+
cpp_options: Optional[dict[str, Any]] = None,
|
|
402
|
+
user_header: OptionalPath = None,
|
|
403
|
+
override_options: bool = False,
|
|
404
|
+
*,
|
|
405
|
+
_internal: bool = False,
|
|
406
|
+
) -> None:
|
|
407
|
+
"""
|
|
408
|
+
Deprecated: To compile a model, use the :class:`~cmdstanpy.CmdStanModel`
|
|
409
|
+
constructor or :func:`cmdstanpy.compile_stan_file()`.
|
|
410
|
+
|
|
411
|
+
Compile the given Stan program file. Translates the Stan code to
|
|
412
|
+
C++, then calls the C++ compiler.
|
|
413
|
+
|
|
414
|
+
By default, this function compares the timestamps on the source and
|
|
415
|
+
executable files; if the executable is newer than the source file, it
|
|
416
|
+
will not recompile the file, unless argument ``force`` is ``True``
|
|
417
|
+
or unless the compiler options have been changed.
|
|
418
|
+
|
|
419
|
+
:param force: When ``True``, always compile, even if the executable file
|
|
420
|
+
is newer than the source file. Used for Stan models which have
|
|
421
|
+
``#include`` directives in order to force recompilation when changes
|
|
422
|
+
are made to the included files.
|
|
423
|
+
|
|
424
|
+
:param stanc_options: Options for stanc compiler.
|
|
425
|
+
:param cpp_options: Options for C++ compiler.
|
|
426
|
+
:param user_header: A path to a header file to include during C++
|
|
427
|
+
compilation.
|
|
428
|
+
|
|
429
|
+
:param override_options: When ``True``, override existing option.
|
|
430
|
+
When ``False``, add/replace existing options. Default is ``False``.
|
|
431
|
+
"""
|
|
432
|
+
if not _internal:
|
|
433
|
+
get_logger().warning(
|
|
434
|
+
"CmdStanModel.compile() is deprecated and will be removed in "
|
|
435
|
+
"the next major version. To compile a model, use the "
|
|
436
|
+
"CmdStanModel() constructor or cmdstanpy.compile_stan_file()."
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
if not self._stan_file:
|
|
440
|
+
raise RuntimeError('Please specify source file')
|
|
441
|
+
|
|
442
|
+
compiler_options = None
|
|
443
|
+
if (
|
|
444
|
+
stanc_options is not None
|
|
445
|
+
or cpp_options is not None
|
|
446
|
+
or user_header is not None
|
|
447
|
+
):
|
|
448
|
+
compiler_options = compilation.CompilerOptions(
|
|
449
|
+
stanc_options=stanc_options,
|
|
450
|
+
cpp_options=cpp_options,
|
|
451
|
+
user_header=user_header,
|
|
452
|
+
)
|
|
453
|
+
compiler_options.validate()
|
|
454
|
+
|
|
455
|
+
if compiler_options != self._compiler_options:
|
|
456
|
+
force = True
|
|
457
|
+
if self._compiler_options is None:
|
|
458
|
+
self._compiler_options = compiler_options
|
|
459
|
+
elif override_options:
|
|
460
|
+
self._compiler_options = compiler_options
|
|
461
|
+
else:
|
|
462
|
+
self._compiler_options.add(compiler_options)
|
|
463
|
+
|
|
464
|
+
self._exe_file = compilation.compile_stan_file(
|
|
465
|
+
str(self.stan_file),
|
|
466
|
+
force=force,
|
|
467
|
+
stanc_options=self._compiler_options.stanc_options,
|
|
468
|
+
cpp_options=self._compiler_options.cpp_options,
|
|
469
|
+
user_header=self._compiler_options.user_header,
|
|
470
|
+
)
|
|
471
|
+
|
|
308
472
|
@deprecate_positional_args(last_deprecated="jacobian")
|
|
309
473
|
def optimize(
|
|
310
474
|
self,
|