OpenDP computations are always strict about the types being used. Integers and floats are never treated interchangeably and there is never implicit casting between types. In fact, OpenDP is particular about the bit-depth of data types, as it can impact the choice of constants in the privacy analysis.
You can explicitly set the types that a transformation or measurement work with via type arguments in the constructor. Each constructor has its own set of permissible types, based on the type of computation it is performing. For instance, the clamp constructor accepts any numerical type for its type argument TA:
>>> from opendp.transformations import make_clamp
>>> from opendp.typing import *
>>> clamper = make_clamp((0, 1), TA=i32)
Many of the API docs indicate that parameters like TIA or D are type arguments.
When you want to describe the type of a domain, metric, measure, or other elements, you can do so via a type descriptor.
The canonical form is a literal string, like
"i32" to denote that the type should be a 32-bit integer,
"SymmetricDistance" to denote the type of a metric.
These arguments accept any of the following, depending on the context:
Python type (like float or bool)
Python typing module annotation (like List[str])
opendp.typing(mimics Python type annotations for OpenDP types)
In addition, there is a common pattern to the naming of type arguments.
D for Domain
M for Metric or Measure
T for the type of members of a domain
Q for the type of distances in a metric or measure
There are additional modifiers:
I for Input
O for Output
A for Atomic, the smallest type. i32 is the atomic type of Vec<i32>
Some examples being:
TA for the atomic type. float could be the TA for a float
TIA for Atomic Input Type. str could be the TIA for a
MO for Output Metric. AbsoluteDistance[int] could be the MO for a
QO for Output distance. float could be the QO for a
discrete laplace measurement.
The API docs should also include explanations in most contexts.
OpenDP supports the following atomic data types:
i8 (8-bit signed integer)
i16 (16-bit signed integer)
u8 (8-bit unsigned integer)
u16 (16-bit unsigned integer)
f32 (32-bit single-precision float)
f64 (64-bit double-precision float)
Some types are parameterized by another type, like:
Examples of these types include:
It can be more convenient to denote types in terms of Python types, so we’ve added some aliases for Python types.
Python Type Alias
Default Rust Type
You can change the default type for floats and ints via
These functions make it easy to set the default bit depth throughout your code, all at once.
This can be particularly useful when working with NumPy arrays which default to i64, or when working with deep learning libraries that default to single-precision floats.