Questions or feedback?

Transformation example#

Use 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", "contrib")

enable_features("honest-but-curious", "contrib")

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

>>> def make_repeat(multiplicity):
...     """Construct 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:
...         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,
...     )
...

make_repeat <- function(input_domain, input_metric, multiplicity) {
  function_ <- function(arg) rep(arg, multiplicity)
  stability_map <- function(d_in) d_in * multiplicity

  make_user_transformation(
    input_domain = input_domain,
    input_metric = input_metric,
    output_domain = input_domain,
    output_metric = input_metric,
    function_ = function_,
    stability_map = stability_map
  )
}
then_repeat <- to_then(make_repeat)

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)
...     >> 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)
4.0

space <- c(
  vector_domain(atom_domain(.T = String)),
  symmetric_distance()
)
trans <- space |>
  then_cast_default(.TOA = i32) |>
  then_repeat(2L) |>
  then_clamp(c(1L, 2L)) |>
  then_sum() |>
  then_laplace(1.)
release <- trans(arg = c("0", "1", "2", "3"))
trans(d_in = 1L)

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