Skip to content

Commit

Permalink
Build against Bazel's tensorflow.
Browse files Browse the repository at this point in the history
This project needs to compile and link against tensorflow-cpu. Before,
the tensorflow-cpu pip package had to be installed manually (e.g. in the
devcontainer) but now Bazel manages it as part of the build system.

This project now uses Bazel's hermetic python interpreter instead of
relying on what is installed on the host system. This prevents python
version mismatches which are a problem because cpython's ABI changes
across minor versions.
  • Loading branch information
james-choncholas committed Sep 27, 2023
1 parent 6711f52 commit 114afb2
Show file tree
Hide file tree
Showing 26 changed files with 2,648 additions and 561 deletions.
22 changes: 22 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
build --enable_bzlmod

build -c opt

build --spawn_strategy=standalone
build --strategy=Genrule=standalone
build --spawn_strategy=sandboxed

build --cxxopt='-std=c++17'
build --cxxopt='-D_GLIBCXX_USE_CXX11_ABI=1'

build:test --cxxopt='-DPYBIND11_ABSEIL_STATUS_MODULE_PATH=pybind11_abseil.pybind11_abseil.status'

build:release --cxxopt='-DPYBIND11_ABSEIL_STATUS_MODULE_PATH=pybind11_abseil.status'

build:asan --strip=never
build:asan --copt -fsanitize=address
build:asan --copt -DADDRESS_SANITIZER
build:asan --copt -O1
build:asan --copt -g
build:asan --copt -fno-omit-frame-pointer
build:asan --linkopt -fsanitize=address
4 changes: 3 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
"ms-python.python",
"ms-vscode.cpptools-extension-pack",
"BazelBuild.vscode-bazel",
"minherz.copyright-inserter"
"minherz.copyright-inserter",
"DavidAnson.vscode-markdownlint",
"yzhang.markdown-all-in-one"
]
}
},
Expand Down
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ bazel-bin
bazel-tf-shell
bazel-out
bazel-testlogs
requirements.txt
__pycache__
.bazelrc
**.venv
*.whl
MODULE.bazel.lock
MODULE.bazel.lock
61 changes: 54 additions & 7 deletions BUILD
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
load("//:version.bzl", "VERSION_LABEL")
load("@rules_python//python:pip.bzl", "compile_pip_requirements")
load("@python_versions//3.8:defs.bzl", compile_pip_requirements_3_8 = "compile_pip_requirements")
load("@python_versions//3.8:defs.bzl", compile_pip_requirements_3_9 = "compile_pip_requirements")
load("@python_versions//3.8:defs.bzl", compile_pip_requirements_3_10 = "compile_pip_requirements")
load("@python_versions//3.8:defs.bzl", compile_pip_requirements_3_11 = "compile_pip_requirements")

load("@pip//:requirements.bzl", "requirement")
load("@rules_python//python:defs.bzl", "py_binary")
load("@rules_python//python:packaging.bzl", "py_wheel")
Expand All @@ -9,16 +13,43 @@ exports_files([
"LICENSE",
"setup.py",
"requirements.in",
"requirements.txt",
"requirements_3_8.txt",
"requirements_3_9.txt",
"requirements_3_10.txt",
"requirements_3_11.txt",
"README.md",
"DESCRIPTION.md",
])

compile_pip_requirements(
name = "requirements",
compile_pip_requirements_3_8(
name = "requirements_3_8",
extra_args = ["--allow-unsafe"], # need setuptools
requirements_in = "//:requirements.in",
requirements_txt = "//:requirements_3_8.txt",
visibility = ["//visibility:public"],
)

compile_pip_requirements_3_9(
name = "requirements_3_9",
extra_args = ["--allow-unsafe"], # need setuptools
requirements_in = "//:requirements.in",
requirements_txt = "//:requirements_3_9.txt",
visibility = ["//visibility:public"],
)

compile_pip_requirements_3_10(
name = "requirements_3_10",
extra_args = ["--allow-unsafe"], # need setuptools
requirements_in = "//:requirements.in",
requirements_txt = "//:requirements_3_10.txt",
visibility = ["//visibility:public"],
)

compile_pip_requirements_3_11(
name = "requirements_3_11",
extra_args = ["--allow-unsafe"], # need setuptools
requirements_in = "//:requirements.in",
requirements_txt = "//:requirements.txt",
requirements_txt = "//:requirements_3_11.txt",
visibility = ["//visibility:public"],
)

Expand Down Expand Up @@ -54,16 +85,31 @@ py_binary(
],
)

py_binary(
name = "wheel_rename",
srcs = ["//tools:wheel_rename.py"],
python_version = "PY3",
srcs_version = "PY3",
visibility = [
"//:__pkg__",
],
deps = [
requirement("packaging"),
],
)

py_wheel(
name = "wheel",
abi = "ABI",
author = "Google Inc.",
author_email = "[email protected]",
classifiers = [
"Programming Language :: Python :: 3",
"Topic :: Security :: Cryptography",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering :: Mathematics",
],
description_file = "//:DESCRIPTION.md",
description_file = "//:README.md",
distribution = "tf-shell",
extra_distinfo_files = {
"//:LICENSE": "LICENSE",
Expand All @@ -82,6 +128,7 @@ py_wheel(
python_requires = ">=3.8.0",
python_tag = "INTERPRETER",
requires = ["tensorflow-cpu==2.13.0"],
summary = "TF-Shell: Privacy preserving machine learning with Tensorflow and the SHELL encryption library",
version = VERSION_LABEL,
deps = [
"//shell_ml:shell_ml_pkg",
Expand Down
1 change: 0 additions & 1 deletion DESCRIPTION.md

This file was deleted.

70 changes: 67 additions & 3 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,77 @@ module(
version = "0.0.1",
)

bazel_dep(name = "rules_python", version = "0.23.1")
hermetic_python_version = "3.11"

hermetic_python_version_short = hermetic_python_version.replace(".", "_")

bazel_dep(name = "rules_python", version = "0.25.0")

python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(
is_default = hermetic_python_version == "3.8",
python_version = "3.8",
)
python.toolchain(
is_default = hermetic_python_version == "3.9",
python_version = "3.9",
)
python.toolchain(
is_default = hermetic_python_version == "3.10",
python_version = "3.10",
)
python.toolchain(
is_default = hermetic_python_version == "3.11",
python_version = "3.11",
)
use_repo(python,
"python_3_8", "python_3_8_x86_64-unknown-linux-gnu",
"python_3_9", "python_3_9_x86_64-unknown-linux-gnu",
"python_3_10", "python_3_10_x86_64-unknown-linux-gnu",
"python_3_11", "python_3_11_x86_64-unknown-linux-gnu",
"python_versions")

pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
pip.parse(
name = "pip",
requirements_lock = "//:requirements.txt",
hub_name = "pip",
python_version = "3.8",
requirements_lock = "//:requirements_3_8.txt",
)
pip.parse(
hub_name = "pip",
python_version = "3.9",
requirements_lock = "//:requirements_3_9.txt",
)
pip.parse(
hub_name = "pip",
python_version = "3.10",
requirements_lock = "//:requirements_3_10.txt",
)
pip.parse(
hub_name = "pip",
python_version = "3.11",
requirements_lock = "//:requirements_3_11.txt",
)
use_repo(pip, "pip")

bazel_dep(name = "buildifier_prebuilt", version = "6.1.0", dev_dependency = True)


bazel_dep(name = "pybind11_bazel", version = "2.11.1")
python_configure = use_extension("@pybind11_bazel//:python_configure.bzl", "extension")

#python_configure.toolchain(python_interpreter_target = "@python_versions//3.11:defs.bzl")
# above points to $(bazel info output_base)/external/rules_python~0.25.0~python~python_versions/3.11/defs.bzl
# and loads the repo @python_3_11//:defs
#python_configure.toolchain(python_interpreter_target = "@python_3_11//:defs.bzl")
# above points to ""
# and says the interpreter is "@python_3_11_x86_64-unknown-linux-gnu//:bin/python3"
# the above repo must be in the use_repo statement of rules_python
# something like below would be much better but I don't know how to get a
# plain old variable from a modules bzl file in this context.
#load("@python_3_11//:defs.bzl", "interpreter")
python_configure.toolchain(
python_interpreter_target = "@python_" + hermetic_python_version_short + "_x86_64-unknown-linux-gnu//:bin/python3"
)

use_repo(python_configure, "local_config_python", "pybind11")
57 changes: 27 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ homomorphic encryption via the

This is not an officially supported Google product.


## Getting Started

```bash
pip install tf-shell
```
See `./examples/` for how to use the library.

See `./examples/` for how to use the library.

## Background

Homomorphic encryption allows computation on encrypted data. For example, given
two ciphertexts `a` and `b` representing the numbers `3` and `4`, respectively,
one can compute a ciphertext `c` representing the number `7` without decrypting
Expand All @@ -29,8 +30,8 @@ ciphertexts with arbitrary depth. That said, because machine learning models are
of bounded depth, the performance benefits of leveled schemes (without
bootstrapping, e.g. SHELL) outweight limitations in circuit depth.


## Design

This library has two modules, `shell_tensor` which supports Tensorflow Tensors
containing ciphertexts with homomorphic properties, and `shell_ml` some (very)
simple machine learning tools supporting privacy preserving training.
Expand All @@ -41,47 +42,43 @@ party who holds the features would like to train a model without learning the
labels. The resultant trained model is differentially private with respect to
the labels.


## Building


### Build From Source
1. Install Bazelisk and python3 or use the devcontainer

2. Install requriements
1. Install Bazelisk and python3 or use the devcontainer.

2. Run the tests.

```bash
python -m venv .venv
source .venv/bin/activate
echo "build --enable_bzlmod" >> .bazelrc
[ -f ./requirements.txt ] || ( touch requirements.txt && bazelisk run //:requirements.update )
pip install --require-hashes --no-deps -r requirements.txt
bazelisk test --config test ...
```

3. Setup tensorflow
3. Build the code.

```bash
export TF_NEED_CUDA=0
export PIP_MANYLINUX2010=1
export TF_CUDA_VERSION=10.1
./configure_bazelrc.sh
bazelisk build --config release //:wheel
bazelisk run //:wheel_rename
```

3. Build
4. (Optional) Install the wheel, e.g. to try out the `./examples/`.
You may first need to copy the wheel out of the devcontainer's filesystem.
```bash
bazelisk build --config release //:wheel
python tools/wheel_rename.py
cp -f bazel-bin/*-*-cp*.whl ./ # copy out of devcontainer fs
cp -f bazel-bin/*.whl ./ # run in devcontainer
```
3. Install build artifact in a new venv, e.g. to try out the `./examples/`.
Then install.
```bash
pip install bazel-bin/tf-shell-...-manylinux.whl
pip install tf_shell-...-manylinux.whl
```
Note the cpython api is not compatible across minor python versions (e.g. 3.10,
3.11) so the wheel must be rebuilt for each python version.

### Code Formatters and Counters
```bash
bazelisk run //:bazel_formatter
bazelisk run //:python_formatter
Expand All @@ -92,14 +89,17 @@ bazelisk run //:clang_formatter
cloc ./ --fullpath --not-match-d='/(bazel-.*|.*\.venv)/'
```

### Update Python Dependencies
```bash
bazelisk run //:requirements.update
for ver in 3_8 3_9 3_10 3_11; do
touch requirements_${ver}.txt
bazelisk run //:requirements_${ver}.update
done
```

### PyPI Package
```bash
bazelisk build -c opt //:wheel
python wheel_rename.py
Expand All @@ -111,17 +111,14 @@ export TWINE_CERT
twine upload
```

## Contributing
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details.

## License
Apache 2.0; see [`LICENSE`](LICENSE) for details.

## Disclaimer
This project is not an official Google project. It is not supported by
Expand Down
Loading

0 comments on commit 114afb2

Please sign in to comment.