2019-05-21 18:40:04 +00:00
|
|
|
# Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
|
|
|
|
|
|
# This source code is licensed under the MIT license found in the
|
|
|
|
|
# LICENSE file in the root directory of this source tree.
|
|
|
|
|
|
2021-10-03 01:03:03 +00:00
|
|
|
import os
|
2017-02-22 23:59:43 +00:00
|
|
|
import sys
|
2022-04-04 14:06:59 +00:00
|
|
|
import platform
|
2021-10-03 01:03:03 +00:00
|
|
|
from pathlib import Path
|
|
|
|
|
from shutil import copy, copytree, rmtree
|
|
|
|
|
from typing import List
|
|
|
|
|
|
|
|
|
|
from pkg_resources import add_activation_listener, normalize_path, require, working_set
|
|
|
|
|
from setuptools import find_packages, setup, Extension
|
|
|
|
|
from setuptools.command.build_ext import build_ext
|
2017-02-22 23:59:43 +00:00
|
|
|
from setuptools.command.build_py import build_py
|
2017-03-16 15:49:02 +00:00
|
|
|
from setuptools.command.develop import develop
|
2017-02-22 23:59:43 +00:00
|
|
|
from setuptools.command.test import test as test_command
|
2017-03-16 15:49:02 +00:00
|
|
|
|
|
|
|
|
|
2022-04-04 14:06:59 +00:00
|
|
|
MODEL_DIR = "stan"
|
2021-10-03 01:03:03 +00:00
|
|
|
MODEL_TARGET_DIR = os.path.join("prophet", "stan_model")
|
2017-03-16 15:49:02 +00:00
|
|
|
|
2021-10-03 01:03:03 +00:00
|
|
|
CMDSTAN_VERSION = "2.26.1"
|
|
|
|
|
BINARIES_DIR = "bin"
|
|
|
|
|
BINARIES = ["diagnose", "print", "stanc", "stansummary"]
|
|
|
|
|
TBB_PARENT = "stan/lib/stan_math/lib"
|
|
|
|
|
TBB_DIRS = ["tbb", "tbb_2019_U8"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def prune_cmdstan(cmdstan_dir: str) -> None:
|
|
|
|
|
"""
|
|
|
|
|
Keep only the cmdstan executables and tbb files (minimum required to run a cmdstanpy commands on a pre-compiled model).
|
|
|
|
|
"""
|
|
|
|
|
original_dir = Path(cmdstan_dir).resolve()
|
|
|
|
|
parent_dir = original_dir.parent
|
|
|
|
|
temp_dir = parent_dir / "temp"
|
|
|
|
|
if temp_dir.is_dir():
|
|
|
|
|
rmtree(temp_dir)
|
|
|
|
|
temp_dir.mkdir()
|
|
|
|
|
|
2022-04-04 14:06:59 +00:00
|
|
|
print("Copying ", original_dir, " to ", temp_dir, " for pruning")
|
2021-10-03 01:03:03 +00:00
|
|
|
copytree(original_dir / BINARIES_DIR, temp_dir / BINARIES_DIR)
|
|
|
|
|
for f in (temp_dir / BINARIES_DIR).iterdir():
|
|
|
|
|
if f.is_dir():
|
|
|
|
|
rmtree(f)
|
|
|
|
|
elif f.is_file() and f.stem not in BINARIES:
|
|
|
|
|
os.remove(f)
|
|
|
|
|
for tbb_dir in TBB_DIRS:
|
|
|
|
|
copytree(original_dir / TBB_PARENT / tbb_dir, temp_dir / TBB_PARENT / tbb_dir)
|
|
|
|
|
|
|
|
|
|
rmtree(original_dir)
|
|
|
|
|
temp_dir.rename(original_dir)
|
|
|
|
|
|
2022-04-04 14:06:59 +00:00
|
|
|
def repackage_cmdstan():
|
|
|
|
|
return os.environ.get("PROPHET_REPACKAGE_CMDSTAN", "").lower() not in ["false", "0"]
|
2020-02-07 22:34:08 +00:00
|
|
|
|
2021-10-03 23:16:55 +00:00
|
|
|
|
2022-04-04 14:06:59 +00:00
|
|
|
def maybe_install_cmdstan_toolchain():
|
2021-12-25 12:20:24 +00:00
|
|
|
"""Install C++ compilers required to build stan models on Windows machines."""
|
2022-04-04 14:06:59 +00:00
|
|
|
import cmdstanpy
|
|
|
|
|
try:
|
|
|
|
|
cmdstanpy.utils.cxx_toolchain_path()
|
|
|
|
|
except Exception:
|
2022-07-07 22:19:56 +00:00
|
|
|
try:
|
|
|
|
|
from cmdstanpy.install_cxx_toolchain import run_rtools_install
|
|
|
|
|
except ImportError:
|
|
|
|
|
# older versions
|
|
|
|
|
from cmdstanpy.install_cxx_toolchain import main as run_rtools_install
|
|
|
|
|
|
|
|
|
|
run_rtools_install({"version": None, "dir": None, "verbose": True})
|
2022-04-04 14:06:59 +00:00
|
|
|
cmdstanpy.utils.cxx_toolchain_path()
|
2021-10-03 01:03:03 +00:00
|
|
|
|
2021-12-25 12:20:24 +00:00
|
|
|
|
|
|
|
|
def install_cmdstan_deps(cmdstan_dir: Path):
|
2021-10-03 01:03:03 +00:00
|
|
|
import cmdstanpy
|
2022-04-04 14:06:59 +00:00
|
|
|
from multiprocessing import cpu_count
|
|
|
|
|
|
|
|
|
|
if repackage_cmdstan():
|
|
|
|
|
if platform.platform().startswith("Win"):
|
|
|
|
|
maybe_install_cmdstan_toolchain()
|
|
|
|
|
print("Installing cmdstan to", cmdstan_dir)
|
|
|
|
|
if os.path.isdir(cmdstan_dir):
|
|
|
|
|
rmtree(cmdstan_dir)
|
|
|
|
|
|
|
|
|
|
if not cmdstanpy.install_cmdstan(
|
|
|
|
|
version=CMDSTAN_VERSION,
|
|
|
|
|
dir=cmdstan_dir.parent,
|
|
|
|
|
overwrite=True,
|
|
|
|
|
verbose=True,
|
|
|
|
|
cores=cpu_count(),
|
|
|
|
|
progress=True,
|
|
|
|
|
):
|
|
|
|
|
raise RuntimeError("CmdStan failed to install in repackaged directory")
|
2021-12-25 12:20:24 +00:00
|
|
|
|
2021-10-03 01:03:03 +00:00
|
|
|
|
2021-12-25 12:20:24 +00:00
|
|
|
def build_cmdstan_model(target_dir):
|
|
|
|
|
"""
|
|
|
|
|
Rebuild cmdstan in the build environment, then use this installation to compile the stan model.
|
|
|
|
|
The stan model is copied to {target_dir}/prophet_model.bin
|
|
|
|
|
The cmdstan files required to run cmdstanpy commands are copied to {target_dir}/cmdstan-{version}.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
target_dir: Directory to copy the compiled model executable and core cmdstan files to.
|
|
|
|
|
"""
|
|
|
|
|
import cmdstanpy
|
2022-04-04 14:06:59 +00:00
|
|
|
|
2021-12-25 12:20:24 +00:00
|
|
|
cmdstan_dir = (Path(target_dir) / f"cmdstan-{CMDSTAN_VERSION}").resolve()
|
|
|
|
|
install_cmdstan_deps(cmdstan_dir)
|
2021-10-03 01:03:03 +00:00
|
|
|
model_name = "prophet.stan"
|
|
|
|
|
target_name = "prophet_model.bin"
|
|
|
|
|
sm = cmdstanpy.CmdStanModel(stan_file=os.path.join(MODEL_DIR, model_name))
|
|
|
|
|
copy(sm.exe_file, os.path.join(target_dir, target_name))
|
2022-04-04 14:06:59 +00:00
|
|
|
|
2021-10-03 01:03:03 +00:00
|
|
|
# Clean up
|
|
|
|
|
for f in Path(MODEL_DIR).iterdir():
|
|
|
|
|
if f.is_file() and f.name != model_name:
|
|
|
|
|
os.remove(f)
|
2022-04-04 14:06:59 +00:00
|
|
|
|
|
|
|
|
if repackage_cmdstan():
|
|
|
|
|
prune_cmdstan(cmdstan_dir)
|
2021-10-03 01:03:03 +00:00
|
|
|
|
2021-10-03 23:16:55 +00:00
|
|
|
|
|
|
|
|
|
2021-10-03 01:03:03 +00:00
|
|
|
def get_backends_from_env() -> List[str]:
|
2022-04-04 14:06:59 +00:00
|
|
|
return os.environ.get("STAN_BACKEND", "CMDSTANPY").split(",")
|
2020-02-07 22:34:08 +00:00
|
|
|
|
2021-10-03 23:16:55 +00:00
|
|
|
|
2020-02-07 22:34:08 +00:00
|
|
|
def build_models(target_dir):
|
2022-09-04 22:31:33 +00:00
|
|
|
print("Compiling cmdstanpy model")
|
2022-05-05 07:37:17 +00:00
|
|
|
build_cmdstan_model(target_dir)
|
2017-03-16 15:49:02 +00:00
|
|
|
|
2022-05-05 07:37:17 +00:00
|
|
|
if 'PYSTAN' in get_backends_from_env():
|
|
|
|
|
raise ValueError("PyStan backend is not supported for Prophet >= 1.1")
|
2017-03-16 15:49:02 +00:00
|
|
|
|
2017-02-22 23:59:43 +00:00
|
|
|
class BuildPyCommand(build_py):
|
|
|
|
|
"""Custom build command to pre-compile Stan models."""
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
if not self.dry_run:
|
2018-05-04 23:04:29 +00:00
|
|
|
target_dir = os.path.join(self.build_lib, MODEL_TARGET_DIR)
|
2017-03-16 15:49:02 +00:00
|
|
|
self.mkpath(target_dir)
|
2020-02-07 22:34:08 +00:00
|
|
|
build_models(target_dir)
|
2017-02-22 23:59:43 +00:00
|
|
|
|
|
|
|
|
build_py.run(self)
|
|
|
|
|
|
|
|
|
|
|
2021-10-03 01:03:03 +00:00
|
|
|
class BuildExtCommand(build_ext):
|
|
|
|
|
"""Ensure built extensions are added to the correct path in the wheel."""
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
2017-03-16 15:49:02 +00:00
|
|
|
class DevelopCommand(develop):
|
|
|
|
|
"""Custom develop command to pre-compile Stan models in-place."""
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
if not self.dry_run:
|
2018-05-04 23:04:29 +00:00
|
|
|
target_dir = os.path.join(self.setup_path, MODEL_TARGET_DIR)
|
2017-03-16 15:49:02 +00:00
|
|
|
self.mkpath(target_dir)
|
2020-02-07 22:34:08 +00:00
|
|
|
build_models(target_dir)
|
2017-03-16 15:49:02 +00:00
|
|
|
|
|
|
|
|
develop.run(self)
|
2017-03-09 16:47:59 +00:00
|
|
|
|
2017-02-22 23:59:43 +00:00
|
|
|
|
|
|
|
|
class TestCommand(test_command):
|
2020-02-07 22:34:08 +00:00
|
|
|
user_options = [
|
2021-10-03 01:03:03 +00:00
|
|
|
("test-module=", "m", "Run 'test_suite' in specified module"),
|
|
|
|
|
(
|
|
|
|
|
"test-suite=",
|
|
|
|
|
"s",
|
|
|
|
|
"Run single test, case or suite (e.g. 'module.test_suite')",
|
|
|
|
|
),
|
|
|
|
|
("test-runner=", "r", "Test runner to use"),
|
|
|
|
|
("test-slow", "w", "Test slow suites (default off)"),
|
2020-02-07 22:34:08 +00:00
|
|
|
]
|
2020-12-08 23:35:54 +00:00
|
|
|
test_slow = None
|
2020-02-07 22:34:08 +00:00
|
|
|
|
|
|
|
|
def initialize_options(self):
|
|
|
|
|
super(TestCommand, self).initialize_options()
|
|
|
|
|
self.test_slow = False
|
|
|
|
|
|
|
|
|
|
def finalize_options(self):
|
|
|
|
|
super(TestCommand, self).finalize_options()
|
|
|
|
|
if self.test_slow is None:
|
2021-10-03 01:03:03 +00:00
|
|
|
self.test_slow = getattr(self.distribution, "test_slow", False)
|
2020-02-07 22:34:08 +00:00
|
|
|
|
2017-02-22 23:59:43 +00:00
|
|
|
"""We must run tests on the build directory, not source."""
|
|
|
|
|
|
|
|
|
|
def with_project_on_sys_path(self, func):
|
|
|
|
|
# Ensure metadata is up-to-date
|
2021-10-03 01:03:03 +00:00
|
|
|
self.reinitialize_command("build_py", inplace=0)
|
|
|
|
|
self.run_command("build_py")
|
2017-02-22 23:59:43 +00:00
|
|
|
bpy_cmd = self.get_finalized_command("build_py")
|
|
|
|
|
build_path = normalize_path(bpy_cmd.build_lib)
|
|
|
|
|
|
|
|
|
|
# Build extensions
|
2021-10-03 01:03:03 +00:00
|
|
|
self.reinitialize_command("egg_info", egg_base=build_path)
|
|
|
|
|
self.run_command("egg_info")
|
2017-02-22 23:59:43 +00:00
|
|
|
|
2021-10-03 01:03:03 +00:00
|
|
|
self.reinitialize_command("build_ext", inplace=0)
|
|
|
|
|
self.run_command("build_ext")
|
2017-02-22 23:59:43 +00:00
|
|
|
|
|
|
|
|
ei_cmd = self.get_finalized_command("egg_info")
|
|
|
|
|
|
|
|
|
|
old_path = sys.path[:]
|
|
|
|
|
old_modules = sys.modules.copy()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
sys.path.insert(0, normalize_path(ei_cmd.egg_base))
|
|
|
|
|
working_set.__init__()
|
|
|
|
|
add_activation_listener(lambda dist: dist.activate())
|
2021-10-03 01:03:03 +00:00
|
|
|
require("%s==%s" % (ei_cmd.egg_name, ei_cmd.egg_version))
|
2017-02-22 23:59:43 +00:00
|
|
|
func()
|
|
|
|
|
finally:
|
|
|
|
|
sys.path[:] = old_path
|
|
|
|
|
sys.modules.clear()
|
|
|
|
|
sys.modules.update(old_modules)
|
|
|
|
|
working_set.__init__()
|
|
|
|
|
|
2021-10-03 01:03:03 +00:00
|
|
|
|
|
|
|
|
with open("README.md", "r", encoding="utf-8") as f:
|
2020-07-30 00:02:37 +00:00
|
|
|
long_description = f.read()
|
2020-02-07 22:34:08 +00:00
|
|
|
|
2021-10-03 01:03:03 +00:00
|
|
|
with open("requirements.txt", "r") as f:
|
2018-02-03 01:10:44 +00:00
|
|
|
install_requires = f.read().splitlines()
|
2017-08-10 18:14:23 +00:00
|
|
|
|
2022-09-04 20:15:28 +00:00
|
|
|
about = {}
|
|
|
|
|
here = Path(__file__).parent.resolve()
|
|
|
|
|
with open(here / "prophet" / "__version__.py", "r") as f:
|
|
|
|
|
exec(f.read(), about)
|
|
|
|
|
|
2017-02-22 23:59:43 +00:00
|
|
|
setup(
|
2022-09-04 20:15:28 +00:00
|
|
|
name=about["__title__"],
|
|
|
|
|
version=about["__version__"],
|
|
|
|
|
description=about["__description__"],
|
|
|
|
|
url=about["__url__"],
|
2022-05-24 11:51:05 +00:00
|
|
|
project_urls={
|
|
|
|
|
"Source": "https://github.com/facebook/prophet",
|
|
|
|
|
},
|
2022-09-04 20:15:28 +00:00
|
|
|
author=about["__author__"],
|
|
|
|
|
author_email=about["__author_email__"],
|
|
|
|
|
license=about["__license__"],
|
2018-12-21 02:15:07 +00:00
|
|
|
packages=find_packages(),
|
2018-02-03 01:10:44 +00:00
|
|
|
install_requires=install_requires,
|
2022-05-22 09:40:41 +00:00
|
|
|
python_requires=">=3.7",
|
2017-02-22 23:59:43 +00:00
|
|
|
zip_safe=False,
|
2018-12-21 00:11:53 +00:00
|
|
|
include_package_data=True,
|
2021-10-03 01:03:03 +00:00
|
|
|
ext_modules=[Extension("prophet.stan_model", [])],
|
2017-02-22 23:59:43 +00:00
|
|
|
cmdclass={
|
2021-10-03 01:03:03 +00:00
|
|
|
"build_ext": BuildExtCommand,
|
|
|
|
|
"build_py": BuildPyCommand,
|
|
|
|
|
"develop": DevelopCommand,
|
|
|
|
|
"test": TestCommand,
|
2017-02-22 23:59:43 +00:00
|
|
|
},
|
2021-10-03 01:03:03 +00:00
|
|
|
test_suite="prophet.tests",
|
2019-07-25 23:20:21 +00:00
|
|
|
classifiers=[
|
2021-10-03 23:16:55 +00:00
|
|
|
"Programming Language :: Python",
|
|
|
|
|
"Programming Language :: Python :: 3",
|
|
|
|
|
"Programming Language :: Python :: 3.7",
|
|
|
|
|
"Programming Language :: Python :: 3.8",
|
2022-05-22 09:40:41 +00:00
|
|
|
"Programming Language :: Python :: 3.9",
|
|
|
|
|
"Programming Language :: Python :: 3.10",
|
2020-02-07 22:34:08 +00:00
|
|
|
],
|
2020-07-30 00:02:37 +00:00
|
|
|
long_description=long_description,
|
2021-10-03 01:03:03 +00:00
|
|
|
long_description_content_type="text/markdown",
|
2017-02-22 23:59:43 +00:00
|
|
|
)
|