Core estimation¶
Estimator¶
MicroPURC estimator: the fixed-point map \(\Phi\) and its iteration.
- class micropurc.core.estimator.MicroPURCEstimator(network, forward_solver, estimation_config=None, active_set_config=None, projection_config=None)[source]¶
Bases:
objectEstimator for the microPURC model with quadratic perturbation.
Each trip contributes a projected least-squares block, and the estimate is a fixed point of
\[\Phi_N(\beta) = -H_N(\beta)^{-1} g_N(\beta), \qquad H_N = \sum_i Z_i^{\top} \hat P_i Z_i, \qquad g_N = \sum_i Z_i^{\top} \hat P_i (\mathrm{scale} \odot y_i),\]where the sums run over the trips whose active set survives grounding, \(Z_i\) is the design restricted to trip \(i\)’s active links, and \(\hat P_i\) projects onto the null space of that trip’s grounded active incidence. The dependence of \(\Phi_N\) on \(\beta\) runs through the active sets, which come from the forward solve, so \(\Phi_N\) is piecewise constant.
fit()drives \(\beta = \Phi_N(\beta)\) by damped Krasnoselskii-Mann iteration: the step is \(\beta + \lambda (\Phi_N(\beta) - \beta)\), with \(\lambda\) backtracked by a factorls_tauuntil the merit \(Q(\beta) = \tfrac12 \lVert \Phi_N(\beta) - \beta \rVert^2\) drops to or below the largest merit over the lastnm_windowiterations. Once the backtracking budget is spent, the smallest trial step is taken as is.- Parameters:
network (Network)
forward_solver (ForwardSolver)
estimation_config (EstimationConfig | None)
active_set_config (ActiveSetConfig | None)
projection_config (ProjectionConfig | None)
- __init__(network, forward_solver, estimation_config=None, active_set_config=None, projection_config=None)[source]¶
Bind the network and forward solver and precompute native arrays.
- Parameters:
network (Network) – The network the model is estimated on.
forward_solver (ForwardSolver) – Solver for the inner perturbed-utility flow problem.
estimation_config (EstimationConfig | None) – Iteration and tolerance settings; defaults used if omitted.
active_set_config (ActiveSetConfig | None) – Active-set detection settings; defaults if omitted.
projection_config (ProjectionConfig | None) – Range-space projection settings; defaults if omitted.
- Return type:
None
- phi(beta, y, b=None, *, origins=None, dests=None, lazy_design=None)[source]¶
Evaluate the fixed-point map \(\Phi_N(\beta)\).
- Parameters:
beta (ndarray) – Parameter vector to evaluate at, shape (K,).
y – Route indicators, dense (N, L) or sparse CSR (N, L).
b – Node imbalances, shape (N, V). Needed when
originsanddestsare absent.origins (ndarray | None) – Origin node indices, shape (N,).
dests (ndarray | None) – Destination node indices, shape (N,).
lazy_design (LazyDesign | None) – A lazy design evaluated on the fly by the native engine.
- Returns:
\(\Phi_N(\beta) = -H_N(\beta)^{-1} g_N(\beta)\), shape (K,).
- Return type:
- fit(y, b=None, beta_init=None, *, origins=None, dests=None, z_variants=None, trip_variant=None, lazy_design=None)[source]¶
Solve \(\beta = \Phi_N(\beta)\) from
beta_init.The iteration runs until the fixed-point residual meets
fp_tol, optionally scaled by \(\sqrt{K}\), or untilmax_iterationsis spent. Convergence additionally requires the condition number of the accumulated Hessian to stay withinmax_cond_H, which rules out a residual that goes small only because \(\Phi_N\) has flattened at a degenerate iterate. The final estimate is accumulated once more to obtain the scores and the covariance.- Parameters:
y – Route indicators, dense (N, L) or sparse CSR (N, L).
b – Node imbalances, shape (N, V). Needed when
originsanddestsare absent.beta_init (Sequence[float] | None) – Initial parameter guess, shape (K,); defaults to ones.
origins (ndarray | None) – Origin node indices, shape (N,).
dests (ndarray | None) – Destination node indices, shape (N,).
z_variants (ndarray | None) – Per-variant Z matrices, shape (n_variants, L, K).
trip_variant (ndarray | None) – Per-trip variant index, shape (N,). Required with z_variants.
lazy_design (LazyDesign | None) – A lazy design whose per-variant regressors are evaluated on the fly, keeping the (N, L, K) design out of memory. Mutually exclusive with z_variants.
- Returns:
A dict holding
beta_hat: the final iterate, shape (K,);se_hat,cov: the i.i.d. sandwich standard errors and covariance,Nonewhen the final accumulation yields no scores or a singular Hessian;u_scores: per-trip scores, shape (N, K), aligned with the input trips, a dropped trip carrying a zero row;kept_mask: length-N boolean, False at a dropped trip;hessian: the accumulated Hessian, shape (K, K);diagnostics: the per-run summary with the convergence verdict, the merit history, and the formatted convergence and active-set tables.
Clustered inference follows from passing
hessianandu_scorestocluster_robust_covariance()with length-N cluster labels: the zero rows keep dropped trips out of the meat, so the caller needs no drop bookkeeping.u_scoresandkept_maskareNonewhen the final accumulation returned no scores; if the survivor mask cannot be matched to those scores,u_scoreskeeps only the surviving rows andkept_maskstaysNone.- Raises:
ValueError – If z_variants is given without trip_variant, or if both lazy_design and z_variants are given.
- Return type:
Projection¶
One trip’s contribution to the estimating equations, via a Schur complement.
Every contribution is a contraction with the range-space projector
where \(\tilde A\) is the trip’s active incidence submatrix after one node row per connected component has been grounded. The C++ extension applies \(\hat P\) through a Cholesky factor of the \(m \times m\) Gram matrix \(\tilde A \tilde A^{\top}\), so the \(e \times e\) projector itself is never formed. This module adapts numpy arrays to that call and names the three outputs it returns.
- class micropurc.core.projection.TripContribution(H_i, g_i, u_i)[source]¶
Bases:
objectPer-trip contribution to the estimator.
- Variables:
H_i (numpy.ndarray) – Hessian block \(Z_{\mathrm{act}}^{\top} \hat P Z_{\mathrm{act}}\), shape (K, K).
g_i (numpy.ndarray) – Gradient block \(Z_{\mathrm{act}}^{\top} \hat P (\mathrm{scale} \odot y)\), shape (K,).
u_i (numpy.ndarray | None) – Score \(Z_{\mathrm{act}}^{\top} \hat P \big(\mathrm{scale} \odot (y - \hat x)\big)\), shape (K,);
Nonewhen the flows were withheld.
- Parameters:
- micropurc.core.projection.trip_contribution_schur(Z_act, A_local, y_act, scale_act, x_hat_act=None)[source]¶
Compute one trip’s
(H_i, g_i, u_i)via the native Schur solve.- Parameters:
Z_act (ndarray) – Link attributes on active edges, shape (e, K).
A_local (ndarray) – Active incidence submatrix, shape (m, e), dense, with one node row per connected component already dropped so that \(\tilde A \tilde A^{\top}\) is invertible.
y_act (ndarray) – Route indicator on active edges, shape (e,).
scale_act (ndarray) – Perturbation scale on active edges, shape (e,).
x_hat_act (ndarray | None) – Optimal flows on active edges, shape (e,). Supplying it requests the score
u_i;Noneleavesu_iunset and skips the extra right-hand side in the Schur solve.
- Returns:
The trip’s
H_i,g_i, andu_i.- Return type:
Covariance¶
Sandwich covariance estimation and cluster-robust standard errors.
- micropurc.core.covariance.sandwich_covariance(H, u_vectors)[source]¶
Compute the sandwich variance and its standard errors.
\[\mathrm{cov} = H^{-1} S H^{-1}, \qquad S = \sum_i u_i u_i^{\top},\]where each per-trip score \(u_i = Z_{\mathrm{act}}^{\top} \hat P \big(\mathrm{scale} \odot (y_i - \hat x_i)\big)\) is a \(K\)-vector, so the meat accumulates as a sum of rank-one terms.
- micropurc.core.covariance.cluster_robust_covariance(H, u_vectors, cluster_ids)[source]¶
Compute the cluster-robust sandwich variance and its standard errors.
Scores are summed within each cluster before the meat is formed, so within-cluster dependence is carried through to the variance:
\[S_{\mathrm{cl}} = \frac{J}{J-1} \sum_{j=1}^{J} \Big(\sum_{i \in j} u_i\Big) \Big(\sum_{i \in j} u_i\Big)^{\top}, \qquad \mathrm{cov} = H^{-1} S_{\mathrm{cl}} H^{-1},\]over \(J\) clusters. The CR1 finite-cluster scaling \(J/(J-1)\) is included here, so a caller that applies its own would double-count it.
Both
Hand the meat are accumulated over observations, so the returned covariance is already scaled as a sampling variance and needs no further division by the sample size.Wald intervals built from these standard errors take their critical value from \(t_{J-1}\).
- Parameters:
- Returns:
Cluster-robust covariance, shape (K, K). se: Cluster-robust standard errors, shape (K,).
- Return type:
cov
- Raises:
ValueError – If cluster_ids and u_vectors disagree in length, or if there are fewer than two clusters (the CR1 correction is undefined).
Active set¶
Active-set determination for a single trip.
A link carries flow when its dual slack is negative: with link costs \(c = Z\beta\) and the node potentials \(\lambda\) returned by the forward solve, link \(j\) is active when
The C++ extension performs the slack test, the acyclicity and
destination-reachability checks on the active subnetwork, and the
component-aware grounding that makes \(\tilde A \tilde A^{\top}\)
invertible. This module adapts numpy and Network
objects to those calls and keeps the Python result types.
- class micropurc.core.activeset.ActiveSet(indices, slack, margins, kept_node_rows, num_components, valid, drop_reason='')[source]¶
Bases:
objectResult of active-set determination for a single trip.
- Variables:
indices (numpy.ndarray) – Active (flow-carrying) link ids, those with \(s_j < 0\).
slack (numpy.ndarray) – Dual slack \(s_j = c_j + (A^{\top}\lambda)_j\) for every link, shape (L,).
margins (numpy.ndarray | None) – Per-link margin \(|s_j| / \lVert Z_j \rVert\), shape (L,). Slack is affine in \(\beta\) through \(c = Z\beta\), so with the node potentials held fixed this is the length of the smallest parameter perturbation that flips link \(j\) between active and inactive. Links with \(\lVert Z_j \rVert = 0\) carry
inf, so the trip’s margin is the minimum of the vector.Noneunlessz_rownormis supplied todetermine_active_set().kept_node_rows (numpy.ndarray) – Node rows of \(A\) retained for the range-space projection, one row per connected component having been dropped.
num_components (int) – Number of connected components in the active subnetwork.
valid (bool) – Whether the trip yields a usable active set.
drop_reason (str) – Why the trip was dropped, if
validis False.
- Parameters:
- micropurc.core.activeset.active_threshold(x, config)[source]¶
Compute the flow magnitude below which a link counts as carrying nothing.
The rule is
max(threshold_min, threshold_factor * x.max()), so the threshold follows the scale of the flow vector; an emptyxgivesthreshold_min. Route sampling uses this to clean numerically negligible flows before walking the support. Estimation reads activity from dual slack.- Parameters:
x (ndarray) – Link flows, shape (L,).
config (ActiveSetConfig) – Supplies
threshold_factorandthreshold_min.
- Returns:
The threshold, in flow units.
- Return type:
- micropurc.core.activeset.determine_active_set(x, dest, network, config, projection_mode='robust_pinv', lam_eq=None, c=None, scale_m=None, z_rownorm=None)[source]¶
Determine the active set for a single trip.
A link is active when \(s_j = c_j + (A^{\top}\lambda)_j\) falls below
-config.slack_tol. The active subnetwork is then checked for acyclicity and for reachability ofdest, and grounded one node row per connected component; a trip that fails any of these is returned withvalid=Falseand adrop_reason.- Parameters:
x (ndarray) – Optimal link flows (unused; kept for call-site symmetry).
dest (int) – Destination node id.
network (Network) – The network.
config (ActiveSetConfig) – Active-set detection settings (
slack_tol).projection_mode (str) – Range-space grounding mode (
robust_pinv).lam_eq (ndarray | None) – Equality-constraint duals (node potentials) from the solve.
c (ndarray | None) – Link cost vector for this trip (
Z_variant @ beta).scale_m (ndarray | None) – Per-link perturbation scale. Accepted by the native signature and unread by the slack test.
z_rownorm (ndarray | None) – Per-link design-row norms \(\lVert Z_j \rVert\) for this trip’s variant. When given, the returned
ActiveSetcarries the margins \(|s_j| / \lVert Z_j \rVert\); otherwisemarginsisNone.
- Returns:
The trip’s active set.
- Raises:
ValueError – If
lam_eq,c, orscale_mis missing, or ifz_rownormhas a length other than the number of links.- Return type:
Diagnostics¶
Convergence and active-set diagnostics for the estimator.
The estimator solves a fixed point whose map is piecewise constant in the active set of binding non-negativity constraints. This module records the per-iteration quantities of a run (residuals, merit values, step sizes, active-set churn, and dual-slack margins), classifies why a run fails to converge, and probes how sensitive the detected active set is to a perturbation of the parameter estimate.
The main entry points are:
DiagnosticsCollector– per-run iteration records, active-set churn, margin statistics, and non-convergence classification;ActiveSetReport– a per-replication summary of active-set behavior;ConvergenceAnalysis– aggregation of diagnostics across replications;active_set_stability_probe()– sensitivity of the active set to a perturbation of the estimate.
- class micropurc.core.diagnostics.NonConvergenceReason(value)[source]¶
Bases:
EnumClassification of why an estimation run failed to converge.
- Variables:
MAX_ITERATIONS – The iteration budget ran out while the residual was still moving, or the history is too short to classify.
STAGNATION – The residual plateaued.
OSCILLATION – The residual changes alternate in sign.
LINE_SEARCH_EXHAUSTION – The line search rejected most late steps.
H_CONDITIONING – The Hessian is ill-conditioned in the late iterations.
ACTIVE_SET_CYCLING – Active sets keep flipping while the residual holds still.
TRIP_DROP_INSTABILITY – The number of dropped trips fluctuates across iterations.
- MAX_ITERATIONS = 'max_iterations'¶
- STAGNATION = 'stagnation'¶
- OSCILLATION = 'oscillation'¶
- LINE_SEARCH_EXHAUSTION = 'ls_exhaustion'¶
- H_CONDITIONING = 'h_conditioning'¶
- ACTIVE_SET_CYCLING = 'aset_cycling'¶
- TRIP_DROP_INSTABILITY = 'trip_drop'¶
- class micropurc.core.diagnostics.IterationRecord(iteration, phi_residual, Q_merit, step_size, accepted, ls_tries, cache_hits=0, cache_misses=0, active_set_changes=0, link_flips=0, cond_H=0.0, min_eig_H=0.0, dropped_trips=0, wall_clock_s=0.0, beta=None, Q_ref=0.0)[source]¶
Bases:
objectPer-iteration diagnostic record.
The residual and merit describe the iterate the estimator entered the iteration with;
betais the iterate it left with.- Variables:
iteration (int) – Zero-based iteration counter.
phi_residual (float) – \(\lVert \Phi_N(\beta) - \beta \rVert\).
Q_merit (float) – Merit value \(Q = \tfrac12 \lVert \Phi_N(\beta) - \beta \rVert^2\).
step_size (float) – Damping factor applied to the fixed-point step.
accepted (bool) – Whether the line search found a step meeting the non-monotone acceptance test.
ls_tries (int) – Number of trial steps evaluated.
cache_hits (int) – Filled by callers that track their own caches; the native accumulation path leaves this at 0.
cache_misses (int) – Counterpart of
cache_hits.active_set_changes (int) – Filled by callers that track per-trip active-set changes; the native accumulation path leaves this at 0.
link_flips (int) – Number of trips whose active set differs from the previous iteration, backfilled by
DiagnosticsCollector.finalize_churn().cond_H (float) – Condition number of the accumulated Hessian.
min_eig_H (float) – Smallest eigenvalue of the accumulated Hessian.
dropped_trips (int) – Trips discarded during accumulation at this iterate.
wall_clock_s (float) – Seconds spent in this iteration.
beta (numpy.ndarray | None) – The iterate after this iteration’s step.
Q_ref (float) – Non-monotone reference level the trial step had to beat.
- Parameters:
- class micropurc.core.diagnostics.MarginStats(min_active, median_active, q05_active, q95_active, max_inactive, n_active_total, n_inactive_total)[source]¶
Bases:
objectSummary statistics of dual-slack margins for one iteration.
A link’s distance to flipping is \(|s_j|\). The active statistics are taken over \(|s_j|\) for links with \(s_j < 0\), pooled across the trips of one iteration; small values mark links on the verge of leaving the active set.
- Variables:
min_active (float) – Smallest \(|s_j|\) over active links,
infwhen no link is active.median_active (float) – Median \(|s_j|\) over active links.
q05_active (float) – 5th percentile of \(|s_j|\) over active links.
q95_active (float) – 95th percentile of \(|s_j|\) over active links.
max_inactive (float) – Largest slack over inactive links.
n_active_total (int) – Number of active links pooled over trips.
n_inactive_total (int) – Number of inactive links pooled over trips.
- Parameters:
- class micropurc.core.diagnostics.DropReasonCounts(no_active_links=0, trivial_flow=0, dag_connectivity_failed=0, not_dag=0, no_kept_rows=0, other=0)[source]¶
Bases:
objectPer-iteration counts of trip-drop reasons.
- Variables:
no_active_links (int) – No link passed the dual-slack test.
trivial_flow (int) – At most one link is active.
dag_connectivity_failed (int) – An active node fails to reach the destination.
not_dag (int) – The active subnetwork contains a directed cycle.
no_kept_rows (int) – Component grounding left no node row to project on.
other (int) – Reasons outside the buckets above.
- Parameters:
- class micropurc.core.diagnostics.DiagnosticsCollector(records=<factory>, active_set_sizes=<factory>, margin_stats=<factory>, drop_reasons=<factory>, peak_rss_kb=0, total_wall_s=0.0, native_time_solve_s=0.0, native_time_projection_s=0.0, native_time_schur_s=0.0, native_total_qp_iters=0, n_threads=0, _prev_trip_active=<factory>, _current_trip_active=<factory>, aset_hash_history=<factory>, per_trip_cumulative_churn=<factory>)[source]¶
Bases:
objectCollects per-iteration diagnostics for a single estimation run.
One collector belongs to one call of
fit(), which replaces it at the start of every fit.- Variables:
records (list[micropurc.core.diagnostics.IterationRecord]) – One
IterationRecordper iteration, in order.active_set_sizes (list[list[int]]) – Per-iteration lists of per-trip active-set sizes.
margin_stats (list[micropurc.core.diagnostics.MarginStats]) – Per-iteration dual-slack margin summaries.
drop_reasons (list[micropurc.core.diagnostics.DropReasonCounts]) – Per-iteration trip-drop counts by reason.
peak_rss_kb (int) – Peak resident set size in the raw platform unit; read it through
peak_rss_mb().total_wall_s (float) – Wall-clock seconds for the whole fit.
native_time_solve_s (float) – Forward-QP seconds, summed over passes and threads.
native_time_projection_s (float) – Projection seconds, summed the same way.
native_time_schur_s (float) – Schur-accumulation seconds, summed the same way.
native_total_qp_iters (int) – Interior-point iterations over the whole fit.
n_threads (int) – Worker threads the native pipeline reported.
aset_hash_history (list[numpy.ndarray]) – One per-trip active-set hash array per recorded evaluation, consumed by
finalize_churn().per_trip_cumulative_churn (dict[int, int]) – Trip id to the number of iterations at which that trip’s active set changed.
- Parameters:
records (list[IterationRecord])
margin_stats (list[MarginStats])
drop_reasons (list[DropReasonCounts])
peak_rss_kb (int)
total_wall_s (float)
native_time_solve_s (float)
native_time_projection_s (float)
native_time_schur_s (float)
native_total_qp_iters (int)
n_threads (int)
- records: list[IterationRecord]¶
- margin_stats: list[MarginStats]¶
- drop_reasons: list[DropReasonCounts]¶
- record_iteration(rec)[source]¶
Append one iteration’s record.
- Parameters:
rec (IterationRecord)
- Return type:
None
- record_active_set_sizes(sizes)[source]¶
Append the per-trip active-set sizes observed in one iteration.
- record_trip_active_set(trip_id, indices)[source]¶
Store one trip’s active link indices for the current evaluation.
- record_active_set_hashes(hashes)[source]¶
Append per-trip active-set hashes from one \(\Phi\) evaluation.
The estimator calls this once before the iteration loop and once per iteration. The arrays are only stored here;
finalize_churn()compares them in bulk once the loop is over, which keeps churn accounting off the hot path.- Parameters:
hashes (ndarray) – Per-trip active-set hash, shape (N,). A hash of 0 marks a trip that was dropped.
- Return type:
None
- flush_active_set_churn(*, detailed=False)[source]¶
Compare current against previous active sets; return total link flips.
This serves callers that register index sets through
record_trip_active_set(). Once any hash array has been recorded, churn is accounted for in bulk byfinalize_churn()and this returns zero.
- finalize_churn()[source]¶
Compute per-trip and per-iteration churn from the saved hash arrays.
Called once after the iteration loop. Populates
per_trip_cumulative_churnand backfillslink_flipson eachIterationRecord. A trip counts as churning between two evaluations when both of its hashes are nonzero and differ, so a trip dropped at either iterate is passed over.- Return type:
None
- record_margins(all_slacks)[source]¶
Compute and store one iteration’s margin statistics.
Slacks are pooled across trips and split at zero: negative entries are the active links and enter as \(|s_j|\), the rest are the inactive ones.
- record_drop_reasons(counts)[source]¶
Append one iteration’s drop-reason counts.
- Parameters:
counts (DropReasonCounts)
- Return type:
None
- snapshot_rss()[source]¶
Raise the recorded peak RSS to the process’s current peak.
Measurement is best effort: a platform that answers neither
getrusagenor the Win32 query leaves the previous value standing.- Return type:
None
- peak_rss_mb()[source]¶
Peak resident set size in MiB, normalized across platforms.
ru_maxrssis bytes on macOS and KiB on Linux, andpeak_rss_kbstoresru_maxrss // 1024, so its unit is KiB on macOS and MiB on Linux and Windows. This method converts whichever it holds to MiB.- Returns:
The peak resident set size in MiB.
- Return type:
- accumulate_native_stats(stats)[source]¶
Sum one native accumulate pass’s phase timings into the fit totals.
The estimator calls the native batch routine once per Krasnoselskii-Mann \(\Phi\) evaluation and once for the covariance pass, and each pass returns its own
time_solve_s/time_projection_s/time_schur_s/total_qp_iters. Adding them here makessummary()report totals over the whole fit.The phase timings are aggregate CPU-seconds summed over the worker threads, so under parallelism they exceed the wall-clock
total_wall_sby roughly the achieved thread utilization. They carry the relative cost split across solve, projection, and Schur;total_wall_scarries the runtime.- Parameters:
stats (dict) – One pass’s native statistics dict.
- Return type:
None
- static compute_cond_H(H)[source]¶
Compute the condition number and smallest eigenvalue of
H.His treated as symmetric, and the condition number is \(\lambda_{\max} / \max(|\lambda_{\min}|, 10^{-30})\), the floor keeping a singularHfinite.
- classify_non_convergence(stagnation_window=20, stagnation_tol=0.0001, oscillation_window=10, oscillation_frac=0.6, ls_exhaustion_window=10, ls_exhaustion_frac=0.7, cond_threshold=1000000000000.0, churn_window=10, churn_threshold=50, drop_cv_threshold=0.3)[source]¶
Classify why convergence failed from the iteration history.
The tests run in a fixed order and the first match wins: Hessian conditioning, line-search exhaustion, trip-drop instability, active-set cycling, oscillation, then stagnation. A history that matches none of them, or that holds fewer than three records, is reported as an exhausted iteration budget.
- Parameters:
stagnation_window (int) – Length of the trailing window shared by the conditioning, line-search, drop, and stagnation tests.
stagnation_tol (float) – Relative residual change below which the residual counts as flat, used by the stagnation and cycling tests.
oscillation_window (int) – Number of trailing residual differences inspected for sign changes.
oscillation_frac (float) – Fraction of those differences that must alternate.
ls_exhaustion_window (int) – Shortest trailing window for which the rejection test applies.
ls_exhaustion_frac (float) – Fraction of rejected steps that triggers.
cond_threshold (float) – Condition number above which the Hessian is blamed.
churn_window (int) – Trailing iterations used by the cycling test.
churn_threshold (int) – Mean per-iteration link flips that count as churn.
drop_cv_threshold (float) – Coefficient of variation of the dropped-trip counts above which drops count as unstable.
- Returns:
The reason, paired with an evidence dict holding the metrics that triggered it.
- Return type:
- summary()[source]¶
Produce a summary dict for diagnostics output.
The
convergedentry is a residual-only verdict: the finalphi_residualat or below1e-3. When it comes out False the summary also carries anon_convergence_reasonand its evidence.fit()overwritesconvergedwith its own verdict, which uses the configured fixed-point tolerance and the Hessian conditioning bound.- Returns:
The summary, empty when no iteration was recorded.
- Return type:
- format_convergence_table()[source]¶
Render the per-iteration convergence diagnostics as a text table.
- Returns:
One header line, then one line per iteration carrying the residual, the merit and its non-monotone reference, the step size, the line-search tries and outcome, the cache and churn counters, the dropped trips, the Hessian conditioning, and wall-clock seconds.
- Return type:
- format_activeset_table()[source]¶
Render the per-iteration active-set diagnostics as a text table.
Rows cover the iterations for which active-set sizes were recorded, so the table is empty when only hashes were collected.
- Returns:
One header line, then one line per iteration carrying the trip count, the median, smallest and largest active-set size, the smallest and median active margin, and the churn counter.
- Return type:
- class micropurc.core.diagnostics.ActiveSetReport(converged, num_iterations, final_residual, beta_hat, non_convergence_reason, link_flips_per_iter, residual_per_iter, margin_stats_per_iter, per_trip_cumulative_flips)[source]¶
Bases:
objectPer-run summary of active-set behavior.
Records the per-iteration churn, dual-slack margin distributions, and the non-convergence classification for one estimation run.
- Variables:
converged (bool) – Whether the run reached the fixed-point tolerance.
num_iterations (int) – Total iterations executed.
final_residual (float) – \(\lVert \Phi(\beta) - \beta \rVert\) at the last iteration.
beta_hat (numpy.ndarray) – The final parameter estimate.
non_convergence_reason (str | None) – The
NonConvergenceReasonvalue for a run that failed to converge,Noneotherwise.link_flips_per_iter (list[int]) – Per-iteration total link flips across all trips.
residual_per_iter (list[float]) – Per-iteration \(\lVert \Phi(\beta) - \beta \rVert\).
margin_stats_per_iter (list[micropurc.core.diagnostics.MarginStats]) – Per-iteration dual-slack margin distributions.
per_trip_cumulative_flips (dict[int, int]) – Cumulative link flips per trip, which identifies the trips driving most of the churn.
- Parameters:
- margin_stats_per_iter: list[MarginStats]¶
- class micropurc.core.diagnostics.ConvergenceAnalysis(reports)[source]¶
Bases:
objectAggregate active-set reports across replications and sample sizes.
Cross-tabulates the non-convergence reasons against the sample size and computes summary statistics over replications.
- Parameters:
reports (dict[int, list[ActiveSetReport]])
- __init__(reports)[source]¶
Store the per-sample-size replication reports.
- Parameters:
reports (dict[int, list[ActiveSetReport]]) – Mapping from sample size
Nto the list of per-replication reports at that size.- Return type:
None
- churn_after_iteration(after_iter=3)[source]¶
Fraction of runs with at least one link flip after a given iteration.
- class micropurc.core.diagnostics.StabilityProbeResult(n_trips, n_directions, radii, trips_changed, links_flipped)[source]¶
Bases:
objectResult of the active-set stability probe.
- Variables:
n_trips (int) – Number of trips probed.
n_directions (int) – Random directions drawn per radius.
radii (list[float]) – Perturbation radii, in the units of the parameter norm.
trips_changed (dict[float, list[int]]) – Radius to a per-trip list holding the largest active-set symmetric difference seen over the directions; a direction that makes the trip invalid contributes 1.
links_flipped (dict[float, list[int]]) – Radius to the symmetric-difference sizes pooled over all trips and directions that changed at all.
- Parameters:
- micropurc.core.diagnostics.active_set_stability_probe(network, solver, beta_hat, y, b, active_set_config, projection_mode='robust_pinv', radii=None, n_directions=5, seed=42)[source]¶
Perturb \(\hat\beta\) and measure how stable the active set is.
For every radius and every random unit direction the estimate is perturbed, the forward problem is re-solved for each trip, and the resulting active set is compared with the one at \(\hat\beta\). Trips whose reference active set is invalid are skipped, and a perturbed solve that turns invalid is scored as a change of size one.
- Parameters:
network – Network supplying the incidence and the design
Z.solver – Forward solver for the inner QP.
beta_hat (ndarray) – Parameter estimate to perturb, shape (K,).
y (ndarray) – Route indicators, shape (N, L); only the trip count is read.
b (ndarray) – Node imbalances, shape (N, V); each destination is its
argmax.active_set_config – Active-set detection settings.
projection_mode (str) – Range-space grounding mode.
radii (list[float] | None) – Perturbation radii. Defaults to
[1e-4, 1e-3, 1e-2, 1e-1]scaled by the norm ofbeta_hat.n_directions (int) – Random unit directions drawn per radius.
seed (int) – Seed for drawing the directions.
- Returns:
The per-radius active-set changes and flip counts.
- Return type:
Lazy design¶
Lazy, term-coded design matrix for large problems.
A LazyDesign holds per-link attribute columns, per-variant covariates,
and a compact table of terms that the native engine evaluates on the fly, so
the (N, L, K) design is never held in memory. Each term contributes one
parameter’s column, evaluated per link and per trip variant:
TERM_COLUMN(a)— link attribute columna;TERM_PRODUCT(a, b)— product of columnsaandb(interaction);TERM_COVARIATE_INDICATOR(a, cov, op, thr)— columnagated by the indicator1[covariate[cov] op thr].
Link attributes may vary across an attribute-variant axis (for instance,
time-of-day); variant_attr maps each trip variant to its attribute-variant
row, and variant_covariates holds the covariate values used by indicator
terms. Build one directly, or via micropurc.spec.compile_design() with
materialize=False.
- class micropurc.core.lazy_design.LazyDesign(link_attrs, variant_attr, variant_covariates, trip_variant, term_code, term_a, term_b, term_cov, term_op, term_thr)[source]¶
Bases:
objectA term-coded design evaluated lazily by the native engine.
- Variables:
link_attrs (numpy.ndarray) – Attribute columns, shape
(n_attr_variants, L, A).variant_attr (numpy.ndarray) – Attribute-variant row for each variant, shape
(n_var,).variant_covariates (numpy.ndarray) – Covariate values per variant, shape
(n_var, C).trip_variant (numpy.ndarray) – Variant index for each trip, shape
(N,).term_code (numpy.ndarray) – Opcode per term, shape
(K,).term_a (numpy.ndarray) – Primary attribute column per term, shape
(K,).term_b (numpy.ndarray) – Second column for product terms, shape
(K,).term_cov (numpy.ndarray) – Covariate index for indicator terms, shape
(K,).term_op (numpy.ndarray) – Comparison code for indicator terms, shape
(K,).term_thr (numpy.ndarray) – Threshold for indicator terms, shape
(K,).
- Parameters: