예제 #1
0
    def test_non_spaces_location(self):
        good_path = os.path.join(TMPDIR, 'good_dir')
        with TemporaryCopiedFile(good_path) as (p, is_changed):
            self.assertEqual(p, good_path)
            self.assertFalse(is_changed)

        # prepare files for test
        bad_path = os.path.join(TMPDIR, 'bad dir')
        os.makedirs(bad_path, exist_ok=True)
        stan = os.path.join(datafiles_path, 'bernoulli.stan')
        stan_bad = os.path.join(bad_path, 'bad name.stan')
        shutil.copy(stan, stan_bad)

        stan_copied = None
        try:
            with TemporaryCopiedFile(stan_bad) as (p, is_changed):
                stan_copied = p
                self.assertTrue(os.path.exists(stan_copied))
                self.assertTrue(' ' not in stan_copied)
                self.assertTrue(is_changed)
                raise RuntimeError
        except RuntimeError:
            pass

        if platform.system() != 'Windows':
            self.assertFalse(os.path.exists(stan_copied))

        # cleanup after test
        shutil.rmtree(bad_path, ignore_errors=True)
예제 #2
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')
예제 #3
0
    def compile(
        self,
        opt_lvl: int = 2,
        overwrite: bool = False,
        include_paths: List[str] = None,
    ) -> None:
        """
        Compile the given Stan program file.  Translates the Stan code to
        C++, then calls the C++ compiler.

        :param opt_lvl: Optimization level used by the C++ compiler, one of
            {0, 1, 2, 3}.  Defaults to level 2. Level 0 optimization results
            in the shortest compilation time with code that may run slowly.
            Higher optimization levels increase runtime performance but will
            take longer to compile.

        :param overwrite: When True, existing executable will be overwritten.
            Defaults to False.

        :param include_paths: List of paths to directories where Stan should
            look for files to include in compilation of the C++ executable.
        """
        if not self._stan_file:
            raise RuntimeError('Please specify source file')

        if self._exe_file is not None and not overwrite:
            self._logger.warning('model is already compiled')
            return

        with TemporaryCopiedFile(self._stan_file) as (stan_file, is_copied):
            hpp_file = os.path.splitext(stan_file)[0] + '.hpp'
            hpp_file = Path(hpp_file).as_posix()
            if overwrite or not os.path.exists(hpp_file):
                self._logger.info('stan to c++ (%s)', hpp_file)
                stanc_path = os.path.join(cmdstan_path(), 'bin',
                                          'stanc' + EXTENSION)
                stanc_path = Path(stanc_path).as_posix()
                cmd = [
                    stanc_path,
                    '--o={}'.format(hpp_file),
                    Path(stan_file).as_posix(),
                ]
                if include_paths is not None:
                    bad_paths = [
                        d for d in include_paths if not os.path.exists(d)
                    ]
                    if any(bad_paths):
                        raise Exception('invalid include paths: {}'.format(
                            ', '.join(bad_paths)))
                    cmd.append('--include_paths=' +
                               ','.join((Path(p).as_posix()
                                         for p in include_paths)))

                do_command(cmd, logger=self._logger)
                if not os.path.exists(hpp_file):
                    raise Exception('syntax error'.format(stan_file))

            exe_file, _ = os.path.splitext(os.path.abspath(stan_file))
            exe_file = Path(exe_file).as_posix()
            exe_file += EXTENSION
            make = os.getenv('MAKE', 'make')
            cmd = [make, 'O={}'.format(opt_lvl), exe_file]
            self._logger.info('compiling c++')
            try:
                do_command(cmd, cmdstan_path(), self._logger)
            except Exception as e:
                self._logger.error('make cmd failed %s', e)

            if is_copied:

                original_target_dir = os.path.dirname(self._stan_file)
                # reconstruct the output file name
                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)

                # copy the generated file back to the original directory
                shutil.copy(exe_file, self._exe_file)
            else:
                self._exe_file = exe_file

        self._logger.info('compiled model file: %s', self._exe_file)
예제 #4
0
    def compile(self, opt_lvl: int = 3, force: 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 opt_lvl: Optimization level used by the C++ compiler, one of
            {0, 1, 2, 3}.  Defaults to level 2. Level 0 optimization results
            in the shortest compilation time with code that may run slowly.
            Higher optimization levels increase runtime performance but will
            take longer to compile.

        :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.
        """
        if not self._stan_file:
            raise RuntimeError('Please specify source file')

        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()
            exe_file += 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:
                make = os.getenv(
                    'MAKE',
                    'make'
                    if platform.system() != 'Windows'
                    else 'mingw32-make',
                )
                hpp_file = os.path.splitext(stan_file)[0] + '.hpp'
                hpp_file = Path(hpp_file).as_posix()
                if not os.path.exists(hpp_file):
                    self._logger.info('stan to c++ (%s)', hpp_file)
                    cmd = [
                        make,
                        Path(exe_file).as_posix(),
                        'STANCFLAGS+=--o={}'.format(hpp_file),
                    ]
                    if self._include_paths is not None:
                        bad_paths = [
                            d
                            for d in self._include_paths
                            if not os.path.exists(d)
                        ]
                        if any(bad_paths):
                            raise ValueError(
                                'invalid include paths: {}'.format(
                                    ', '.join(bad_paths)
                                )
                            )
                        cmd.append(
                            'STANCFLAGS+=--include_paths='
                            + ','.join(
                                (
                                    Path(p).as_posix()
                                    for p in self._include_paths
                                )
                            )
                        )
                    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:
                    cmd = [make, 'O={}'.format(opt_lvl), exe_file]
                    self._logger.info('compiling c++')
                    try:
                        do_command(cmd, cmdstan_path(), logger=self._logger)
                    except RuntimeError as e:
                        self._logger.error('make cmd failed %s', repr(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')