22
33import io
44import os
5- import platform
65import re
76import shutil
87import subprocess
98import sys
109import tempfile
1110import threading
12- from collections import OrderedDict
1311from concurrent .futures import ThreadPoolExecutor
1412from io import StringIO
1513from multiprocessing import cpu_count
16- from typing import Any , Callable , Mapping , Sequence , TypeVar
14+ from typing import Any , Callable , Mapping , Sequence
1715
1816import numpy as np
1917import pandas as pd
3735 CmdStanMLE ,
3836 CmdStanPathfinder ,
3937 CmdStanVB ,
38+ PrevFit ,
4039 RunSet ,
4140 from_csv ,
4241)
43- from cmdstanpy .utils import (
44- cmdstan_path ,
45- cmdstan_version_before ,
46- do_command ,
47- get_logger ,
48- )
42+ from cmdstanpy .utils import do_command , get_logger
43+ from cmdstanpy .utils .cmdstan import cmdstan_version_before , windows_tbb_path
4944from cmdstanpy .utils .filesystem import (
5045 temp_inits ,
5146 temp_metrics ,
5651from . import progress as progbar
5752
5853OptionalPath = str | os .PathLike | None
59- Fit = TypeVar ('Fit' , CmdStanMCMC , CmdStanMLE , CmdStanVB )
6054
6155
6256class CmdStanModel :
@@ -118,6 +112,8 @@ def __init__(
118112
119113 self ._fixed_param = False
120114
115+ windows_tbb_path ()
116+
121117 if exe_file is not None :
122118 self ._exe_file = os .path .realpath (os .path .expanduser (exe_file ))
123119 if not os .path .exists (self ._exe_file ):
@@ -164,33 +160,26 @@ def __init__(
164160 )
165161
166162 # try to detect models w/out parameters, needed for sampler
167- if (not cmdstan_version_before (2 , 27 )) and cmdstan_version_before (
168- 2 , 36
169- ):
163+ if cmdstan_version_before (2 , 36 ):
170164 model_info = self .src_info ()
171165 if 'parameters' in model_info :
172166 self ._fixed_param |= len (model_info ['parameters' ]) == 0
173167
174- if platform .system () == 'Windows' :
175- try :
176- do_command (['where.exe' , 'tbb.dll' ], fd_out = None )
177- except RuntimeError :
178- # Add tbb to the $PATH on Windows
179- libtbb = os .environ .get ('STAN_TBB' )
180- if libtbb is None :
181- libtbb = os .path .join (
182- cmdstan_path (), 'stan' , 'lib' , 'stan_math' , 'lib' , 'tbb'
183- )
184- get_logger ().debug ("Adding TBB (%s) to PATH" , libtbb )
185- os .environ ['PATH' ] = ';' .join (
186- list (
187- OrderedDict .fromkeys (
188- [libtbb ] + os .environ .get ('PATH' , '' ).split (';' )
189- )
190- )
191- )
192- else :
193- get_logger ().debug ("TBB already found in load path" )
168+ # check CmdStan version compatibility
169+ exe_info = None
170+ try :
171+ exe_info = self .exe_info ()
172+ # pylint: disable=broad-except
173+ except Exception as e :
174+ get_logger ().warning (
175+ 'Could not get exe info for model %s, error: %s' ,
176+ self ._name ,
177+ str (e ),
178+ )
179+ if cmdstan_version_before (2 , 35 , exe_info ):
180+ raise RuntimeError (
181+ "This version of CmdStanPy requires CmdStan 2.35 or higher."
182+ )
194183
195184 def __repr__ (self ) -> str :
196185 return (
@@ -238,7 +227,7 @@ def src_info(self) -> dict[str, Any]:
238227 If stanc is older than 2.27 or if the stan
239228 file cannot be found, returns an empty dictionary.
240229 """
241- if self .stan_file is None or cmdstan_version_before ( 2 , 27 ) :
230+ if self .stan_file is None :
242231 return {}
243232 return compilation .src_info (str (self .stan_file ), self ._stanc_options )
244233
@@ -404,12 +393,6 @@ def optimize(
404393 jacobian = jacobian ,
405394 )
406395
407- if jacobian and cmdstan_version_before (2 , 32 , self .exe_info ()):
408- raise ValueError (
409- "Jacobian adjustment for optimization is only supported "
410- "in CmdStan 2.32 and above."
411- )
412-
413396 with (
414397 temp_single_json (data ) as _data ,
415398 temp_inits (inits , allow_multiple = False ) as _inits ,
@@ -734,34 +717,23 @@ def sample(
734717 if chains == 1 :
735718 force_one_process_per_chain = True
736719
737- if (
738- force_one_process_per_chain is None
739- and not cmdstan_version_before (2 , 28 , info_dict )
740- and stan_threads == 'true'
741- ):
720+ if force_one_process_per_chain is None and stan_threads == 'true' :
742721 one_process_per_chain = False
743722 num_threads = parallel_chains * num_threads
744723 parallel_procs = 1
745724 if force_one_process_per_chain is False :
746- if not cmdstan_version_before (2 , 28 , info_dict ):
747- one_process_per_chain = False
748- num_threads = parallel_chains * num_threads
749- parallel_procs = 1
750- if stan_threads == 'false' :
751- get_logger ().warning (
752- 'Stan program not compiled for threading, '
753- 'process will run chains sequentially. '
754- 'For multi-chain parallelization, recompile '
755- 'the model with argument '
756- '"cpp_options={\' STAN_THREADS\' :\' TRUE\' }.'
757- )
758- else :
725+ one_process_per_chain = False
726+ num_threads = parallel_chains * num_threads
727+ parallel_procs = 1
728+ if stan_threads == 'false' :
759729 get_logger ().warning (
760- 'Installed version of CmdStan cannot multi-process '
761- 'chains, will run %d processes. '
762- 'Run "install_cmdstan" to upgrade to latest version.' ,
763- chains ,
730+ 'Stan program not compiled for threading, '
731+ 'process will run chains sequentially. '
732+ 'For multi-chain parallelization, recompile '
733+ 'the model with argument '
734+ '"cpp_options={\' STAN_THREADS\' :\' TRUE\' }.'
764735 )
736+
765737 os .environ ['STAN_NUM_THREADS' ] = str (num_threads )
766738
767739 if chain_ids is None :
@@ -958,15 +930,15 @@ def sample(
958930 def generate_quantities (
959931 self ,
960932 data : Mapping [str , Any ] | str | os .PathLike | None = None ,
961- previous_fit : Fit | list [str ] | None = None ,
933+ previous_fit : PrevFit | list [str ] | None = None ,
962934 seed : int | None = None ,
963935 gq_output_dir : OptionalPath = None ,
964936 sig_figs : int | None = None ,
965937 show_console : bool = False ,
966938 refresh : int | None = None ,
967939 time_fmt : str = "%Y%m%d%H%M%S" ,
968940 timeout : float | None = None ,
969- ) -> CmdStanGQ [Fit ]:
941+ ) -> CmdStanGQ [PrevFit ]:
970942 """
971943 Run CmdStan's generate_quantities method which runs the generated
972944 quantities block of a model given an existing sample.
@@ -1032,7 +1004,16 @@ def generate_quantities(
10321004 :return: CmdStanGQ object
10331005 """
10341006
1035- if isinstance (previous_fit , (CmdStanMCMC , CmdStanMLE , CmdStanVB )):
1007+ if isinstance (
1008+ previous_fit ,
1009+ (
1010+ CmdStanMCMC ,
1011+ CmdStanMLE ,
1012+ CmdStanVB ,
1013+ CmdStanLaplace ,
1014+ CmdStanPathfinder ,
1015+ ),
1016+ ):
10361017 fit_object = previous_fit
10371018 fit_csv_files = previous_fit .runset .csv_files
10381019 elif isinstance (previous_fit , list ):
@@ -1042,7 +1023,7 @@ def generate_quantities(
10421023 )
10431024 try :
10441025 fit_csv_files = previous_fit
1045- fit_object : Fit = from_csv (fit_csv_files ) # type: ignore
1026+ fit_object : PrevFit = from_csv (fit_csv_files ) # type: ignore
10461027 except ValueError as e :
10471028 raise ValueError (
10481029 'Invalid sample from Stan CSV files, error:\n \t {}\n \t '
@@ -1064,11 +1045,6 @@ def generate_quantities(
10641045 'to generate additional quantities of interest.'
10651046 )
10661047 elif isinstance (fit_object , CmdStanMLE ):
1067- if cmdstan_version_before (2 , 31 ):
1068- raise RuntimeError (
1069- "Method generate_quantities was not "
1070- "available for non-HMC until CmdStan 2.31"
1071- )
10721048 chains = 1
10731049 chain_ids = [1 ]
10741050 if fit_object ._save_iterations :
@@ -1077,11 +1053,6 @@ def generate_quantities(
10771053 'to generate additional quantities of interest.'
10781054 )
10791055 else : # isinstance(fit_object, CmdStanVB)
1080- if cmdstan_version_before (2 , 31 ):
1081- raise RuntimeError (
1082- "Method generate_quantities was not "
1083- "available for non-HMC until CmdStan 2.31"
1084- )
10851056 chains = 1
10861057 chain_ids = [1 ]
10871058
@@ -1492,19 +1463,6 @@ def pathfinder(
14921463 """
14931464
14941465 exe_info = self .exe_info ()
1495- if cmdstan_version_before (2 , 33 , exe_info ):
1496- raise ValueError (
1497- "Method 'pathfinder' not available for CmdStan versions "
1498- "before 2.33"
1499- )
1500-
1501- if (not psis_resample or not calculate_lp ) and cmdstan_version_before (
1502- 2 , 34 , exe_info
1503- ):
1504- raise ValueError (
1505- "Arguments 'psis_resample' and 'calculate_lp' are only "
1506- "available for CmdStan versions 2.34 and later"
1507- )
15081466
15091467 if num_threads is not None :
15101468 if (
@@ -1613,11 +1571,6 @@ def log_prob(
16131571 unconstrained parameters of the model.
16141572 """
16151573
1616- if cmdstan_version_before (2 , 31 , self .exe_info ()):
1617- raise ValueError (
1618- "Method 'log_prob' not available for CmdStan versions "
1619- "before 2.31"
1620- )
16211574 with (
16221575 temp_single_json (data ) as _data ,
16231576 temp_single_json (params ) as _params ,
@@ -1729,11 +1682,7 @@ def laplace_sample(
17291682
17301683 :return: A :class:`CmdStanLaplace` object.
17311684 """
1732- if cmdstan_version_before (2 , 32 , self .exe_info ()):
1733- raise ValueError (
1734- "Method 'laplace_sample' not available for CmdStan versions "
1735- "before 2.32"
1736- )
1685+
17371686 if opt_args is not None and mode is not None :
17381687 raise ValueError (
17391688 "Cannot specify both 'opt_args' and 'mode' arguments"
0 commit comments