def test_opts_cpp_opencl(self):
        cpp_opts = {'OPENCL_DEVICE_ID': 1}
        opts = CompilerOptions(cpp_options=cpp_opts)
        opts.validate()
        opts_list = opts.compose()
        self.assertTrue('STAN_OPENCL=TRUE' in opts_list)
        self.assertTrue('OPENCL_DEVICE_ID=1' in opts_list)

        cpp_opts = {'OPENCL_DEVICE_ID': 'BAD'}
        opts = CompilerOptions(cpp_options=cpp_opts)
        with self.assertRaises(ValueError):
            opts.validate()

        cpp_opts = {'OPENCL_DEVICE_ID': -1}
        opts = CompilerOptions(cpp_options=cpp_opts)
        with self.assertRaises(ValueError):
            opts.validate()

        cpp_opts = {'OPENCL_PLATFORM_ID': 'BAD'}
        opts = CompilerOptions(cpp_options=cpp_opts)
        with self.assertRaises(ValueError):
            opts.validate()

        cpp_opts = {'OPENCL_PLATFORM_ID': -1}
        opts = CompilerOptions(cpp_options=cpp_opts)
        with self.assertRaises(ValueError):
            opts.validate()
    def test_opts_add(self):
        stanc_opts = {'warn-uninitialized': True}
        cpp_opts = {'STAN_OPENCL': 'TRUE', 'OPENCL_DEVICE_ID': 1}
        opts = CompilerOptions(stanc_options=stanc_opts, cpp_options=cpp_opts)
        opts.validate()
        opts_list = opts.compose()
        self.assertTrue('STAN_OPENCL=TRUE' in opts_list)
        self.assertTrue('OPENCL_DEVICE_ID=1' in opts_list)
        new_opts = CompilerOptions(cpp_options={
            'STAN_OPENCL': 'FALSE',
            'OPENCL_DEVICE_ID': 2
        })
        opts.add(new_opts)
        opts_list = opts.compose()
        self.assertTrue('STAN_OPENCL=FALSE' in opts_list)
        self.assertTrue('OPENCL_DEVICE_ID=2' in opts_list)

        expect = 'STANCFLAGS+=--include_paths=' + DATAFILES_PATH.replace(
            '\\', '/')
        stanc_opts2 = {'include_paths': DATAFILES_PATH}
        new_opts2 = CompilerOptions(stanc_options=stanc_opts2)
        opts.add(new_opts2)
        opts_list = opts.compose()
        self.assertTrue(expect in opts_list)

        path2 = os.path.join(HERE, 'data', 'optimize')
        expect = 'STANCFLAGS+=--include_paths=' + ','.join(
            [DATAFILES_PATH, path2]).replace('\\', '/')
        stanc_opts3 = {'include_paths': path2}
        new_opts3 = CompilerOptions(stanc_options=stanc_opts3)
        opts.add(new_opts3)
        opts_list = opts.compose()
        self.assertTrue(expect in opts_list)
    def test_user_header(self):
        header_file = os.path.join(DATAFILES_PATH, 'return_one.hpp')
        opts = CompilerOptions(user_header=header_file)
        opts.validate()
        self.assertTrue(opts.stanc_options['allow_undefined'])

        bad = os.path.join(DATAFILES_PATH, 'nonexistant.hpp')
        opts = CompilerOptions(user_header=bad)
        with self.assertRaisesRegex(ValueError, "cannot be found"):
            opts.validate()

        bad_dir = os.path.join(DATAFILES_PATH, 'optimize')
        opts = CompilerOptions(user_header=bad_dir)
        with self.assertRaisesRegex(ValueError, "cannot be found"):
            opts.validate()

        non_header = os.path.join(DATAFILES_PATH, 'bernoulli.stan')
        opts = CompilerOptions(user_header=non_header)
        with self.assertRaisesRegex(ValueError, "must end in .hpp"):
            opts.validate()

        header_file = os.path.join(DATAFILES_PATH, 'return_one.hpp')
        opts = CompilerOptions(user_header=header_file,
                               cpp_options={'USER_HEADER': 'foo'})
        with self.assertRaisesRegex(ValueError, "Disagreement"):
            opts.validate()
    def test_opts_cpp(self):
        cpp_opts = {}
        opts = CompilerOptions(cpp_options=cpp_opts)
        opts.validate()
        self.assertEqual(opts.compose(), [])

        cpp_opts['STAN_MPI'] = 'TRUE'
        opts = CompilerOptions(cpp_options=cpp_opts)
        opts.validate()
        self.assertEqual(opts.compose(), ['STAN_MPI=TRUE'])
    def test_opts_cpp(self):
        cpp_opts = {}
        opts = CompilerOptions(cpp_options=cpp_opts)
        opts.validate()
        self.assertEqual(opts.compose(), [])

        cpp_opts['STAN_MPI'] = 'TRUE'
        opts = CompilerOptions(cpp_options=cpp_opts)
        opts.validate()
        self.assertEqual(opts.compose(), ['STAN_MPI=TRUE'])

        cpp_opts['STAN_BAD'] = 'TRUE'
        opts = CompilerOptions(cpp_options=cpp_opts)
        with self.assertRaises(ValueError):
            opts.validate()
    def test_opts_stanc_includes(self):
        path2 = os.path.join(HERE, 'data', 'optimize')
        paths_str = ','.join([DATAFILES_PATH, path2]).replace('\\', '/')
        expect = 'STANCFLAGS+=--include_paths=' + paths_str

        stanc_opts = {'include_paths': paths_str}
        opts = CompilerOptions(stanc_options=stanc_opts)
        opts.validate()
        opts_list = opts.compose()
        self.assertTrue(expect in opts_list)

        stanc_opts = {'include_paths': [DATAFILES_PATH, path2]}
        opts = CompilerOptions(stanc_options=stanc_opts)
        opts.validate()
        opts_list = opts.compose()
        self.assertTrue(expect in opts_list)
 def test_opts_stanc_opencl(self):
     stanc_opts = {}
     stanc_opts['use-opencl'] = 'foo'
     opts = CompilerOptions(stanc_options=stanc_opts)
     opts.validate()
     self.assertEqual(opts.compose(),
                      ['STANCFLAGS+=--use-opencl', 'STAN_OPENCL=TRUE'])
    def test_opts_empty_eq(self):
        opts_a = CompilerOptions()
        self.assertTrue(opts_a.is_empty())

        opts_b = None
        self.assertTrue(opts_a == opts_b)

        opts_c = CompilerOptions(stanc_options={'--O'})
        self.assertTrue(opts_a != opts_c != opts_b)

        stanc_opts = {}
        cpp_opts = {'STAN_THREADS': 'T'}
        opts_c = CompilerOptions(stanc_options=stanc_opts,
                                 cpp_options=cpp_opts)
        self.assertFalse(opts_c.is_empty())
        self.assertFalse(opts_a == opts_c)
    def test_opts_empty(self):
        opts = CompilerOptions()
        opts.validate()
        self.assertEqual(opts.compose(), [])
        self.assertEqual(opts.__repr__(), 'stanc_options={}, cpp_options={}')

        stanc_opts = {}
        opts = CompilerOptions(stanc_options=stanc_opts)
        opts.validate()
        self.assertEqual(opts.compose(), [])

        cpp_opts = {}
        opts = CompilerOptions(cpp_options=cpp_opts)
        opts.validate()
        self.assertEqual(opts.compose(), [])

        opts = CompilerOptions(stanc_options=stanc_opts, cpp_options=cpp_opts)
        opts.validate()
        self.assertEqual(opts.compose(), [])
        self.assertEqual(opts.__repr__(), 'stanc_options={}, cpp_options={}')
    def test_opts_stanc(self):
        stanc_opts = {}
        opts = CompilerOptions()
        opts.validate()
        self.assertEqual(opts.compose(), [])

        opts = CompilerOptions(stanc_options=stanc_opts)
        opts.validate()
        self.assertEqual(opts.compose(), [])

        stanc_opts['warn-uninitialized'] = True
        opts = CompilerOptions(stanc_options=stanc_opts)
        opts.validate()
        self.assertEqual(opts.compose(), ['STANCFLAGS+=--warn-uninitialized'])

        stanc_opts['name'] = 'foo'
        opts = CompilerOptions(stanc_options=stanc_opts)
        opts.validate()
        self.assertEqual(
            opts.compose(),
            ['STANCFLAGS+=--warn-uninitialized', 'STANCFLAGS+=--name=foo'],
        )
    def test_opts_add_include_paths(self):
        expect = 'STANCFLAGS+=--include_paths=' + DATAFILES_PATH.replace(
            '\\', '/')
        stanc_opts = {'warn-uninitialized': True}
        opts = CompilerOptions(stanc_options=stanc_opts)
        opts.validate()
        opts_list = opts.compose()
        self.assertTrue(expect not in opts_list)

        opts.add_include_path(DATAFILES_PATH)
        opts.validate()
        opts_list = opts.compose()
        self.assertTrue(expect in opts_list)

        path2 = os.path.join(HERE, 'data', 'optimize')
        paths_str = ','.join([DATAFILES_PATH, path2]).replace('\\', '/')
        expect = 'STANCFLAGS+=--include_paths=' + paths_str
        opts.add_include_path(path2)
        opts.validate()
        opts_list = opts.compose()
        self.assertTrue(expect in opts_list)
Beispiel #12
0
    def __init__(
        self,
        model_name: str = None,
        stan_file: str = None,
        exe_file: str = None,
        compile: bool = True,
        stanc_options: Dict = None,
        cpp_options: Dict = None,
        logger: logging.Logger = None,
    ) -> None:
        """
        Initialize object given constructor args.

        :param model_name: Model name, used for output file names.
        :param stan_file: Path to Stan program file.
        :param exe_file: Path to compiled executable file.
        :param compile: Whether or not to compile the model.
        :param stanc_options: Options for stanc compiler.
        :param cpp_options: Options for C++ compiler.
        :param logger: Python logger object.
        """
        self._name = None
        self._stan_file = None
        self._exe_file = None
        self._compiler_options = CompilerOptions(stanc_options=stanc_options,
                                                 cpp_options=cpp_options)
        self._logger = logger or get_logger()

        if model_name is not None:
            if not model_name.strip():
                raise ValueError(
                    'Invalid value for argument model name, found "{}"'.format(
                        model_name))
            self._name = model_name.strip()

        if stan_file is None:
            if exe_file is None:
                raise ValueError(
                    'Missing model file arguments, you must specify '
                    'either Stan source or executable program file or both.')
        else:
            self._stan_file = os.path.realpath(os.path.expanduser(stan_file))
            if not os.path.exists(self._stan_file):
                raise ValueError('no such file {}'.format(self._stan_file))
            _, filename = os.path.split(stan_file)
            if len(filename) < 6 or not filename.endswith('.stan'):
                raise ValueError('invalid stan filename {}'.format(
                    self._stan_file))
            if self._name is None:
                self._name, _ = os.path.splitext(filename)
            # if program has include directives, record path
            with open(self._stan_file, 'r') as fd:
                program = fd.read()
            if '#include' in program:
                path, _ = os.path.split(self._stan_file)
                if self._compiler_options is None:
                    self._compiler_options = CompilerOptions(
                        stanc_options={'include_paths': [path]})
                elif self._compiler_options._stanc_options is None:
                    self._compiler_options._stanc_options = {
                        'include_paths': [path]
                    }
                else:
                    self._compiler_options.add_include_path(path)

        if exe_file is not None:
            self._exe_file = os.path.realpath(os.path.expanduser(exe_file))
            if not os.path.exists(self._exe_file):
                raise ValueError('no such file {}'.format(self._exe_file))
            _, exename = os.path.split(self._exe_file)
            if self._name is None:
                self._name, _ = os.path.splitext(exename)
            else:
                if self._name != os.path.splitext(exename)[0]:
                    raise ValueError(
                        'Name mismatch between Stan file and compiled'
                        ' executable, expecting basename: {}'
                        ' found: {}.'.format(self._name, exename))

        if self._compiler_options is not None:
            self._compiler_options.validate()

        if platform.system() == 'Windows':
            # Add tbb to the $PATH on Windows
            libtbb = os.environ.get('STAN_TBB')
            if libtbb is None:
                libtbb = os.path.join(cmdstan_path(), 'stan', 'lib',
                                      'stan_math', 'lib', 'tbb')
            os.environ['PATH'] = ';'.join(
                list(
                    OrderedDict.fromkeys(
                        [libtbb] + os.environ.get('PATH', '').split(';'))))

        if compile and self._exe_file is None:
            self.compile()
            if self._exe_file is None:
                raise ValueError(
                    'Unable to compile Stan model file: {}.'.format(
                        self._stan_file))
Beispiel #13
0
    def compile(
        self,
        force: bool = False,
        stanc_options: Dict = None,
        cpp_options: Dict = None,
        override_options: bool = False,
    ) -> None:
        """
        Compile the given Stan program file.  Translates the Stan code to
        C++, then calls the C++ compiler.

        By default, this function compares the timestamps on the source and
        executable files; if the executable is newer than the source file, it
        will not recompile the file, unless argument ``force`` is ``True``.

        :param force: When ``True``, always compile, even if the executable file
            is newer than the source file.  Used for Stan models which have
            ``#include`` directives in order to force recompilation when changes
            are made to the included files.

        :param stanc_options: Options for stanc compiler.
        :param cpp_options: Options for C++ compiler.

        :param override_options: When ``True``, override existing option.
            When ``False``, add/replace existing options.  Default is ``False``.
        """
        if not self._stan_file:
            raise RuntimeError('Please specify source file')

        compiler_options = None
        if not (stanc_options is None and cpp_options is None):
            compiler_options = CompilerOptions(stanc_options=stanc_options,
                                               cpp_options=cpp_options)
            compiler_options.validate()
            if self._compiler_options is None:
                self._compiler_options = compiler_options
            elif override_options:
                self._compiler_options = compiler_options
            else:
                self._compiler_options.add(compiler_options)

        compilation_failed = False
        with TemporaryCopiedFile(self._stan_file) as (stan_file, is_copied):
            exe_file, _ = os.path.splitext(os.path.abspath(stan_file))
            exe_file = Path(exe_file).as_posix() + EXTENSION
            do_compile = True
            if os.path.exists(exe_file):
                src_time = os.path.getmtime(self._stan_file)
                exe_time = os.path.getmtime(exe_file)
                if exe_time > src_time and not force:
                    do_compile = False
                    self._logger.info('found newer exe file, not recompiling')

            if do_compile:
                self._logger.info('compiling stan program, exe file: %s',
                                  exe_file)
                if self._compiler_options is not None:
                    self._compiler_options.validate()
                    self._logger.info('compiler options: %s',
                                      self._compiler_options)
                make = os.getenv(
                    'MAKE',
                    'make'
                    if platform.system() != 'Windows' else 'mingw32-make',
                )
                cmd = [make]
                if self._compiler_options is not None:
                    cmd.extend(self._compiler_options.compose())
                cmd.append(Path(exe_file).as_posix())
                try:
                    do_command(cmd, cmdstan_path(), logger=self._logger)
                except RuntimeError as e:
                    self._logger.error('file %s, exception %s', stan_file,
                                       str(e))
                    compilation_failed = True

            if not compilation_failed:
                if is_copied:
                    original_target_dir = os.path.dirname(
                        os.path.abspath(self._stan_file))
                    new_exec_name = (os.path.basename(
                        os.path.splitext(self._stan_file)[0]) + EXTENSION)
                    self._exe_file = os.path.join(original_target_dir,
                                                  new_exec_name)
                    shutil.copy(exe_file, self._exe_file)
                else:
                    self._exe_file = exe_file
                self._logger.info('compiled model file: %s', self._exe_file)
            else:
                self._logger.error('model compilation failed')
 def test_opts_stanc_ignore(self):
     stanc_opts = {}
     stanc_opts['auto-format'] = True
     opts = CompilerOptions(stanc_options=stanc_opts)
     opts.validate()
     self.assertEqual(opts.compose(), [])