Skip to content

Commit

Permalink
Update Bazel Tools for Java
Browse files Browse the repository at this point in the history
Java DP library:
- Update Bazel tools for Java: bazel-common and rules_jvm_external

Accounting library:
- Adds to/from tuple functions for DpEvents.

ZetaSQL:
- Mention flags to increase available memory for ZetaSQL

PiperOrigin-RevId: 522106800
Change-Id: I4db58ab45a0554efee6fd48a30895f868fe4be75
GitOrigin-RevId: 057b69f9b8537c438a872fed7fadce3f11e586ed
  • Loading branch information
Differential Privacy Team authored and monsieurmuffin committed Apr 13, 2023
1 parent 1212b69 commit cc0468a
Show file tree
Hide file tree
Showing 8 changed files with 916 additions and 395 deletions.
27 changes: 20 additions & 7 deletions examples/java/WORKSPACE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,34 @@ local_repository(
path = "../../java",
)

# Load maven rules.
load("@com_google_java_differential_privacy//:dp_java_deps_preload.bzl", "dp_java_deps_prework")

dp_java_deps_prework()

load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")

rules_jvm_external_deps()

load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup")

rules_jvm_external_setup()

load("@com_google_java_differential_privacy//:dp_java_deps.bzl", "dp_java_deps")

dp_java_deps()

# Load dependencies for the base workspace.
load("@com_google_differential_privacy//:differential_privacy_deps.bzl", "differential_privacy_deps")

differential_privacy_deps()

# Protobuf transitive dependencies.
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
protobuf_deps()

# Load maven rules.
load("@com_google_java_differential_privacy//:dp_java_deps_preload.bzl", "dp_java_deps_prework")
dp_java_deps_prework()

load("@com_google_java_differential_privacy//:dp_java_deps.bzl", "dp_java_deps")
dp_java_deps()
protobuf_deps()

# Complete pinning after defining dependencies.
load("@maven//:defs.bzl", "pinned_maven_install")

pinned_maven_install()
3 changes: 3 additions & 0 deletions examples/zetasql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ the [codelab](codelab.md).
ZetaSQL on Mac. We're working on a solution, but if you are interested in
trying this now, using Bazelisk and Homebrew could work.
1. Windows is currently not a supported configuration.
1. In case we are running out of memory, try using the
`--evaluator_max_value_byte_size` and
`--evaluator_max_intermediate_byte_size` flags to increase the max memory.

We will continue to publish updates and improvements to the library. Please
[reach out](https://github.com/google/differential-privacy#reach-out) to us with
Expand Down
11 changes: 9 additions & 2 deletions java/WORKSPACE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,33 @@ local_repository(
)

# Load maven rules.
load("@com_google_java_differential_privacy//:dp_java_deps_preload.bzl", "dp_java_deps_prework")
load("//:dp_java_deps_preload.bzl", "dp_java_deps_prework")

dp_java_deps_prework()

load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")

rules_jvm_external_deps()

load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup")

rules_jvm_external_setup()

load("@com_google_java_differential_privacy//:dp_java_deps.bzl", "dp_java_deps")
load("//:dp_java_deps.bzl", "dp_java_deps")

dp_java_deps()

# Load dependencies for the base workspace.
load("@com_google_differential_privacy//:differential_privacy_deps.bzl", "differential_privacy_deps")

differential_privacy_deps()

# Protobuf transitive dependencies.
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")

protobuf_deps()

# Complete pinning after defining dependencies.
load("@maven//:defs.bzl", "pinned_maven_install")

pinned_maven_install()
10 changes: 5 additions & 5 deletions java/dp_java_deps_preload.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ def dp_java_deps_prework():
This must be called before the rest of the dependencies are loaded.
"""
RULES_JVM_EXTERNAL_TAG = "4.4.2"
RULES_JVM_EXTERNAL_SHA = "735602f50813eb2ea93ca3f5e43b1959bd80b213b836a07a62a29d757670b77b"
BAZEL_COMMON_TAG = "3d0e5005cfcbee836e31695d4ab91b5328ccc506"
BAZEL_COMMON_SHA = "8dd4dd688b42148f2a87652901a4eb2c85c64834be7a6890ebfc8ef1f67eeeaa"
RULES_JVM_EXTERNAL_TAG = "5.1"
RULES_JVM_EXTERNAL_SHA = "8c3b207722e5f97f1c83311582a6c11df99226e65e2471086e296561e57cc954"
BAZEL_COMMON_TAG = "a9e1d8efd54cbf27249695b23775b75ca65bb59d"
BAZEL_COMMON_SHA = "17ea98149586dff60aa741c67fbd9a010fbb1507df90e741c50403bf5228bea3"
http_archive(
name = "rules_jvm_external",
strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
sha256 = RULES_JVM_EXTERNAL_SHA,
url = "https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/%s.zip" % RULES_JVM_EXTERNAL_TAG,
url = "https://github.com/bazelbuild/rules_jvm_external/releases/download/%s/rules_jvm_external-%s.tar.gz" % (RULES_JVM_EXTERNAL_TAG, RULES_JVM_EXTERNAL_TAG),
)
http_archive(
name = "bazel_common",
Expand Down
1,045 changes: 670 additions & 375 deletions java/maven_install.json

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions python/dp_accounting/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ py_library(
],
)

py_test(
name = "dp_event_test",
srcs = ["dp_event_test.py"],
python_version = "PY3",
srcs_version = "PY3",
deps = [
":dp_event",
requirement("absl-py"),
],
)

py_library(
name = "dp_event_builder",
srcs = ["dp_event_builder.py"],
Expand Down
118 changes: 112 additions & 6 deletions python/dp_accounting/dp_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,22 @@ class can implement the accounting correctly without knowing any other
* `PrivacyAccountant` implementations are expected to return `supports(event)`
is `False` when processing unknown mechanisms.
"""

from typing import List, Union
import importlib
import typing
from typing import NamedTuple, Protocol, Union

import attr


@typing.runtime_checkable
class DpEventNamedTuple(Protocol):
_fields: tuple[str, ...]

module_name: str
class_name: str


@attr.s(frozen=True)
class DpEvent(object):
"""Represents application of a private mechanism.
Expand All @@ -72,6 +82,104 @@ class DpEvent(object):
with other `DpEvent`s.
"""

def to_named_tuple(self) -> DpEventNamedTuple:
"""Converts DpEvent to a NamedTuple representation.
This can be useful for passing around DpEvents in contexts where it is
undesirable to take dp_accounting as a dependency, or for serialization.
Returns:
NamedTuple representing the DpEvent.
Raises:
ValueError: This type of DpEvent is not convertable to NamedTuple. This
could happen for example if the DpEvent has private fields or has a
field with init = False.
"""
cls = type(self)
_check_attrs_cls_for_known_errors(cls)

fields = [('module_name', str), ('class_name', str)]
fields.extend([(x.name, x.type) for x in attr.fields(cls)])
named_tuple_wrapper_name = f'_{cls.__name__}NamedTupleWrapper'
_NamedTupleWrapper = NamedTuple(named_tuple_wrapper_name, fields) # pylint: disable=invalid-name

values = {'module_name': cls.__module__, 'class_name': cls.__name__}
for key, value in attr.asdict(self, recurse=False).items():
if attr.has(type(value)):
value = value.to_named_tuple()
values[key] = value

return _NamedTupleWrapper(**values)

@classmethod
def from_named_tuple(cls, obj: DpEventNamedTuple) -> 'DpEvent':
"""Converts NamedTuple representation to corresponding DpEvent.
This can be useful for passing around DpEvents in contexts where it is
undesirable to take dp_accounting as a dependency, or for serialization.
Args:
obj: The NamedTuple to convert to a DpEvent.
Returns:
The DpEvent corresponding to the NamedTuple.
Raises:
ValueError: This type of DpEvent is not convertable to NamedTuple. This
could happen for example if the DpEvent has private fields or has a
field with init = False.
"""
module_name = obj.module_name
if isinstance(module_name, bytes):
module_name = module_name.decode()
class_name = obj.class_name
if isinstance(class_name, bytes):
class_name = class_name.decode()
module = importlib.import_module(module_name)
subcls = getattr(module, class_name)

fields = set(obj._fields) - set(['module_name', 'class_name'])
values = {}
for field in fields:
value = getattr(obj, field)
if isinstance(value, DpEventNamedTuple):
value = subcls.from_named_tuple(value)
values[field] = value
try:
return subcls(**values)
except Exception as e:
_check_attrs_cls_for_known_errors(subcls)
raise e


def _check_attrs_cls_for_known_errors(cls: type[DpEvent]) -> None:
"""Checks properties of attrs that are not compatible with NamedTuple.
Args:
cls: Class type to check.
Raises:
ValueError: cls is incompatible with conversion to NamedTuple.
"""
if not attr.has(cls):
raise ValueError(
f'Expected `cls` to be an `attrs` decorated class, found `{cls}`.'
)
for field in attr.fields(cls):
if field.name.startswith('_'):
raise ValueError(
'Expected all fields on the `attrs` decorated class to be public, '
f'found `{cls}` has a field `{field.name}` thats starts with `_`, '
'making it private.'
)
if not field.init:
raise ValueError(
'Expected all fields on the `attrs` decorated class to set `init` to '
f'True, found `{cls}` has a field `{field.name}` that set `init` to '
'False.'
)


@attr.s(frozen=True)
class NoOpDpEvent(DpEvent):
Expand Down Expand Up @@ -150,7 +258,7 @@ class ComposedDpEvent(DpEvent):
The composition may be adaptive, where the query producing each event depends
on the results of prior queries.
"""
events: List[DpEvent]
events: list[DpEvent]


@attr.s(frozen=True, slots=True, auto_attribs=True)
Expand Down Expand Up @@ -211,7 +319,7 @@ class SingleEpochTreeAggregationDpEvent(DpEvent):
tree.
"""
noise_multiplier: float
step_counts: Union[int, List[int]]
step_counts: Union[int, list[int]]


@attr.s(frozen=True, slots=True, auto_attribs=True)
Expand All @@ -232,5 +340,3 @@ class RepeatAndSelectDpEvent(DpEvent):
event: DpEvent
mean: float
shape: float


86 changes: 86 additions & 0 deletions python/dp_accounting/dp_event_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright 2023, The TensorFlow Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from absl.testing import absltest
from absl.testing import parameterized

from dp_accounting import dp_event


class DpEventTest(parameterized.TestCase):

@parameterized.named_parameters(
('base_class', dp_event.DpEvent()),
('no_op', dp_event.NoOpDpEvent()),
('non_private', dp_event.NonPrivateDpEvent()),
('unsupported', dp_event.UnsupportedDpEvent()),
('gaussian', dp_event.GaussianDpEvent(1.0)),
('laplace', dp_event.LaplaceDpEvent(1.0)),
(
'self_composed',
dp_event.SelfComposedDpEvent(dp_event.GaussianDpEvent(1.0), 10),
),
(
'composed',
dp_event.ComposedDpEvent(
[dp_event.GaussianDpEvent(1.0), dp_event.LaplaceDpEvent(1.0)]
),
),
(
'poisson',
dp_event.PoissonSampledDpEvent(0.1, dp_event.GaussianDpEvent(1.0)),
),
(
'sampled_with_replacement',
dp_event.SampledWithReplacementDpEvent(
1000, 10, dp_event.GaussianDpEvent(1.0)
),
),
(
'sampled_without_replacement',
dp_event.SampledWithoutReplacementDpEvent(
1000, 10, dp_event.GaussianDpEvent(1.0)
),
),
('tree_int', dp_event.SingleEpochTreeAggregationDpEvent(1.0, 5)),
('tree_list', dp_event.SingleEpochTreeAggregationDpEvent(1.0, [5, 10])),
(
'repeat_and_select',
dp_event.RepeatAndSelectDpEvent(
dp_event.GaussianDpEvent(1.0), 30.0, 1.0
),
),
(
'complex',
dp_event.ComposedDpEvent([
dp_event.SingleEpochTreeAggregationDpEvent(1.0, 5),
dp_event.PoissonSampledDpEvent(0.1, dp_event.LaplaceDpEvent(1.0)),
dp_event.SelfComposedDpEvent(
dp_event.SampledWithReplacementDpEvent(
1000, 10, dp_event.GaussianDpEvent(1.0)
),
50,
),
]),
),
)
def test_to_from_named_tuple(self, event):
named_tuple = event.to_named_tuple()
self.assertIsInstance(named_tuple, tuple)
self.assertIsInstance(named_tuple, dp_event.DpEventNamedTuple)
reconstructed = dp_event.DpEvent.from_named_tuple(named_tuple)
self.assertEqual(event, reconstructed)


if __name__ == '__main__':
absltest.main()

0 comments on commit cc0468a

Please sign in to comment.