Transformation example#

Use opendp.transformations.make_user_transformation() to construct your own transformation.

Note

This requires a looser trust model, as we cannot verify any privacy or stability properties of user-defined functions.

>>> import opendp.prelude as dp
>>> dp.enable_features("honest-but-curious")

In this example, we mock the typical API of the OpenDP library to make a transformation that duplicates each record multiplicity times:

>>> import opendp.prelude as dp
>>> def make_repeat(multiplicity):
...     """Constructs a Transformation that duplicates each record `multiplicity` times"""
...     def function(arg: list[int]) -> list[int]:
...         return arg * multiplicity
...
...     def stability_map(d_in: int) -> int:
...         # if a user could influence at most `d_in` records before,
...         # they can now influence `d_in` * `multiplicity` records
...         return d_in * multiplicity
...
...     return dp.t.make_user_transformation(
...         input_domain=dp.vector_domain(dp.atom_domain(T=int)),
...         input_metric=dp.symmetric_distance(),
...         output_domain=dp.vector_domain(dp.atom_domain(T=int)),
...         output_metric=dp.symmetric_distance(),
...         function=function,
...         stability_map=stability_map,
...     )

The resulting Transformation may be used interchangeably with those constructed via the library:

>>> trans = (
...     (dp.vector_domain(dp.atom_domain(T=str)), dp.symmetric_distance())
...     >> dp.t.then_cast_default(TOA=int)
...     >> make_repeat(2)  # our custom transformation
...     >> dp.t.then_clamp((1, 2))
...     >> dp.t.then_sum()
...     >> dp.m.then_laplace(1.0)
... )
...
>>> release = trans(["0", "1", "2", "3"])
>>> trans.map(1) # computes epsilon
4.0

The code snip may form a basis for you to create your own data transformations, and mix them into an OpenDP analysis.