mixle.analysis.objective module

J6 – priced-liability and hard-constraint assembly for a MILP-based selection objective: wiring, not new physics (work-plan Sec.7-J, the grand synthesis).

A general pattern for any per-item binary-selection MILP (mixle.stochastic_opt. two_stage_stochastic_plan() is this module’s worked instantiation, turning grade uncertainty – IC-1 Posterior.samples – into a CVaR-penalized block-extraction decision): several independent “pricing” tasks each want to add one more externality term or hard constraint to the same objective without knowing about each other – J2’s monte_carlo_npv (revenue/cost DCF), a G9-style no-mine/ buffer zone, a K6-style public-health exposure cost, an L6-style carbon/emissions price, an N6-style biodiversity offset (mixle.analysis.biodiversity.habitat_offset_liability/no_net_loss_constraint, already landed and explicitly written against this module’s shape). None of those tasks are this module’s dependency – this is the reverse: J6 is the pluggable framework they register priced terms/constraints INTO, so the dependency edge runs G9/K6/L6/N6 -> J6, never J6 -> them (workstream-J.md J6 header note). That means this module only fixes the shape a priced term or a hard constraint must have to plug in; it derives none of the underlying cost models itself (Non-goals).

Two pieces:

  • priced_liabilities() – nets a plan’s per-block environmental/health/carbon externalities (remediation, health, carbon) into one additive per-block dollar array ("total") plus the raw per-term breakdown, in exactly the shape mixle.stochastic_opt.risk_adjusted_plan() consumes via its liabilities parameter (liabilities["total"], subtracted from per-block revenue before the MILP solves) – and the same shape any other priced term (N6’s habitat_offset_liability, or a future workstream’s) adds itself into, without ever importing this module.

  • hard_constraints() – assembles the constraints dict risk_adjusted_plan consumes: an optional hard no_mine_mask (G9 no-mine/buffer polygons – blocks forced to x_b = 0) and an optional list of linear activity caps (K6 exposure budgets, L6 water budgets – coeffs @ x <= bound rows over the block decision vector), normalizing any ">="-sense cap into the solver’s standard <= convention.

hard_constraints(*, no_mine_mask=None, caps=None)[source]

Assemble the constraints dict mixle.stochastic_opt.risk_adjusted_plan() consumes.

  • no_mine_mask: an optional boolean array-like, length n_blocks. True entries are a G9-style no-mine/buffer-zone polygon’s enclosed blocks – hard-fixed to x_b = 0 (stronger than an inequality row: those blocks can never be selected, regardless of the rest of the objective).

  • caps: an optional list of linear activity caps – a K6 exposure budget, an L6 water budget, or any other coeffs @ x <sense> bound row over the block decision vector. Each entry is a dict with "coeffs" (length-n_blocks array-like) and "bound" (float), plus an optional "sense" ("<=" by default; ">=" is negated into the solver’s standard <= form so risk_adjusted_plan never has to special-case it). This is the same coeffs/bound/ sense field naming mixle.analysis.biodiversity.no_net_loss_constraint() already uses for its own linear-constraint payload.

Returns a dict with whichever of "no_mine_mask"/"caps" were supplied (both are omitted, i.e. an empty dict, when neither argument is given – meaning no hard constraint at all).

Raises:

ValueError – if no_mine_mask is not a genuine 1-D boolean array (data that is merely truthy under an implicit bool cast, e.g. a float array or a NaN entry, is rejected rather than silently coerced), if any cap’s "coeffs" is not a finite 1-D array, if any cap’s "bound" is not finite, or if a cap’s "sense" is not "<="/">=".

Parameters:
Return type:

dict

priced_liabilities(plan, *, carbon_price, health_cost, remediation_cost)[source]

Net a plan’s per-block environmental/health/carbon liabilities into one additive dollar array.

plan is a mapping or attribute-bearing object exposing three per-block, length-n_blocks array-likes:

  • grade: the block grade (or other geology/complexity proxy) fed to remediation_cost – a G9-style environmental remediation-cost model keyed on what is actually mined.

  • exposure: the block’s public-health exposure proxy fed to health_cost – a K6-style health-cost model.

  • emissions: the block’s carbon-equivalent emissions, priced directly at carbon_price (an L6-style market/regulatory carbon price is a multiplier, not a fitted cost model, so no callable is needed for it).

Returns a dict with per-block arrays "remediation", "health", "carbon", their elementwise sum "total" (the single per-block liability mixle.stochastic_opt.risk_adjusted_plan() nets out of revenue), and the scalar roll-ups "remediation_total"/"health_total"/ "carbon_total"/"grand_total" for reporting.

Every plan field is validated BEFORE it is handed to a cost callable (MXR-080-1573). The callables are opaque user code: a model that ignores its argument, saturates, or returns a constant turns a NaN grade or an infinite exposure into a perfectly ordinary finite cost, so the invalid source evidence disappears and the resulting liability record carries no trace that the plan it was priced from was malformed. grade/exposure are checked for finiteness only, not sign – both are declared as arbitrary geology/complexity and exposure proxies, so a negative value is a legitimate reading; it is only the prices that carry the non-negativity convention.

Every assembled output is likewise validated after the fact: three individually finite per-block costs near the float64 ceiling can overflow total, and finite per-block totals can overflow grand_total’s reduction, publishing an infinite liability from a record that claims to be validated (MXR-080-1573).

Raises:

ValueError – if carbon_price, plan’s emissions, or either cost callable’s return value is non-finite or negative – a negative liability would net out of revenue as a profit increase rather than a cost, and a NaN would propagate unguarded into the objective – if plan’s grade/exposure is non-finite, if any of plan’s grade/exposure/emissions or a cost callable’s return value has the wrong shape, or if the netted per-block total or any scalar roll-up is not representable in float64.

Parameters:
Return type:

dict