Skip to content

Commit a443a9d

Browse files
authored
Merge pull request #820 from stan-dev/start-2.0-model-constructor
Update model to always be compiled, remove deprecated compilation features
2 parents 956f237 + 6063937 commit a443a9d

File tree

9 files changed

+228
-596
lines changed

9 files changed

+228
-596
lines changed

cmdstanpy/cmdstan_args.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ class CmdStanArgs:
632632
def __init__(
633633
self,
634634
model_name: str,
635-
model_exe: OptionalPath,
635+
model_exe: str,
636636
chain_ids: Optional[list[int]],
637637
method_args: Union[
638638
SamplerArgs,
@@ -692,10 +692,6 @@ def validate(self) -> None:
692692
* if no seed specified, set random seed.
693693
* length of per-chain lists equals specified # of chains
694694
"""
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')
699695

700696
if self.chain_ids is not None:
701697
for chain_id in self.chain_ids:
@@ -857,10 +853,10 @@ def compose_command(
857853
idx, len(self.chain_ids)
858854
)
859855
)
860-
cmd.append(self.model_exe) # type: ignore # guaranteed by validate
856+
cmd.append(self.model_exe)
861857
cmd.append(f'id={self.chain_ids[idx]}')
862858
else:
863-
cmd.append(self.model_exe) # type: ignore # guaranteed by validate
859+
cmd.append(self.model_exe)
864860

865861
if self.seed is not None:
866862
if not isinstance(self.seed, list):

cmdstanpy/compilation.py

Lines changed: 19 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import platform
99
import shutil
1010
import subprocess
11-
from copy import copy
1211
from datetime import datetime
1312
from pathlib import Path
1413
from typing import Any, Iterable, Optional, Union
@@ -37,12 +36,6 @@
3736
'warn-pedantic',
3837
]
3938

40-
# TODO(2.0): remove
41-
STANC_DEPRECATED_OPTS = {
42-
'allow_undefined': 'allow-undefined',
43-
'include_paths': 'include-paths',
44-
}
45-
4639
STANC_IGNORE_OPTS = [
4740
'debug-lex',
4841
'debug-parse',
@@ -67,7 +60,6 @@
6760
OptionalPath = Union[str, os.PathLike, None]
6861

6962

70-
# TODO(2.0): can remove add function and other logic
7163
class CompilerOptions:
7264
"""
7365
User-specified flags for stanc and C++ compiler.
@@ -95,26 +87,6 @@ def __repr__(self) -> str:
9587
self._stanc_options, self._cpp_options
9688
)
9789

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-
11890
@property
11991
def stanc_options(self) -> dict[str, Union[bool, int, str, Iterable[str]]]:
12092
"""Stanc compiler options."""
@@ -144,31 +116,12 @@ def validate_stanc_opts(self) -> None:
144116
Check stanc compiler args and consistency between stanc and C++ options.
145117
Raise ValueError if bad config is found.
146118
"""
147-
# pylint: disable=no-member
148119
if self._stanc_options is None:
149120
return
150121
ignore = []
151122
paths = None
152123
has_o_flag = False
153124

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-
)
172125
for key, val in self._stanc_options.items():
173126
if key in STANC_IGNORE_OPTS:
174127
get_logger().info('ignoring compiler option: %s', key)
@@ -267,37 +220,6 @@ def validate_user_header(self) -> None:
267220

268221
self._cpp_options['USER_HEADER'] = self._user_header
269222

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-
301223
def compose_stanc(self, filename_in_msg: Optional[str]) -> list[str]:
302224
opts = []
303225

@@ -343,7 +265,8 @@ def compose(self, filename_in_msg: Optional[str] = None) -> list[str]:
343265

344266

345267
def src_info(
346-
stan_file: str, compiler_options: CompilerOptions
268+
stan_file: str,
269+
stanc_options: Optional[dict[str, Any]] = None,
347270
) -> dict[str, Any]:
348271
"""
349272
Get source info for Stan program file.
@@ -354,7 +277,7 @@ def src_info(
354277
cmd = (
355278
[stanc_path()]
356279
# handle include-paths, allow-undefined etc
357-
+ compiler_options.compose_stanc(None)
280+
+ CompilerOptions(stanc_options=stanc_options).compose_stanc(None)
358281
+ ['--info', str(stan_file)]
359282
)
360283
proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
@@ -407,12 +330,26 @@ def compile_stan_file(
407330
)
408331
compiler_options.validate()
409332

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+
410345
exe_target = src.with_suffix(EXTENSION)
411346
if exe_target.exists():
412347
exe_time = os.path.getmtime(exe_target)
413348
included_files = [src]
414349
included_files.extend(
415-
src_info(str(src), compiler_options).get('included_files', [])
350+
src_info(str(src), compiler_options.stanc_options).get(
351+
'included_files', []
352+
)
416353
)
417354
out_of_date = any(
418355
os.path.getmtime(included_file) > exe_time
@@ -482,7 +419,7 @@ def compile_stan_file(
482419
raise ValueError(
483420
f"Failed to compile Stan model '{src}'. Console:\n{console}"
484421
)
485-
return str(exe_target)
422+
return os.fspath(exe_target)
486423

487424

488425
def format_stan_file(

0 commit comments

Comments
 (0)