Skip to content

Commit 4c5d3d5

Browse files
authored
Merge pull request #691 from stan-dev/release/1.2-prep
Prep for release v1.2.0
2 parents e6489e4 + 8ae8cc4 commit 4c5d3d5

File tree

11 files changed

+178
-82
lines changed

11 files changed

+178
-82
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ jobs:
110110
run: |
111111
install_cmdstan -h
112112
install_cxx_toolchain -h
113-
python -m cmdstanpy.install_cmdstan --compiler --version ${{ needs.get-cmdstan-version.outputs.version }} --cores 2
113+
$env:TBB_CXXFLAGS = "-D_UCRT"
114+
python -m cmdstanpy.install_cmdstan --version ${{ needs.get-cmdstan-version.outputs.version }} --cores 2
115+
echo "TBB_CXXFLAGS+=-D_UCRT" >> ~/.cmdstan/cmdstan-${{ needs.get-cmdstan-version.outputs.version }}/make/local
114116
115117
- name: Run tests
116118
run: |

cmdstanpy/compilation.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
'warn-pedantic',
3131
]
3232

33+
# TODO(2.0): remove
3334
STANC_DEPRECATED_OPTS = {
3435
'allow_undefined': 'allow-undefined',
3536
'include_paths': 'include-paths',
@@ -59,6 +60,7 @@
5960
OptionalPath = Union[str, os.PathLike, None]
6061

6162

63+
# TODO(2.0): can remove add function and other logic
6264
class CompilerOptions:
6365
"""
6466
User-specified flags for stanc and C++ compiler.

cmdstanpy/model.py

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ def code(self) -> Optional[str]:
434434
)
435435
return code
436436

437+
# TODO(2.0): remove
437438
def compile(
438439
self,
439440
force: bool = False,
@@ -1162,17 +1163,21 @@ def sample(
11621163
if not runset._check_retcodes():
11631164
msg = (
11641165
f'Error during sampling:\n{errors}\n'
1165-
+ f'Command and output files:\n{repr(runset)}\n'
1166-
+ 'Consider re-running with show_console=True if the above'
1167-
+ ' output is unclear!'
1166+
f'Command and output files:\n{repr(runset)}'
11681167
)
1168+
if not show_console:
1169+
msg += (
1170+
'\nConsider re-running with show_console=True if the'
1171+
' above output is unclear!'
1172+
)
11691173
raise RuntimeError(msg)
11701174
if errors:
1171-
msg = (
1172-
f'Non-fatal error during sampling:\n{errors}\n'
1173-
+ 'Consider re-running with show_console=True if the above'
1174-
+ ' output is unclear!'
1175-
)
1175+
msg = f'Non-fatal error during sampling:\n{errors}'
1176+
if not show_console:
1177+
msg += (
1178+
'\nConsider re-running with show_console=True if the'
1179+
' above output is unclear!'
1180+
)
11761181
get_logger().warning(msg)
11771182

11781183
mcmc = CmdStanMCMC(runset)
@@ -1256,6 +1261,7 @@ def generate_quantities(
12561261
12571262
:return: CmdStanGQ object
12581263
"""
1264+
# TODO(2.0): remove
12591265
if mcmc_sample is not None:
12601266
if previous_fit:
12611267
raise ValueError(
@@ -1359,10 +1365,13 @@ def generate_quantities(
13591365
if errors:
13601366
msg = (
13611367
f'Error during generate_quantities:\n{errors}\n'
1362-
+ f'Command and output files:\n{repr(runset)}\n'
1363-
+ 'Consider re-running with show_console=True if the above'
1364-
+ ' output is unclear!'
1368+
f'Command and output files:\n{repr(runset)}'
13651369
)
1370+
if not show_console:
1371+
msg += (
1372+
'\nConsider re-running with show_console=True if the'
1373+
' above output is unclear!'
1374+
)
13661375
raise RuntimeError(msg)
13671376
quantities = CmdStanGQ(runset=runset, previous_fit=fit_object)
13681377
return quantities
@@ -1490,6 +1499,7 @@ def variational(
14901499
14911500
:return: CmdStanVB object
14921501
"""
1502+
# TODO(2.0): remove
14931503
if output_samples is not None:
14941504
if draws is not None:
14951505
raise ValueError(
@@ -1627,7 +1637,7 @@ def pathfinder(
16271637
:param draws: Number of approximate draws to return.
16281638
16291639
:param num_single_draws: Number of draws each single-pathfinder will
1630-
draw. By default, this is set to be equal to draws.
1640+
draw.
16311641
If ``num_paths`` is 1, only one of this and ``draws`` should be
16321642
used.
16331643
@@ -1722,13 +1732,14 @@ def pathfinder(
17221732
"before 2.33"
17231733
)
17241734

1725-
if num_single_draws is None:
1726-
num_single_draws = draws
1727-
elif num_paths == 1 and draws is not None and num_single_draws != draws:
1728-
raise ValueError(
1729-
"Cannot specify both 'draws' and 'num_single_draws'"
1730-
" when 'num_paths' is 1"
1731-
)
1735+
if num_paths == 1:
1736+
if num_single_draws is None:
1737+
num_single_draws = draws
1738+
if draws is not None and num_single_draws != draws:
1739+
raise ValueError(
1740+
"Cannot specify both 'draws' and 'num_single_draws'"
1741+
" when 'num_paths' is 1"
1742+
)
17321743

17331744
pathfinder_args = PathfinderArgs(
17341745
init_alpha=init_alpha,

cmdstanpy/stanfit/gq.py

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ def draws(
172172
self,
173173
*,
174174
inc_warmup: bool = False,
175+
inc_iterations: bool = False,
175176
concat_chains: bool = False,
176177
inc_sample: bool = False,
177178
) -> np.ndarray:
@@ -204,6 +205,7 @@ def draws(
204205
CmdStanMCMC.draws
205206
"""
206207
self._assemble_generated_quantities()
208+
inc_warmup |= inc_iterations
207209
if inc_warmup:
208210
if (
209211
isinstance(self.previous_fit, CmdStanMCMC)
@@ -522,11 +524,7 @@ def draws_xr(
522524
'chain', 'draw', ...
523525
)
524526

525-
def stan_variable(
526-
self,
527-
var: str,
528-
inc_warmup: bool = False,
529-
) -> np.ndarray:
527+
def stan_variable(self, var: str, **kwargs: bool) -> np.ndarray:
530528
"""
531529
Return a numpy.ndarray which contains the set of draws
532530
for the named Stan program variable. Flattens the chains,
@@ -557,9 +555,9 @@ def stan_variable(
557555
558556
:param var: variable name
559557
560-
:param inc_warmup: When ``True`` and the warmup draws are present in
561-
the MCMC sample, then the warmup draws are included.
562-
Default value is ``False``.
558+
:param kwargs: Additional keyword arguments are passed to the underlying
559+
fit's ``stan_variable`` method if the variable is not a generated
560+
quantity.
563561
564562
See Also
565563
--------
@@ -579,36 +577,30 @@ def stan_variable(
579577
+ ", ".join(model_var_names | gq_var_names)
580578
)
581579
if var not in gq_var_names:
582-
if isinstance(self.previous_fit, CmdStanMCMC):
583-
return self.previous_fit.stan_variable(
584-
var, inc_warmup=inc_warmup
585-
)
586-
elif isinstance(self.previous_fit, CmdStanMLE):
587-
return np.atleast_1d( # type: ignore
588-
self.previous_fit.stan_variable(
589-
var, inc_iterations=inc_warmup
590-
)
591-
)
592-
else:
593-
return np.atleast_1d( # type: ignore
594-
self.previous_fit.stan_variable(var)
595-
)
580+
# TODO(2.0) atleast1d may not be needed
581+
return np.atleast_1d( # type: ignore
582+
self.previous_fit.stan_variable(var, **kwargs)
583+
)
584+
596585
# is gq variable
597586
self._assemble_generated_quantities()
598587

599-
draw1, _ = self._draws_start(inc_warmup)
588+
draw1, _ = self._draws_start(
589+
inc_warmup=kwargs.get('inc_warmup', False)
590+
or kwargs.get('inc_iterations', False)
591+
)
600592
draws = flatten_chains(self._draws[draw1:])
601593
out: np.ndarray = self._metadata.stan_vars[var].extract_reshape(draws)
602594
return out
603595

604-
def stan_variables(self, inc_warmup: bool = False) -> Dict[str, np.ndarray]:
596+
def stan_variables(self, **kwargs: bool) -> Dict[str, np.ndarray]:
605597
"""
606598
Return a dictionary mapping Stan program variables names
607599
to the corresponding numpy.ndarray containing the inferred values.
608600
609-
:param inc_warmup: When ``True`` and the warmup draws are present in
610-
the MCMC sample, then the warmup draws are included.
611-
Default value is ``False``
601+
:param kwargs: Additional keyword arguments are passed to the underlying
602+
fit's ``stan_variable`` method if the variable is not a generated
603+
quantity.
612604
613605
See Also
614606
--------
@@ -623,10 +615,10 @@ def stan_variables(self, inc_warmup: bool = False) -> Dict[str, np.ndarray]:
623615
sample_var_names = self.previous_fit._metadata.stan_vars.keys()
624616
gq_var_names = self._metadata.stan_vars.keys()
625617
for name in gq_var_names:
626-
result[name] = self.stan_variable(name, inc_warmup)
618+
result[name] = self.stan_variable(name, **kwargs)
627619
for name in sample_var_names:
628620
if name not in gq_var_names:
629-
result[name] = self.stan_variable(name, inc_warmup)
621+
result[name] = self.stan_variable(name, **kwargs)
630622
return result
631623

632624
def _assemble_generated_quantities(self) -> None:
@@ -733,6 +725,7 @@ def save_csvfiles(self, dir: Optional[str] = None) -> None:
733725
"""
734726
self.runset.save_csvfiles(dir)
735727

728+
# TODO(2.0): remove
736729
@property
737730
def mcmc_sample(self) -> Union[CmdStanMCMC, CmdStanMLE, CmdStanVB]:
738731
get_logger().warning(

cmdstanpy/stanfit/mle.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ def stan_variable(
169169
*,
170170
inc_iterations: bool = False,
171171
warn: bool = True,
172-
) -> np.ndarray:
172+
) -> Union[np.ndarray, float]:
173173
"""
174174
Return a numpy.ndarray which contains the estimates for the
175175
for the named Stan program variable where the dimensions of the
@@ -217,6 +217,14 @@ def stan_variable(
217217
out: np.ndarray = self._metadata.stan_vars[var].extract_reshape(
218218
data
219219
)
220+
# TODO(2.0) remove
221+
if out.shape == () or out.shape == (1,):
222+
get_logger().warning(
223+
"The default behavior of CmdStanMLE.stan_variable() "
224+
"will change in a future release to always return a "
225+
"numpy.ndarray, even for scalar variables."
226+
)
227+
return out.item() # type: ignore
220228
return out
221229
except KeyError:
222230
# pylint: disable=raise-missing-from
@@ -228,7 +236,7 @@ def stan_variable(
228236

229237
def stan_variables(
230238
self, inc_iterations: bool = False
231-
) -> Dict[str, np.ndarray]:
239+
) -> Dict[str, Union[np.ndarray, float]]:
232240
"""
233241
Return a dictionary mapping Stan program variables names
234242
to the corresponding numpy.ndarray containing the inferred values.

cmdstanpy/stanfit/vb.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from cmdstanpy.cmdstan_args import Method
1010
from cmdstanpy.utils import scan_variational_csv
11+
from cmdstanpy.utils.logging import get_logger
1112

1213
from .metadata import InferenceMetadata
1314
from .runset import RunSet
@@ -113,7 +114,9 @@ def metadata(self) -> InferenceMetadata:
113114
"""
114115
return self._metadata
115116

116-
def stan_variable(self, var: str) -> np.ndarray:
117+
def stan_variable(
118+
self, var: str, *, mean: Optional[bool] = None
119+
) -> Union[np.ndarray, float]:
117120
"""
118121
Return a numpy.ndarray which contains the estimates for the
119122
for the named Stan program variable where the dimensions of the
@@ -135,6 +138,10 @@ def stan_variable(self, var: str) -> np.ndarray:
135138
136139
:param var: variable name
137140
141+
:param mean: if True, return the variational mean. Otherwise,
142+
return the variational sample. The default behavior will
143+
change in a future release to return the variational sample.
144+
138145
See Also
139146
--------
140147
CmdStanVB.stan_variables
@@ -144,10 +151,35 @@ def stan_variable(self, var: str) -> np.ndarray:
144151
CmdStanGQ.stan_variable
145152
CmdStanLaplace.stan_variable
146153
"""
154+
# TODO(2.0): remove None case, make default `False`
155+
if mean is None:
156+
get_logger().warning(
157+
"The default behavior of CmdStanVB.stan_variable() "
158+
"will change in a future release to return the "
159+
"variational sample, rather than the mean.\n"
160+
"To maintain the current behavior, pass the argument "
161+
"mean=True"
162+
)
163+
mean = True
164+
if mean:
165+
draws = self._variational_mean
166+
else:
167+
draws = self._variational_sample
168+
147169
try:
148170
out: np.ndarray = self._metadata.stan_vars[var].extract_reshape(
149-
self._variational_sample
171+
draws
150172
)
173+
# TODO(2.0): remove
174+
if out.shape == () or out.shape == (1,):
175+
if mean:
176+
get_logger().warning(
177+
"The default behavior of "
178+
"CmdStanVB.stan_variable(mean=True) will change in a "
179+
"future release to always return a numpy.ndarray, even "
180+
"for scalar variables."
181+
)
182+
return out.item() # type: ignore
151183
return out
152184
except KeyError:
153185
# pylint: disable=raise-missing-from
@@ -157,7 +189,9 @@ def stan_variable(self, var: str) -> np.ndarray:
157189
+ ", ".join(self._metadata.stan_vars.keys())
158190
)
159191

160-
def stan_variables(self) -> Dict[str, np.ndarray]:
192+
def stan_variables(
193+
self, *, mean: Optional[bool] = None
194+
) -> Dict[str, Union[np.ndarray, float]]:
161195
"""
162196
Return a dictionary mapping Stan program variables names
163197
to the corresponding numpy.ndarray containing the inferred values.
@@ -173,7 +207,7 @@ def stan_variables(self) -> Dict[str, np.ndarray]:
173207
"""
174208
result = {}
175209
for name in self._metadata.stan_vars:
176-
result[name] = self.stan_variable(name)
210+
result[name] = self.stan_variable(name, mean=mean)
177211
return result
178212

179213
@property

docsrc/changes.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,36 @@ What's New
77

88
For full changes, see the `Releases page <https://github.com/stan-dev/cmdstanpy/releases>`_ on GitHub.
99

10+
CmdStanPy 1.2.0
11+
---------------
12+
- **New functionality**
13+
14+
- The Pathfinder algorithm (available in CmdStan 2.33+) is now availble as :meth:`CmdStanModel.pathfinder`.
15+
- Laplace approximations (available in CmdStan 2.32+) are now available as :meth:`CmdStanModel.laplace_sample`.
16+
- The :meth:`CmdStanModel.optimize` method now supports the ``jacobian`` boolean argument to enable change-of-variables adjustments.
17+
When enabled, the Maximum a posteriori estimate (MAP) is returned, rather than the MLE.
18+
- The :func:`cmdstanpy.install_cmdstan` function and script can install development versions of CmdStan using the ``git:`` prefix in the version.
19+
20+
- **Deprecations**
21+
The next non-bugfix release of CmdStanPy will be version 2.0, which will remove all existing deprecations. Additional deprecations in this version:
22+
23+
- :class:`CmdStanModel` will *require* that it has a compiled executable after construction. The ``compile`` argument is deprecated,
24+
(the ability to force recompilation is available under the argument ``force_compile``), and the ``compile()`` method is deprecated.
25+
If you wish to compile Stan files independent of constructing a model, use :func:`cmdstanpy.compile_stan_file`.
26+
- :meth:`CmdStanMLE.stan_variable` will begin returning a :class:`np.ndarray` in all cases, as opposed to the current behavior where sometimes a float is returned.
27+
- :meth:`CmdStanVB.stan_variables` will return the _draws_ from the approximate posterior, rather than the optimized mean.
28+
A new argument, ``mean``, can be set to True to return the mean instead. Additionally, a :class:`np.ndarray` will be returned in all cases starting in the next version.
29+
- :meth:`CmdStanModel.variational` argument ``output_samples`` will has been renamed to ``draws``.
30+
31+
- **Other changes**
32+
33+
- A list of dictionaries is now allowed as the ``inits`` argument to :meth:`CmdStanModel.sample`.
34+
- :func:`cmdstanpy.install_cmdstan` correctly fetches the CmdStan version for ppc64el machines.
35+
- The documentation on how to use external C++ code was updated.
36+
- Various other bug fixes.
37+
38+
.. note::
39+
The minimum supported version for CmdStanPy is now Python 3.8.
1040

1141
CmdStanPy 1.1.0
1242
---------------

0 commit comments

Comments
 (0)