==========================
General Privacy Mechanisms
==========================

Beyond DP-SGD, ``gdpnum`` can analyze any privacy mechanism that can be represented as a Privacy Loss Distribution (PLD). This includes a wide range of mechanisms such as:

- Gaussian mechanisms
- Laplace mechanisms
- Randomized response
- Discrete Gaussian mechanisms
- Composition of multiple mechanisms

Working with PLDs
=================

To analyze a general mechanism, you first need to create a Privacy Loss Distribution (PLD) object using the ``dp_accounting`` library:

.. code-block:: python

    from dp_accounting.pld.privacy_loss_distribution import (
        from_gaussian_mechanism,
        from_laplace_mechanism,
        from_randomized_response
    )
    
    # Create a PLD for a Gaussian mechanism
    pld_gaussian = from_gaussian_mechanism(
        standard_deviation=1.0,
        value_discretization_interval=1e-3
    )
    
    # Or for a Laplace mechanism
    pld_laplace = from_laplace_mechanism(
        parameter=1.0,
        value_discretization_interval=1e-3
    )
    
    # Or for randomized response
    pld_rr = from_randomized_response(
        probability_true_to_false=0.1,
        probability_false_to_true=0.2
    )

Converting to GDP
=================

Once you have a PLD, you can convert it to GDP using the ``PLDConverter``:

.. code-block:: python

    import gdpnum
    
    # Create a converter for the PLD
    converter = gdpnum.PLDConverter(pld_gaussian)
    
    # Get the GDP parameter and regret
    mu, regret = converter.get_mu_and_regret()
    
    print(f"GDP parameter μ: {mu:.6f}")
    print(f"Regret: {regret:.6f}")

The regret value indicates how well the mechanism is approximated by GDP. For non-Gaussian mechanisms, there may be some approximation error.

Composing Mechanisms
====================

One of the key advantages of PLDs and GDP is that they compose easily. You can analyze the composition of multiple mechanisms:

.. code-block:: python

    # Compose two mechanisms
    composed_pld = pld_gaussian.compose(pld_laplace)
    
    # Analyze the composed mechanism
    composed_converter = gdpnum.PLDConverter(composed_pld)
    mu, regret = composed_converter.get_mu_and_regret()

Self-Composition
================

For iterative mechanisms (like multiple steps of DP-SGD), you can use self-composition:

.. code-block:: python

    # Create a basic mechanism
    pld_base = from_gaussian_mechanism(
        standard_deviation=1.0,
        value_discretization_interval=1e-3
    )
    
    # Compose it with itself 100 times
    pld_composed = pld_base.self_compose(100)
    
    # Analyze the composed mechanism
    converter = gdpnum.PLDConverter(pld_composed)
    mu, regret = converter.get_mu_and_regret()

Subsampling
===========

For mechanisms that use subsampling (like DP-SGD), you can specify the sampling probability:

.. code-block:: python

    # Gaussian mechanism with subsampling
    pld = from_gaussian_mechanism(
        standard_deviation=1.0,
        sampling_prob=0.1,  # 10% sampling rate
        value_discretization_interval=1e-3
    )
    
    converter = gdpnum.PLDConverter(pld)
    mu, regret = converter.get_mu_and_regret()

Advanced: Working with Tradeoff Curves
======================================

The GDP framework is based on hypothesis testing tradeoff curves. You can directly access these curves:

.. code-block:: python

    # Get the tradeoff curve at recommended alpha values
    beta_values = converter.get_beta()
    
    # Or at specific alpha values
    alphas = [0.001, 0.01, 0.1, 0.5]
    beta_values = converter.get_beta(alphas)
    
    # Plot the tradeoff curve
    import matplotlib.pyplot as plt
    
    plt.figure(figsize=(8, 6))
    plt.plot(alphas, beta_values)
    plt.xlabel("Type I Error (α)")
    plt.ylabel("Type II Error (β)")
    plt.title(f"GDP Tradeoff Curve (μ = {mu:.3f})")
    plt.grid(True)
    plt.show()

This tradeoff curve represents the fundamental privacy-utility tradeoff of the mechanism.

Example: The Census TopDown Algorithm
=====================================

A real-world example is analyzing the U.S. Census Bureau's TopDown algorithm:

.. code-block:: python

    import numpy as np
    from dp_accounting.pld.privacy_loss_distribution import from_discrete_gaussian_mechanism
    from gdpnum import PLDConverter
    
    # TopDown algorithm parameters (simplified example)
    levels = ['US', 'State', 'County', 'PEPG', 'Track Subset Group', 
              'Track Subset', 'Block Group', 'Block']
    allocation = np.array([2, 27.4, 8.5, 13.1, 13.1, 23.8, 11.8, 0.3]) / 100
    compositions = 10
    total_rho = 3.65
    
    # Calculate sigmas for each level
    sigmas = np.sqrt(compositions / (2 * allocation * total_rho))
    
    # Create PLDs for each level
    plds = []
    for sigma in sigmas:
        pld = from_discrete_gaussian_mechanism(
            sigma=sigma,
            value_discretization_interval=1e-3
        ).self_compose(compositions)
        plds.append(pld)
    
    # Compose all levels
    combined_pld = plds[0]
    for pld in plds[1:]:
        combined_pld = combined_pld.compose(pld)
    
    # Analyze with gdpnum
    converter = PLDConverter(combined_pld)
    mu, regret = converter.get_mu_and_regret()
    
    print(f"Census TopDown GDP: μ = {mu:.6f}, regret = {regret:.6f}")