Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft] Python Wheels for PyPi #2010

Merged
merged 47 commits into from
Oct 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
983e08b
init
tcuongd Aug 28, 2021
042c912
allow manual run:
tcuongd Aug 28, 2021
a156cfd
temp triggers for testing
tcuongd Aug 28, 2021
50e8ca6
ensure pyproject.toml is used
tcuongd Aug 28, 2021
40e8f8f
fixes
tcuongd Aug 28, 2021
85f75c0
prophet namespace
tcuongd Aug 28, 2021
a6beac7
fix
tcuongd Aug 28, 2021
44e7eb3
fix
tcuongd Aug 28, 2021
2f9b7ff
expose tests
tcuongd Aug 28, 2021
ab845cd
clarify setup
tcuongd Aug 28, 2021
765f71a
fix
tcuongd Aug 28, 2021
f59468c
use cibuildwheel
tcuongd Aug 28, 2021
7b7f2b7
set cibw options
tcuongd Aug 28, 2021
523d9a2
dont attempt windows wheel
tcuongd Aug 28, 2021
37dd50d
add cmdstanpy to the wheel
tcuongd Aug 28, 2021
292705c
remove unnecessary cmdstan files from wheel
tcuongd Aug 29, 2021
79c6f45
fix linux repair
tcuongd Aug 29, 2021
65fea9d
simplify setup.py
tcuongd Aug 29, 2021
ee1abe7
cache cmdstan files, make linux aware of extension modules, prune cmd…
tcuongd Sep 4, 2021
4d45c59
cleaner prune function, use cache for pip as well
tcuongd Sep 4, 2021
c09212b
create .cmdstan dir first
tcuongd Sep 4, 2021
53e24d9
clean up and fix bugs in setup.py
tcuongd Sep 4, 2021
1bac7e8
cmdstan cache on ci includes os version
tcuongd Sep 4, 2021
144929e
typos
tcuongd Sep 4, 2021
a19d7ef
fix environment variables in docker containers
tcuongd Sep 5, 2021
b84672a
fix env vars
tcuongd Sep 5, 2021
52b8047
explicitly list home in env
tcuongd Sep 5, 2021
6ecd1ff
.
tcuongd Sep 5, 2021
4ff2707
run python command instead of using actions pkg
tcuongd Sep 5, 2021
e8d6bf0
fix absolute path
tcuongd Sep 5, 2021
1d2aca2
/host mount only needed for Linux
tcuongd Sep 5, 2021
d33cbad
.
tcuongd Sep 5, 2021
1bbb065
clean and build in py code
tcuongd Sep 5, 2021
22ea5e6
give env vars to linux, ensure perms
tcuongd Sep 5, 2021
292028a
reset cache, run with sudo so that Linux can write to pip cache
tcuongd Sep 5, 2021
4eeb943
revert
tcuongd Sep 5, 2021
e4fae99
build for py36 and py37 as well
tcuongd Sep 5, 2021
e20d88c
consistent usage of platform variable
tcuongd Sep 5, 2021
b7be6fb
clean up functions, write documentation, remove build_model from mode…
tcuongd Oct 2, 2021
a76d33a
Add unit tests to wheel building (#2020)
Oct 2, 2021
3552e94
parallel wheel builds, fix triggers
tcuongd Oct 2, 2021
b996ff1
Merge branch 'tcuongd-cmdstanpy-wheel' of https://github.com/facebook…
tcuongd Oct 2, 2021
3d6fade
update holidays req from master
tcuongd Oct 2, 2021
a971493
bring back build and test
tcuongd Oct 2, 2021
eabe76f
Merge branch 'master' into tcuongd-cmdstanpy-wheel
tcuongd Oct 2, 2021
461c050
import extension from setuptools
tcuongd Oct 2, 2021
180d081
use cache for build and test, clean up workflow yamls
tcuongd Oct 3, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
branches: [ master ]

env:
CMDSTAN_VERSION: "2.26.1"

jobs:
build-and-test-python:

Expand All @@ -21,16 +24,34 @@ jobs:
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: "Restore pip cache"
id: cache-pip
uses: actions/cache@v2
with:
path: $HOME/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/python/requirements.txt') }}-v1
restore-keys: |
${{ runner.os }}-pip-
- name: "Restore cmdstan cache"
id: cache-cmdstan
uses: actions/cache@v2
with:
path: $HOME/.cmdstan
key: ${{ runner.os }}-cmdstan-${{ env.CMDSTAN_VERSION }}-v1
- name: "Download cmdstan"
if: steps.cache-cmdstan.outputs.cache-hit != 'true'
run: |
wget https://github.com/stan-dev/cmdstan/releases/download/v${{ env.CMDSTAN_VERSION }}/cmdstan-${{ env.CMDSTAN_VERSION }}.tar.gz -O /tmp/cmdstan.tar.gz &> /dev/null
mkdir $HOME/.cmdstan
tar -xf /tmp/cmdstan.tar.gz -C $HOME/.cmdstan &> /dev/null
- name: Install and test
run: |
pip install -U -r python/requirements.txt dask[dataframe] distributed
cd python && python setup.py develop test
cd python
STAN_BACKEND=PYSTAN python setup.py develop test
python setup.py clean
rm -rf prophet/stan_model
wget https://github.com/stan-dev/cmdstan/releases/download/v2.26.1/cmdstan-2.26.1.tar.gz -O /tmp/cmdstan.tar.gz > /dev/null
tar -xvf /tmp/cmdstan.tar.gz -C /tmp > /dev/null
make -C /tmp/cmdstan-2.26.1/ build > /dev/null
CMDSTAN=/tmp/cmdstan-2.26.1 STAN_BACKEND=CMDSTANPY python setup.py develop test
STAN_BACKEND=CMDSTANPY python setup.py develop test

build-and-test-r:

Expand Down
106 changes: 106 additions & 0 deletions .github/workflows/wheel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: "Create Python Wheels"

on:
release:
types: [ created ]
workflow_dispatch: {}

env:
STAN_BACKEND: "PYSTAN,CMDSTANPY"
CMDSTAN_VERSION: "2.26.1"

jobs:
make-wheels-macos-linux:
name: ${{ matrix.python-version }}-${{ matrix.architecture }}-${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- "macos-latest"
- "ubuntu-latest"
python-version:
- "3.6"
- "3.7"
- "3.8"
architecture:
- x64

fail-fast: false

steps:
- name: "Get OS version (Linux)"
if: startsWith(runner.os, 'Linux')
run: |
echo "OS_VERSION=`lsb_release -sr`" >> $GITHUB_ENV
echo "PIP_DEFAULT_CACHE=$HOME/.cache/pip" >> $GITHUB_ENV
echo "DEFAULT_HOME=$HOME" >> $GITHUB_ENV

- name: "Get OS version (macOS)"
if: startsWith(runner.os, 'macOS')
run: |
echo "OS_VERSION=`sw_vers -productVersion`" >> $GITHUB_ENV
echo "PIP_DEFAULT_CACHE=$HOME/Library/Caches/pip" >> $GITHUB_ENV
echo "DEFAULT_HOME=$HOME" >> $GITHUB_ENV

- name: "Checkout repo"
uses: actions/checkout@v2

- name: "Set up Python"
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.architecture }}

- name: "Restore pip cache"
id: cache-pip
uses: actions/cache@v2
with:
path: ${{ env.PIP_DEFAULT_CACHE }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/python/requirements.txt') }}-v1
restore-keys: |
${{ runner.os }}-pip-

- name: "Install pip"
run: |
python -m pip install --upgrade pip
python -m pip install cibuildwheel build

- name: "Restore cmdstan cache"
id: cache-cmdstan
uses: actions/cache@v2
with:
path: ${{ env.DEFAULT_HOME }}/.cmdstan
key: ${{ runner.os }}-cmdstan-${{ env.CMDSTAN_VERSION }}-v1

- name: "Download cmdstan"
if: steps.cache-cmdstan.outputs.cache-hit != 'true'
run: |
wget https://github.com/stan-dev/cmdstan/releases/download/v${{ env.CMDSTAN_VERSION }}/cmdstan-${{ env.CMDSTAN_VERSION }}.tar.gz -O /tmp/cmdstan.tar.gz &> /dev/null
mkdir $HOME/.cmdstan
tar -xf /tmp/cmdstan.tar.gz -C $HOME/.cmdstan &> /dev/null

- name: "Build wheel"
run: |
cd python && python -m cibuildwheel --output-dir wheelhouse
env:
CIBW_ENVIRONMENT: >
STAN_BACKEND="${{ env.STAN_BACKEND }}"
CMDSTAN_VERSION=${{ env.CMDSTAN_VERSION }}
# Linux builds run in a Docker container, need to point the cache to the host machine.
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
CIBW_ENVIRONMENT_LINUX: >
STAN_BACKEND="${{ env.STAN_BACKEND }}"
CMDSTAN_VERSION=${{ env.CMDSTAN_VERSION }}
HOME="/host/${{ env.DEFAULT_HOME }}"
PIP_CACHE_DIR="/host/${{ env.PIP_DEFAULT_CACHE }}"
CIBW_BEFORE_ALL_LINUX: sudo chmod -R a+rwx ${{ env.PIP_DEFAULT_CACHE }}
CIBW_ARCHS: native
CIBW_BUILD_FRONTEND: build
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest --pyargs prophet

- name: "Upload wheel as artifact"
uses: actions/upload-artifact@v2
with:
name: ${{ matrix.os }}-wheel
path: "./**/*.whl"
45 changes: 11 additions & 34 deletions python/prophet/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from pathlib import Path
import pickle
import pkg_resources
import os

import logging
logger = logging.getLogger('prophet.models')
Expand Down Expand Up @@ -53,30 +52,20 @@ def fit(self, stan_init, stan_data, **kwargs) -> dict:
def sampling(self, stan_init, stan_data, samples, **kwargs) -> dict:
pass

@staticmethod
@abstractmethod
def build_model(target_dir, model_dir):
pass


class CmdStanPyBackend(IStanBackend):
CMDSTAN_VERSION = "2.26.1"
def __init__(self):
super().__init__()
import cmdstanpy
cmdstanpy.set_cmdstan_path(
pkg_resources.resource_filename("prophet", f"stan_model/cmdstan-{self.CMDSTAN_VERSION}")
)

@staticmethod
def get_type():
return StanBackendEnum.CMDSTANPY.name

@staticmethod
def build_model(target_dir, model_dir):
from shutil import copy
import cmdstanpy
model_name = 'prophet.stan'
target_name = 'prophet_model.bin'

sm = cmdstanpy.CmdStanModel(
stan_file=os.path.join(model_dir, model_name))
sm.compile()
copy(sm.exe_file, os.path.join(target_dir, target_name))

def load_model(self):
import cmdstanpy
model_file = pkg_resources.resource_filename(
Expand All @@ -87,7 +76,7 @@ def load_model(self):

def fit(self, stan_init, stan_data, **kwargs):
(stan_init, stan_data) = self.prepare_data(stan_init, stan_data)

if 'inits' not in kwargs and 'init' in kwargs:
kwargs['inits'] = self.prepare_data(kwargs['init'], stan_data)[0]

Expand Down Expand Up @@ -120,14 +109,13 @@ def fit(self, stan_init, stan_data, **kwargs):

def sampling(self, stan_init, stan_data, samples, **kwargs) -> dict:
(stan_init, stan_data) = self.prepare_data(stan_init, stan_data)

if 'inits' not in kwargs and 'init' in kwargs:
kwargs['inits'] = self.prepare_data(kwargs['init'], stan_data)[0]

args = dict(
data=stan_data,
inits=stan_init,
algorithm='Newton' if stan_data['T'] < 100 else 'LBFGS',
)

if 'chains' not in kwargs:
Expand All @@ -136,7 +124,7 @@ def sampling(self, stan_init, stan_data, samples, **kwargs) -> dict:
kwargs['iter_sampling'] = iter_half
if 'iter_warmup' not in kwargs:
kwargs['iter_warmup'] = iter_half

args.update(kwargs)

self.stan_fit = self.model.sample(**args)
Expand Down Expand Up @@ -181,7 +169,7 @@ def prepare_data(init, data) -> Tuple[dict, dict]:
'sigma_obs': init['sigma_obs']
}
return (cmdstanpy_init, cmdstanpy_data)

@staticmethod
def stan_to_dict_numpy(column_names: Tuple[str, ...], data: 'np.array'):
import numpy as np
Expand Down Expand Up @@ -235,17 +223,6 @@ class PyStanBackend(IStanBackend):
def get_type():
return StanBackendEnum.PYSTAN.name

@staticmethod
def build_model(target_dir, model_dir):
import pystan
model_name = 'prophet.stan'
target_name = 'prophet_model.pkl'
with open(os.path.join(model_dir, model_name)) as f:
model_code = f.read()
sm = pystan.StanModel(model_code=model_code)
with open(os.path.join(target_dir, target_name), 'wb') as f:
pickle.dump(sm, f, protocol=pickle.HIGHEST_PROTOCOL)

def sampling(self, stan_init, stan_data, samples, **kwargs) -> dict:

args = dict(
Expand Down
8 changes: 8 additions & 0 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[build-system]
requires = [
"setuptools>=42",
"wheel",
"pystan~=2.19.1.1",
"cmdstanpy==0.9.77",
]
build-backend = "setuptools.build_meta"
4 changes: 3 additions & 1 deletion python/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
Cython>=0.22
cmdstanpy==0.9.68
cmdstanpy==0.9.77
pystan~=2.19.1.1
numpy>=1.15.4
pandas>=1.0.4
matplotlib>=2.0.0
LunarCalendar>=0.0.9
convertdate>=2.1.2
holidays>=0.11.3.1
setuptools>=42
setuptools-git>=1.2
python-dateutil>=2.8.0
tqdm>=4.36.1
wheel>=0.37.0
Loading