Pre-Trends Power Analysis
=========================

Power analysis for pre-trends tests (Roth 2022).

.. module:: diff_diff.pretrends

Overview
--------

Passing a pre-trends test does not guarantee that parallel trends holds. Roth (2022)
shows that pre-trends tests are often underpowered to detect economically meaningful
violations of parallel trends. This module provides tools to assess:

1. **Power**: The probability of rejecting the null (no pre-trends) given a violation
2. **Minimum Detectable Violation (MDV)**: The smallest violation detectable at target power

Key insights from Roth (2022):

- Pre-trends tests are joint tests that pre-period coefficients equal zero
- Standard pre-trends tests often have low power against linear trends
- A "passed" pre-trends test may simply reflect lack of statistical power
- MDV provides the minimum violation the test could have detected

PreTrendsPower
--------------

Main class for pre-trends power analysis.

.. autoclass:: diff_diff.PreTrendsPower
   :no-index:
   :members:
   :undoc-members:
   :show-inheritance:

   .. rubric:: Methods

   .. autosummary::

      ~PreTrendsPower.fit
      ~PreTrendsPower.power_curve
      ~PreTrendsPower.sensitivity_to_honest_did

Example
~~~~~~~

.. code-block:: python

   from diff_diff import MultiPeriodDiD, PreTrendsPower

   # First fit an event study
   model = MultiPeriodDiD()
   results = model.fit(data, outcome='y', treatment='treated',
                       time='period', unit='unit_id',
                       post_periods=[5, 6, 7], reference_period=4)

   # Compute pre-trends power for linear violations.
   # Default acceptance region is the Roth (2022) NIS box probability.
   pt = PreTrendsPower(alpha=0.05, power=0.80, violation_type='linear')
   pt_results = pt.fit(results)

   print(f"MDV: {pt_results.mdv:.3f}")
   print(f"Power: {pt_results.power:.2%}")
   print(f"NIS box probability (accept H0): {pt_results.nis_box_probability:.4f}")

   # Select the Wald (noncentral-χ²) acceptance-region form instead of the
   # default NIS box probability. Wald preserves the pre-PR-B acceptance-
   # region math byte-identically; numerical-output bit-identity to pre-PR-B
   # fitted results only holds on regular pre-period grids and on the
   # legacy `relative_times=None` path. PR-B Step 4's `relative_times`
   # threading applies to BOTH NIS and Wald, so on irregular grids the
   # Wald MDV is also in Roth's γ units (see REGISTRY linear-pattern Note).
   pt_wald = PreTrendsPower(
       alpha=0.05, power=0.80, violation_type='linear', pretest_form='wald'
   )

PreTrendsPowerResults
---------------------

Results from pre-trends power analysis.

.. autoclass:: diff_diff.PreTrendsPowerResults
   :no-index:
   :members:
   :undoc-members:
   :show-inheritance:

PreTrendsPowerCurve
-------------------

Power curve across violation magnitudes.

.. autoclass:: diff_diff.PreTrendsPowerCurve
   :no-index:
   :members:
   :undoc-members:
   :show-inheritance:

Convenience Functions
---------------------

compute_pretrends_power
~~~~~~~~~~~~~~~~~~~~~~~

Quick computation of pre-trends power.

.. autofunction:: diff_diff.compute_pretrends_power

compute_mdv
~~~~~~~~~~~

Compute minimum detectable violation.

.. autofunction:: diff_diff.compute_mdv

plot_pretrends_power
~~~~~~~~~~~~~~~~~~~~

Plot a pre-trends test power curve.

.. autofunction:: diff_diff.plot_pretrends_power

Violation Types
---------------

The module supports several types of pre-trends violations:

**linear**
   Linear trend violations where each pre-period differs from the reference by
   an amount proportional to distance. ``delta[t] = M * t`` for pre-periods.

**constant**
   Constant violations where all pre-periods have the same deviation.
   ``delta[t] = M`` for all pre-periods.

**last_period**
   Only the period immediately before treatment is violated.
   ``delta[-1] = M``, all other pre-periods are zero.

**custom**
   User-specified violation pattern via the ``violation_weights`` parameter.
   Accepted by both ``PreTrendsPower`` (constructor kwarg) and the convenience
   helpers ``compute_pretrends_power`` / ``compute_mdv`` (forwarded kwarg).

Complete Example
----------------

.. code-block:: python

   import numpy as np
   from diff_diff import (
       MultiPeriodDiD,
       PreTrendsPower,
       compute_mdv,
       plot_pretrends_power,
   )

   # Fit event study
   model = MultiPeriodDiD()
   results = model.fit(data, outcome='y', treatment='treated',
                       time='period', unit='unit_id',
                       post_periods=[5, 6, 7], reference_period=4)

   # Compute MDV
   mdv = compute_mdv(results, alpha=0.05, target_power=0.80)
   print(f"Minimum Detectable Violation: {mdv:.3f}")

   # Power curve analysis
   pt = PreTrendsPower(alpha=0.05, violation_type='linear')
   curve = pt.power_curve(results, n_points=50)

   # Plot power curve
   ax = plot_pretrends_power(curve, target_power=0.80)
   ax.figure.savefig('pretrends_power.png')

   # Integration with HonestDiD
   sensitivity = pt.sensitivity_to_honest_did(results)

References
----------

- Roth, J. (2022). Pretest with Caution: Event-Study Estimates after Testing
  for Parallel Trends. *American Economic Review: Insights*, 4(3), 305-322.

- R package: `pretrends <https://github.com/jonathandroth/pretrends>`_

See Also
--------

- :doc:`honest_did` - Sensitivity analysis under parallel trends violations
- :doc:`utils` - Standard parallel trends tests
