Skip to content

Commit 13fe59f

Browse files
authored
Merge pull request #686 from stan-dev/feature/pathfinder
[Stan 2.33] Pathfinder
2 parents 099bcfd + fa307df commit 13fe59f

24 files changed

+2095
-294
lines changed

.pylintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ disable=consider-using-in,
7676
consider-using-dict-items,
7777
unspecified-encoding,
7878
consider-using-f-string,
79+
duplicate-code,
7980

8081
# Enable the message, report, category or checker with the given id(s). You can
8182
# either give multiple identifier separated by comma (,) or put this option

cmdstanpy/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ def _cleanup_tmpdir() -> None:
2626
from .model import CmdStanModel
2727
from .stanfit import (
2828
CmdStanGQ,
29+
CmdStanLaplace,
2930
CmdStanMCMC,
3031
CmdStanMLE,
32+
CmdStanPathfinder,
3133
CmdStanVB,
3234
InferenceMetadata,
3335
from_csv,
@@ -51,6 +53,8 @@ def _cleanup_tmpdir() -> None:
5153
'CmdStanMLE',
5254
'CmdStanGQ',
5355
'CmdStanVB',
56+
'CmdStanLaplace',
57+
'CmdStanPathfinder',
5458
'CmdStanModel',
5559
'InferenceMetadata',
5660
'from_csv',

cmdstanpy/cmdstan_args.py

Lines changed: 197 additions & 208 deletions
Large diffs are not rendered by default.

cmdstanpy/model.py

Lines changed: 269 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
LaplaceArgs,
4545
Method,
4646
OptimizeArgs,
47+
PathfinderArgs,
4748
SamplerArgs,
4849
VariationalArgs,
4950
)
@@ -53,6 +54,7 @@
5354
CmdStanLaplace,
5455
CmdStanMCMC,
5556
CmdStanMLE,
57+
CmdStanPathfinder,
5658
CmdStanVB,
5759
RunSet,
5860
from_csv,
@@ -874,6 +876,7 @@ def sample(
874876
* dictionary - pairs parameter name : initial value.
875877
* string - pathname to a JSON or Rdump data file.
876878
* list of strings - per-chain pathname to data file.
879+
* list of dictionaries - per-chain initial values.
877880
878881
:param iter_warmup: Number of warmup iterations for each chain.
879882
@@ -947,8 +950,7 @@ def sample(
947950
only those values in the generated quantities block that are
948951
produced by RNG functions may change. This provides
949952
a way to use Stan programs to generate simulated data via the
950-
generated quantities block. This option must be used when the
951-
parameters block is empty. Default value is ``False``.
953+
generated quantities block. Default value is ``False``.
952954
953955
:param output_dir: Name of the directory to which CmdStan output
954956
files are written. If unspecified, output files will be written
@@ -964,14 +966,14 @@ def sample(
964966
If ``True``, CSV outputs are written to an output file
965967
'<model_name>-<YYYYMMDDHHMM>-diagnostic-<chain_id>',
966968
e.g. 'bernoulli-201912081451-diagnostic-1.csv', see
967-
https://mc-stan.org/docs/cmdstan-guide/stan-csv.html,
969+
https://mc-stan.org/docs/cmdstan-guide/stan_csv.html,
968970
section "Diagnostic CSV output file" for details.
969971
970972
:param save_profile: Whether or not to profile auto-diff operations in
971973
labelled blocks of code. If ``True``, CSV outputs are written to
972974
file '<model_name>-<YYYYMMDDHHMM>-profile-<chain_id>'.
973975
Introduced in CmdStan-2.26, see
974-
https://mc-stan.org/docs/cmdstan-guide/stan-csv.html,
976+
https://mc-stan.org/docs/cmdstan-guide/stan_csv.html,
975977
section "Profiling CSV output file" for details.
976978
977979
:param show_progress: If ``True``, display progress bar to track
@@ -1448,12 +1450,14 @@ def variational(
14481450
adapt_iter: Optional[int] = None,
14491451
tol_rel_obj: Optional[float] = None,
14501452
eval_elbo: Optional[int] = None,
1451-
output_samples: Optional[int] = None,
1453+
draws: Optional[int] = None,
14521454
require_converged: bool = True,
14531455
show_console: bool = False,
14541456
refresh: Optional[int] = None,
14551457
time_fmt: str = "%Y%m%d%H%M%S",
14561458
timeout: Optional[float] = None,
1459+
*,
1460+
output_samples: Optional[int] = None,
14571461
) -> CmdStanVB:
14581462
"""
14591463
Run CmdStan's variational inference algorithm to approximate
@@ -1530,7 +1534,7 @@ def variational(
15301534
15311535
:param eval_elbo: Number of iterations between ELBO evaluations.
15321536
1533-
:param output_samples: Number of approximate posterior output draws
1537+
:param draws: Number of approximate posterior output draws
15341538
to save.
15351539
15361540
:param require_converged: Whether or not to raise an error if Stan
@@ -1551,6 +1555,19 @@ def variational(
15511555
15521556
:return: CmdStanVB object
15531557
"""
1558+
if output_samples is not None:
1559+
if draws is not None:
1560+
raise ValueError(
1561+
"Cannot supply both 'draws' and deprecated argument "
1562+
"'output_samples'"
1563+
)
1564+
get_logger().warning(
1565+
"Argument name `output_samples` is deprecated, please "
1566+
"rename to `draws`."
1567+
)
1568+
1569+
draws = output_samples
1570+
15541571
variational_args = VariationalArgs(
15551572
algorithm=algorithm,
15561573
iter=iter,
@@ -1561,7 +1578,7 @@ def variational(
15611578
adapt_iter=adapt_iter,
15621579
tol_rel_obj=tol_rel_obj,
15631580
eval_elbo=eval_elbo,
1564-
output_samples=output_samples,
1581+
output_samples=draws,
15651582
)
15661583

15671584
with temp_single_json(data) as _data, temp_inits(
@@ -1634,6 +1651,196 @@ def variational(
16341651
vb = CmdStanVB(runset)
16351652
return vb
16361653

1654+
def pathfinder(
1655+
self,
1656+
data: Union[Mapping[str, Any], str, os.PathLike, None] = None,
1657+
*,
1658+
init_alpha: Optional[float] = None,
1659+
tol_obj: Optional[float] = None,
1660+
tol_rel_obj: Optional[float] = None,
1661+
tol_grad: Optional[float] = None,
1662+
tol_rel_grad: Optional[float] = None,
1663+
tol_param: Optional[float] = None,
1664+
history_size: Optional[int] = None,
1665+
num_paths: Optional[int] = None,
1666+
max_lbfgs_iters: Optional[int] = None,
1667+
draws: Optional[int] = None,
1668+
num_single_draws: Optional[int] = None,
1669+
num_elbo_draws: Optional[int] = None,
1670+
# arguments standard to all methods
1671+
seed: Optional[int] = None,
1672+
inits: Union[Dict[str, float], float, str, os.PathLike, None] = None,
1673+
output_dir: OptionalPath = None,
1674+
sig_figs: Optional[int] = None,
1675+
save_profile: bool = False,
1676+
show_console: bool = False,
1677+
refresh: Optional[int] = None,
1678+
time_fmt: str = "%Y%m%d%H%M%S",
1679+
timeout: Optional[float] = None,
1680+
) -> CmdStanPathfinder:
1681+
"""
1682+
Run CmdStan's Pathfinder variational inference algorithm.
1683+
1684+
:param data: Values for all data variables in the model, specified
1685+
either as a dictionary with entries matching the data variables,
1686+
or as the path of a data file in JSON or Rdump format.
1687+
1688+
:param num_paths: Number of single-path Pathfinders to run.
1689+
Default is 4, when the number of paths is 1 then no importance
1690+
sampling is done.
1691+
1692+
:param draws: Number of approximate draws to return.
1693+
1694+
:param num_single_draws: Number of draws each single-pathfinder will
1695+
draw. By default, this is set to be equal to draws.
1696+
If ``num_paths`` is 1, only one of this and ``draws`` should be
1697+
used.
1698+
1699+
:param max_lbfgs_iters: Maximum number of L-BFGS iterations.
1700+
1701+
:param num_elbo_draws: Number of Monte Carlo draws to evaluate ELBO.
1702+
1703+
:param seed: The seed for random number generator. Must be an integer
1704+
between 0 and 2^32 - 1. If unspecified,
1705+
:class:`numpy.random.RandomState` is used to generate a seed.
1706+
1707+
:param inits: Specifies how the algorithm initializes parameter values.
1708+
Initialization is either uniform random on a range centered on 0,
1709+
exactly 0, or a dictionary or file of initial values for some or all
1710+
parameters in the model. The default initialization behavior will
1711+
initialize all parameter values on range [-2, 2] on the
1712+
*unconstrained* support. If the expected parameter values are
1713+
too far from this range, this option may improve adaptation.
1714+
The following value types are allowed:
1715+
1716+
* Single number n > 0 - initialization range is [-n, n].
1717+
* 0 - all parameters are initialized to 0.
1718+
* dictionary - pairs parameter name : initial value.
1719+
* string - pathname to a JSON or Rdump data file.
1720+
* list of strings - per-path pathname to data file.
1721+
* list of dictionaries - per-path initial values.
1722+
1723+
:param init_alpha: For internal L-BFGS: Line search step size for
1724+
first iteration
1725+
1726+
:param tol_obj: For internal L-BFGS: Convergence tolerance on changes
1727+
in objective function value
1728+
1729+
:param tol_rel_obj: For internal L-BFGS: Convergence tolerance on
1730+
relative changes in objective function value
1731+
1732+
:param tol_grad: For internal L-BFGS: Convergence tolerance on the
1733+
norm of the gradient
1734+
1735+
:param tol_rel_grad: For internal L-BFGS: Convergence tolerance on
1736+
the relative norm of the gradient
1737+
1738+
:param tol_param: For internal L-BFGS: Convergence tolerance on changes
1739+
in parameter value
1740+
1741+
:param history_size: For internal L-BFGS: Size of the history for LBFGS
1742+
Hessian approximation. The value should be less than the
1743+
dimensionality of the parameter space. 5-10 is usually sufficient
1744+
1745+
:param output_dir: Name of the directory to which CmdStan output
1746+
files are written. If unspecified, output files will be written
1747+
to a temporary directory which is deleted upon session exit.
1748+
1749+
:param sig_figs: Numerical precision used for output CSV and text files.
1750+
Must be an integer between 1 and 18. If unspecified, the default
1751+
precision for the system file I/O is used; the usual value is 6.
1752+
Introduced in CmdStan-2.25.
1753+
1754+
:param save_profile: Whether or not to profile auto-diff operations in
1755+
labelled blocks of code. If ``True``, CSV outputs are written to
1756+
file '<model_name>-<YYYYMMDDHHMM>-profile-<path_id>'.
1757+
Introduced in CmdStan-2.26, see
1758+
https://mc-stan.org/docs/cmdstan-guide/stan_csv.html,
1759+
section "Profiling CSV output file" for details.
1760+
1761+
:param show_console: If ``True``, stream CmdStan messages sent to stdout
1762+
and stderr to the console. Default is ``False``.
1763+
1764+
:param refresh: Specify the number of iterations CmdStan will take
1765+
between progress messages. Default value is 100.
1766+
1767+
:param time_fmt: A format string passed to
1768+
:meth:`~datetime.datetime.strftime` to decide the file names for
1769+
output CSVs. Defaults to "%Y%m%d%H%M%S"
1770+
1771+
:param timeout: Duration at which Pathfinder times
1772+
out in seconds. Defaults to None.
1773+
1774+
:return: A :class:`CmdStanPathfinder` object
1775+
1776+
References
1777+
----------
1778+
1779+
Zhang, L., Carpenter, B., Gelman, A., & Vehtari, A. (2022). Pathfinder:
1780+
Parallel quasi-Newton variational inference. Journal of Machine Learning
1781+
Research, 23(306), 1–49. Retrieved from
1782+
http://jmlr.org/papers/v23/21-0889.html
1783+
"""
1784+
if cmdstan_version_before(2, 33, self.exe_info()):
1785+
raise ValueError(
1786+
"Method 'pathfinder' not available for CmdStan versions "
1787+
"before 2.33"
1788+
)
1789+
1790+
if num_single_draws is None:
1791+
num_single_draws = draws
1792+
elif num_paths == 1 and draws is not None and num_single_draws != draws:
1793+
raise ValueError(
1794+
"Cannot specify both 'draws' and 'num_single_draws'"
1795+
" when 'num_paths' is 1"
1796+
)
1797+
1798+
pathfinder_args = PathfinderArgs(
1799+
init_alpha=init_alpha,
1800+
tol_obj=tol_obj,
1801+
tol_rel_obj=tol_rel_obj,
1802+
tol_grad=tol_grad,
1803+
tol_rel_grad=tol_rel_grad,
1804+
tol_param=tol_param,
1805+
history_size=history_size,
1806+
num_psis_draws=draws,
1807+
num_paths=num_paths,
1808+
max_lbfgs_iters=max_lbfgs_iters,
1809+
num_draws=num_single_draws,
1810+
num_elbo_draws=num_elbo_draws,
1811+
)
1812+
1813+
with temp_single_json(data) as _data, temp_inits(inits) as _inits:
1814+
args = CmdStanArgs(
1815+
self._name,
1816+
self._exe_file,
1817+
chain_ids=None,
1818+
data=_data,
1819+
seed=seed,
1820+
inits=_inits,
1821+
output_dir=output_dir,
1822+
sig_figs=sig_figs,
1823+
save_profile=save_profile,
1824+
method_args=pathfinder_args,
1825+
refresh=refresh,
1826+
)
1827+
dummy_chain_id = 0
1828+
runset = RunSet(args=args, chains=1, time_fmt=time_fmt)
1829+
self._run_cmdstan(
1830+
runset,
1831+
dummy_chain_id,
1832+
show_console=show_console,
1833+
timeout=timeout,
1834+
)
1835+
runset.raise_for_timeouts()
1836+
1837+
if not runset._check_retcodes():
1838+
msg = "Error during Pathfinder! Command '{}' failed: {}".format(
1839+
' '.join(runset.cmd(0)), runset.get_err_msgs()
1840+
)
1841+
raise RuntimeError(msg)
1842+
return CmdStanPathfinder(runset)
1843+
16371844
def log_prob(
16381845
self,
16391846
params: Union[Dict[str, Any], str, os.PathLike],
@@ -1733,6 +1940,61 @@ def laplace_sample(
17331940
timeout: Optional[float] = None,
17341941
opt_args: Optional[Dict[str, Any]] = None,
17351942
) -> CmdStanLaplace:
1943+
"""
1944+
Run a Laplace approximation around the posterior mode.
1945+
1946+
:param data: Values for all data variables in the model, specified
1947+
either as a dictionary with entries matching the data variables,
1948+
or as the path of a data file in JSON or Rdump format.
1949+
1950+
:param mode: The mode around which to place the approximation.
1951+
This can be:
1952+
* A :class:`CmdStanMLE` object
1953+
* A path to a CSV file containing the output of an optimization run.
1954+
* ``None`` - use default optimizer settings and/or any ``opt_args``.
1955+
1956+
:param draws: Number of approximate draws to return.
1957+
Defaults to 1000
1958+
1959+
:param jacobian: Whether or not to enable the Jacobian adjustment
1960+
for constrained parameters. Defaults to ``True``.
1961+
Note: This must match the argument used in the creation of
1962+
``mode``, if supplied.
1963+
1964+
:param output_dir: Name of the directory to which CmdStan output
1965+
files are written. If unspecified, output files will be written
1966+
to a temporary directory which is deleted upon session exit.
1967+
1968+
:param sig_figs: Numerical precision used for output CSV and text files.
1969+
Must be an integer between 1 and 18. If unspecified, the default
1970+
precision for the system file I/O is used; the usual value is 6.
1971+
Introduced in CmdStan-2.25.
1972+
1973+
:param save_profile: Whether or not to profile auto-diff operations in
1974+
labelled blocks of code. If ``True``, CSV outputs are written to
1975+
file '<model_name>-<YYYYMMDDHHMM>-profile-<path_id>'.
1976+
Introduced in CmdStan-2.26, see
1977+
https://mc-stan.org/docs/cmdstan-guide/stan_csv.html,
1978+
section "Profiling CSV output file" for details.
1979+
1980+
:param show_console: If ``True``, stream CmdStan messages sent to stdout
1981+
and stderr to the console. Default is ``False``.
1982+
1983+
:param refresh: Specify the number of iterations CmdStan will take
1984+
between progress messages. Default value is 100.
1985+
1986+
:param time_fmt: A format string passed to
1987+
:meth:`~datetime.datetime.strftime` to decide the file names for
1988+
output CSVs. Defaults to "%Y%m%d%H%M%S"
1989+
1990+
:param timeout: Duration at which Pathfinder times
1991+
out in seconds. Defaults to None.
1992+
1993+
:param opt_args: Dictionary of additional arguments
1994+
which will be passed to :meth:`~CmdStanModel.optimize`
1995+
1996+
:return: A :class:`CmdStanLaplace` object.
1997+
"""
17361998
if cmdstan_version_before(2, 32, self.exe_info()):
17371999
raise ValueError(
17382000
"Method 'laplace_sample' not available for CmdStan versions "

0 commit comments

Comments
 (0)