Discretize Distributions.jl

A Julia package for converting continuous and discrete probability distributions into discrete representations with interval-based support using IntervalArithmetic.jl.

The package provides functions to discretize univariate distributions into DiscreteNonParametric distributions where the support consists of IntervalArithmetic.Interval objects. Each interval [a, b] represents a probability mass over that range, computed using the cumulative distribution function (CDF) for continuous distributions or aggregated probability mass function (PMF) for discrete distributions.

Limitations

  • Finite support: Infinite distributions are truncated using quantile bounds (default 0.1% and 99.9%)
  • Discrete distribution quirks: Discretizing already-discrete distributions has some limitations and edge cases
  • Non-integer discrete values: Discrete distributions with non-integer support may behave unexpectedly

Future Work

  • Develop better warnings for incompatible distributions
  • Support for multivariate distributions

API Overview

The package provides three main discretize methods:

  1. Fixed intervals: discretize(dist, interval_width) - Creates uniform intervals of specified width
  2. Custom boundaries: discretize(dist, boundaries) - Uses custom interval boundaries
  3. Pre-constructed intervals: discretize(dist, intervals) - Uses pre-built Interval objects

All methods return a DiscreteNonParametric distribution with IntervalArithmetic.Interval support.

Working with Results

using Distributions, DiscretizeDistributions, IntervalArithmetic

# Discretize a normal distribution
normal_dist = Normal(0, 1)
interval_dist = discretize(normal_dist, 0.5)

# The result has interval support
support(interval_dist)  # Vector of Interval{Float64} objects
probs(interval_dist)    # Corresponding probabilities

# Convert to point-based distributions
left_aligned = left_align_distribution(interval_dist)     # Use left endpoints
centered = centred_distribution(interval_dist)            # Use midpoints  
right_aligned = right_align_distribution(interval_dist)   # Use right endpoints

Mathematical Details

Continuous Distributions

For continuous distributions, discretisation computes probability masses using the cumulative distribution function (CDF):

\[P(X' ∈ [a_i, a_{i+1})) = F(a_{i+1}) - F(a_i)\]

where F(x) is the CDF of the continuous distribution X.

Discrete Distributions

For discrete distributions, probability masses are aggregated over intervals using the probability mass function (PMF):

\[P(X' ∈ [a_i, a_{i+1})) = ∑_{k=⌈a_i⌉}^{⌊a_{i+1}⌋-1} P(X = k) + (P(X = ⌊a_i⌋) × (⌈a_i⌉ - a_i)) + (P(X = ⌊a_{i+1}⌋) × (a_{i+1} - ⌊a_{i+1}⌋))\]

All resulting discrete distributions are normalized to ensure probabilities sum to 1.

Advanced Usage

Handling Unbounded Distributions

For distributions with infinite support, control truncation with quantile bounds:

# Normal distribution - unbounded in both directions  
normal_dist = Normal(0, 1)
discrete_normal = discretize(normal_dist, 0.2; min_quantile=0.005, max_quantile=0.995)

# Exponential distribution - unbounded above
exp_dist = Exponential(1.0)  
discrete_exp = discretize(exp_dist, 0.1; max_quantile=0.99)

# Result includes infinite tail intervals
support(discrete_exp)  # [..., interval(4.5, 5.0), interval(5.0, ∞)]

Custom Interval Structures

Create non-uniform discretisations with custom boundaries:

# Fine resolution near zero, coarser elsewhere
custom_boundaries = [-5.0, -2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0, 5.0]
discrete_custom = discretize(Normal(0, 1), custom_boundaries)

# Results in intervals: [(-∞,-5], [-5,-2], [-2,-1], ..., [5,∞)]
length(support(discrete_custom))  # 10 intervals (8 from boundaries + 2 infinite tails)

Working with Pre-constructed Intervals

For advanced use cases, you can provide pre-constructed IntervalArithmetic.Interval objects:

using IntervalArithmetic

# Create custom intervals with specific properties
intervals = [
    interval(-2.0, -1.0),    # Standard interval
    interval(-1.0, 0.0),     # Adjacent interval
    interval(0.0, 2.0),      # Wider interval
    interval(2.0, Inf)       # Semi-infinite interval
]

# Discretize using these intervals
normal_dist = Normal(0, 1)
discrete_custom = discretize(normal_dist, intervals)
DiscretizeDistributions.discretizeFunction
discretize(dist::Distributions.UnivariateDistribution, interval::Real; 
           min_quantile=0.001, max_quantile=0.999)

Discretize a univariate distribution into an interval-based discrete distribution using fixed intervals.

This function converts a univariate distribution into a discrete one by dividing the distribution's support into intervals of fixed width and computing the probability mass in each interval. The resulting distribution has IntervalArithmetic.Interval objects as support points.

Arguments

  • dist::Distributions.UnivariateDistribution: The distribution to discretize (continuous or discrete)
  • interval::Real: The width of each discretisation interval
  • min_quantile=0.001: Lower quantile bound for unbounded distributions
  • max_quantile=0.999: Upper quantile bound for unbounded distributions

Returns

  • DiscreteNonParametric{Interval{T}, ...}: Discrete distribution with interval support

Details

For bounded distributions, the natural bounds are used. For unbounded distributions, the bounds are determined using the specified quantiles. The probability mass in each interval is computed using the CDF for continuous distributions or a pseudo-CDF for discrete distributions.

Examples

using Distributions, DiscretizeDistributions, IntervalArithmetic

# Discretize a normal distribution with interval width 0.5
normal_dist = Normal(0, 1)
discrete_intervals = discretize(normal_dist, 0.5)
# Returns distribution with support like [interval(-∞, -3.5), interval(-3.5, -3.0), ...]

# Convert to point-based distributions if needed
left_aligned = left_align_distribution(discrete_intervals)    # [-3.5, -3.0, -2.5, ...]
centered = centred_distribution(discrete_intervals)          # [-3.25, -2.75, -2.25, ...]

# Discretize a discrete distribution
poisson_dist = Poisson(3.0)
discrete_poisson = discretize(poisson_dist, 2)
source
discretize(dist::Distributions.UnivariateDistribution, interval::AbstractVector)

Discretize a univariate distribution using custom interval boundaries.

This function converts a univariate distribution into a discrete one using user-specified interval boundaries. The resulting distribution has IntervalArithmetic.Interval objects as support points representing the probability mass in each interval.

Arguments

  • dist::Distributions.UnivariateDistribution: The distribution to discretize
  • interval::AbstractVector: Vector of interval boundaries (will be sorted automatically)

Returns

  • DiscreteNonParametric{Interval{T}, ...}: Discrete distribution with interval support

Details

The input interval vector is automatically sorted and combined with distribution bounds. Probability masses are computed using the CDF for continuous distributions or pseudo-CDF for discrete distributions. The resulting distribution represents probability masses over intervals [a_i, a_{i+1}).

Examples

using Distributions, DiscretizeDistributions, IntervalArithmetic

# Discretize using custom intervals
normal_dist = Normal(5, 2)
custom_intervals = [0.0, 2.0, 4.0, 6.0, 8.0, 10.0]
discrete_intervals = discretize(normal_dist, custom_intervals)
# Support: [interval(-∞, 0.0), interval(0.0, 2.0), ..., interval(10.0, ∞)]

# Convert to different alignments
left_points = left_align_distribution(discrete_intervals)
centered_points = centred_distribution(discrete_intervals)
right_points = right_align_distribution(discrete_intervals)

# Discrete distribution with custom intervals
poisson_dist = Poisson(3.0)
discrete_poisson = discretize(poisson_dist, [0.5, 2, 4, 6, 8, 10])
source
discretize(dist::Distributions.UnivariateDistribution, interval::AbstractVector{IntervalArithmetic.Interval{X}}) where X <: Real

Discretize a univariate distribution using pre-constructed interval objects.

This function converts a univariate distribution into a discrete one using user-specified IntervalArithmetic.Interval objects. This is the core discretization method that all other discretize methods ultimately call. The resulting distribution has the same interval objects as support points with computed probability masses.

Arguments

  • dist::Distributions.UnivariateDistribution: The distribution to discretize
  • interval::AbstractVector{IntervalArithmetic.Interval{X}}: Vector of pre-constructed intervals

Returns

  • DiscreteNonParametric{Interval{X}, ...}: Discrete distribution with interval support

Details

This method computes probability masses directly using the interval boundaries. For each interval [a, b], the probability is computed as cdf(dist, b) - cdf(dist, a). The resulting probabilities are normalized to sum to 1.

Examples

using Distributions, DiscretizeDistributions, IntervalArithmetic

# Create intervals manually
intervals = [interval(-1.0, 0.0), interval(0.0, 1.0), interval(1.0, 2.0)]

# Discretize using these intervals
normal_dist = Normal(0, 1)
discrete_intervals = discretize(normal_dist, intervals)
# Each interval gets probability mass according to the normal distribution
source
DiscretizeDistributions.left_align_distributionFunction
left_align_distribution(dist::Distributions.DiscreteNonParametric{IntervalArithmetic.Interval{T}, ...})

Convert an interval-based discrete distribution to a left-aligned point-based distribution.

This function takes a discrete distribution with interval support and creates a new distribution where each support point is positioned at the left endpoint (infimum) of the corresponding interval. Infinite intervals are automatically removed before conversion.

Arguments

  • dist::DiscreteNonParametric{Interval{T}, ...}: Input discrete distribution with interval support

Returns

  • DiscreteNonParametric{T, ...}: New distribution with left-aligned point support

Examples

using Distributions, DiscretizeDistributions, IntervalArithmetic

# Create an interval-based distribution
intervals = [interval(0.0, 1.0), interval(1.0, 2.0), interval(2.0, 3.0)]
probs = [0.3, 0.4, 0.3]
interval_dist = DiscreteNonParametric(intervals, probs, check_args=false)

# Convert to left-aligned points
left_aligned = left_align_distribution(interval_dist)
# Support becomes [0.0, 1.0, 2.0] (left endpoints of intervals)
source
DiscretizeDistributions.centred_distributionFunction
centred_distribution(dist::Distributions.DiscreteNonParametric{IntervalArithmetic.Interval{T}, ...})

Convert an interval-based discrete distribution to a centered point-based distribution.

This function takes a discrete distribution with interval support and creates a new distribution where each support point is positioned at the center (midpoint) of the corresponding interval. Infinite intervals are automatically removed before conversion.

Arguments

  • dist::DiscreteNonParametric{Interval{T}, ...}: Input discrete distribution with interval support

Returns

  • DiscreteNonParametric{T, ...}: New distribution with centered point support

Examples

using Distributions, DiscretizeDistributions, IntervalArithmetic

# Create an interval-based distribution
intervals = [interval(0.0, 1.0), interval(1.0, 2.0), interval(2.0, 3.0)]
probs = [0.3, 0.4, 0.3]
interval_dist = DiscreteNonParametric(intervals, probs, check_args=false)

# Convert to centered points
centered = centred_distribution(interval_dist)
# Support becomes [0.5, 1.5, 2.5] (midpoints of intervals)
source
DiscretizeDistributions.right_align_distributionFunction
right_align_distribution(dist::Distributions.DiscreteNonParametric{IntervalArithmetic.Interval{T}, ...})

Convert an interval-based discrete distribution to a right-aligned point-based distribution.

This function takes a discrete distribution with interval support and creates a new distribution where each support point is positioned at the right endpoint (supremum) of the corresponding interval. Infinite intervals are automatically removed before conversion.

Arguments

  • dist::DiscreteNonParametric{Interval{T}, ...}: Input discrete distribution with interval support

Returns

  • DiscreteNonParametric{T, ...}: New distribution with right-aligned point support

Examples

using Distributions, DiscretizeDistributions, IntervalArithmetic

# Create an interval-based distribution
intervals = [interval(0.0, 1.0), interval(1.0, 2.0), interval(2.0, 3.0)]
probs = [0.3, 0.4, 0.3]
interval_dist = DiscreteNonParametric(intervals, probs, check_args=false)

# Convert to right-aligned points
right_aligned = right_align_distribution(interval_dist)
# Support becomes [1.0, 2.0, 3.0] (right endpoints of intervals)
source