Sensitivity, Switching Values, and Monte Carlo PAM Analysis

Chiranjit Mazumder, Himadri Sekhar Roy, Utkarsh Tiwari, Pramit Pandit, and Bikramjeet Ghose

2026-08-30

library(agriPAM)
crops <- agri_pam_example()

Why uncertainty matters

PAM results depend on yields, observed and border prices, conversion factors, exchange rates, transport margins, and shadow prices for domestic factors. Reporting only a point estimate can conceal whether the policy conclusion is stable or rests on a narrow assumption.

agriPAM offers three complementary tools:

  1. a deterministic path for one component;
  2. a switching value for a specified decision threshold; and
  3. Monte Carlo simulation for joint uncertainty.

Deterministic sensitivity paths

changes are proportional. Thus -0.15 reduces a component by 15 percent and 0.10 increases it by 10 percent.

s <- pam_sensitivity(
  crops,
  parameter = "social_revenue",
  changes = seq(-0.20, 0.20, by = 0.05),
  index = "Paddy"
)
s$results[, c("percent_change", "social_profit", "drc", "scb")]
#>   percent_change social_profit       drc       scb
#> 1            -20           100 0.9985935 0.9990165
#> 2            -15          6455 0.9166613 0.9402508
#> 3            -10         12810 0.8471543 0.8880147
#> 4             -5         19165 0.7874452 0.8412771
#> 5              0         25520 0.7355988 0.7992132
#> 6              5         31875 0.6901580 0.7611554
#> 7             10         38230 0.6500046 0.7265575
#> 8             15         44585 0.6142666 0.6949680
#> 9             20         50940 0.5822536 0.6660110

The complete results data frame also contains the six scenario components, all policy transfers, and every standard indicator. This makes a sensitivity table reproducible without manually recomputing selected ratios.

Switching values

A switching value answers a sharper question: how far must one assumption move before a conclusion changes? Profit metrics default to a target of zero and ratio metrics default to a target of one.

switching_value(
  crops,
  parameter = "social_revenue",
  metric = "drc",
  index = "Paddy"
)
#> <agri_pam_switch> Paddy
#>     id      parameter metric target found     change percent_change multiplier
#>  Paddy social_revenue    drc      1  TRUE -0.2007868      -20.07868  0.7992132
#>  base_value switched_value achieved
#>      127100         101580        1

switching_value(
  crops,
  parameter = "private_revenue",
  metric = "private_profit",
  index = "Paddy"
)
#> <agri_pam_switch> Paddy
#>     id       parameter         metric target found     change percent_change
#>  Paddy private_revenue private_profit      0  TRUE -0.2236629      -22.36629
#>  multiplier base_value switched_value achieved
#>   0.7763371     123400          95800        0

The reported percent_change is relative to the base component. A result with found = FALSE means the target was not reached inside the requested search interval; it does not prove that no switching value exists outside it.

Monte Carlo analysis

For a positive component with arithmetic mean \(m\) and coefficient of variation \(c\), the lognormal option uses

\[ \sigma_{\log} = \sqrt{\log(1+c^2)}, \qquad \mu_{\log} = \log(m) - \frac{1}{2}\sigma_{\log}^2. \]

This parameterisation preserves the supplied PAM component as the arithmetic mean. Zero means or zero coefficients of variation remain fixed. The normal option uses mean \(m\) and standard deviation \(mc\) and truncates negative draws at zero.

cv <- c(
  private_revenue = 0.08,
  private_tradable_inputs = 0.10,
  private_domestic_factors = 0.07,
  social_revenue = 0.15,
  social_tradable_inputs = 0.10,
  social_domestic_factors = 0.08
)

mc <- pam_monte_carlo(
  crops,
  cv = cv,
  n = 2000,
  seed = 2026,
  index = "Paddy"
)

mc$summary
#>            metric         mean           sd       q_0_025      q_0_500
#> 1  private_profit 2.757429e+04 1.136905e+04  5.492164e+03 2.745462e+04
#> 2   social_profit 2.582117e+04 2.077052e+04 -1.036769e+04 2.471165e+04
#> 3             drc 7.652938e-01 1.735021e-01  4.894414e-01 7.414161e-01
#> 4             pcr 7.189008e-01 9.425060e-02  5.629056e-01 7.095103e-01
#> 5            npco 9.930934e-01 1.742997e-01  6.908994e-01 9.785222e-01
#> 6            npci 9.364016e-01 1.336912e-01  7.036555e-01 9.253426e-01
#> 7             epc 1.027066e+00 2.484819e-01  6.234390e-01 9.990766e-01
#> 8              pc 3.735929e+00 1.416011e+02 -9.785765e+00 8.940732e-01
#> 9             srp 3.790863e-02 1.927674e-01 -3.033174e-01 2.313398e-02
#> 10            scb 8.161702e-01 1.360641e-01  5.807985e-01 8.042995e-01
#>         q_0_975
#> 1  4.939118e+04
#> 2  7.137099e+04
#> 3  1.167401e+00
#> 4  9.317761e-01
#> 5  1.374614e+00
#> 6  1.219356e+00
#> 7  1.616590e+00
#> 8  1.078922e+01
#> 9  4.539446e-01
#> 10 1.112545e+00
mc$probabilities
#>                   criterion probability
#> 1 private_profit_above_zero       0.992
#> 2  social_profit_above_zero       0.899
#> 3             drc_below_one       0.899
#> 4             pcr_below_one       0.992

The decision probabilities include private and social profit above zero and DRC and PCR below one. They are simulation probabilities under the supplied model, not frequentist significance levels.

Correlated shocks

Independent shocks can be unrealistic when private and social revenues share a yield shock or several costs share an exchange-rate shock. A positive semidefinite correlation matrix can be supplied for the six latent normal shocks.

components <- c(
  "private_revenue", "private_tradable_inputs", "private_domestic_factors",
  "social_revenue", "social_tradable_inputs", "social_domestic_factors"
)
correlation <- diag(6)
dimnames(correlation) <- list(components, components)
correlation["private_revenue", "social_revenue"] <- 0.70
correlation["social_revenue", "private_revenue"] <- 0.70

mc_correlated <- pam_monte_carlo(
  crops,
  cv = cv,
  n = 1000,
  correlation = correlation,
  seed = 2026,
  index = "Paddy"
)
mc_correlated$probabilities
#>                   criterion probability
#> 1 private_profit_above_zero       0.993
#> 2  social_profit_above_zero       0.919
#> 3             drc_below_one       0.919
#> 4             pcr_below_one       0.993

Correlation values should come from data, a defensible elicitation procedure, or transparent scenarios. They should not be tuned to obtain a preferred policy conclusion.

Reproducibility and reporting

When seed is supplied, pam_monte_carlo() restores the caller’s random-number state on exit. A report should retain the seed, number of draws, distribution, component-specific coefficients of variation, correlations, quantiles, and decision thresholds. It should also compare the simulated means with the base PAM and explain any material difference caused by truncation or non-linearity.