-
Notifications
You must be signed in to change notification settings - Fork 0
[WIP] Add tolerance constructor to approximation configs #105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b7b0267
8bbb1f7
bdcb3b2
c0b6865
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,14 +22,83 @@ abstract type BilinearApproxConfig end | |
| Config for Bin2 bilinear approximation using z = ½((x+y)² − x² − y²). | ||
|
|
||
| # Fields | ||
| - `quad_config::QuadraticApproxConfig`: quadratic method used for x², y², and (x+y)² | ||
| - `quad_config::Q`: quadratic method used for x², y², and (x+y)² | ||
| - `add_mccormick::Bool`: whether to add reformulated McCormick cuts through separable variables (default true) | ||
|
|
||
| The Q type parameter lets tolerance helpers dispatch on the inner quad method; | ||
| see `tolerance_depth(::Type{Bin2Config{Q}}; …)`. | ||
| """ | ||
| struct Bin2Config <: BilinearApproxConfig | ||
| quad_config::QuadraticApproxConfig | ||
| struct Bin2Config{Q <: QuadraticApproxConfig} <: BilinearApproxConfig | ||
| quad_config::Q | ||
| add_mccormick::Bool | ||
|
|
||
| Bin2Config( | ||
| quad_config::Q; | ||
| add_mccormick::Bool = true, | ||
| ) where {Q <: QuadraticApproxConfig} = | ||
| new{Q}(quad_config, add_mccormick) | ||
| end | ||
|
|
||
| # --- Tolerance helpers --- | ||
| # | ||
| # Notation: Δx, Δy are domain lengths (Δx = x_max − x_min); ε denotes errors. | ||
| # | ||
| # Bilinear identity: xy = ½((x+y)² − x² − y²). | ||
| # Approximation: z = ½(z_p − z_x − z_y), where z_• is the inner-quad | ||
| # approximation of •² and (•)² for • ∈ {x, y, x+y}. | ||
| # | ||
| # Let ε_x = x² − z_x, ε_y = y² − z_y, ε_p = (x+y)² − z_p be the per-term | ||
| # inner-quad errors. For one-sided-over inner quads (Sawtooth, SolverSOS2, | ||
| # ManualSOS2), each ε_• ∈ [0, ε_•^max] where the bound scales as Δ²·c at | ||
| # depth L (c is the inner quad's per-unit error coefficient): | ||
| # ε_x^max = Δx²·c, ε_y^max = Δy²·c, ε_p^max = (Δx+Δy)²·c. | ||
| # | ||
| # Substituting into z − xy yields z − xy = ½(ε_x + ε_y − ε_p). | ||
| # With each ε_• ∈ [0, ε_•^max], the range of z − xy is | ||
| # ½(0 + 0 − ε_p^max) ≤ z − xy ≤ ½(ε_x^max + ε_y^max − 0), | ||
| # so |z − xy| ≤ max(½ε_p^max, ½(ε_x^max + ε_y^max)). | ||
| # | ||
| # Now (Δx+Δy)² = Δx² + 2ΔxΔy + Δy² ≥ Δx² + Δy² (since Δx, Δy ≥ 0), so | ||
| # ε_p^max ≥ ε_x^max + ε_y^max, and the max collapses to ½ε_p^max. | ||
| # To hit user-target τ, ask the inner quad for ε_p^max ≤ 2τ on Δx+Δy. | ||
|
|
||
| """ | ||
| tolerance_depth(::Type{Bin2Config{Q}}; tolerance, max_delta_x, max_delta_y)::Int | ||
|
|
||
| Inner-quad depth such that Bin2's worst-case overestimation gap is ≤ `tolerance`. | ||
| Derivation: see the comment block above. Forwards to | ||
| `tolerance_depth(Q; tolerance = 2·τ, max_delta = Δx + Δy)`. | ||
|
|
||
| Defined for one-sided-over inner quads: `SawtoothQuadConfig`, `SolverSOS2QuadConfig`, | ||
| `ManualSOS2QuadConfig`, `NMDTQuadConfig`, `DNMDTQuadConfig`. `EpigraphQuadConfig` | ||
| is excluded — it is one-sided-under, so the sign of `ε_p` flips and the bound | ||
| above no longer applies; an Epigraph inner quad can drive `z` arbitrarily far | ||
| from `xy` under MIN/MAX objectives. | ||
|
|
||
| **Caveat for NMDT/DNMDT inner Q**: these are only one-sided-over when their | ||
| `epigraph_depth = 0`. With `epigraph_depth > 0`, the inner result becomes free | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this true that they are one-sided when epi_depth=0? I don't agree intuitively. And if this is true, shouldn't this mean we can use this with HybS? |
||
| in `[epigraph(x), nmdt(x)]`, which crosses `x²` and breaks the derivation. Pass | ||
| NMDT/DNMDT inner Qs with `epigraph_depth = 0` only. | ||
| """ | ||
| function tolerance_depth( | ||
| ::Type{Bin2Config{Q}}; | ||
| tolerance::Float64, | ||
| max_delta_x::Float64, | ||
| max_delta_y::Float64, | ||
| ) where { | ||
| Q <: Union{ | ||
| SawtoothQuadConfig, | ||
| SolverSOS2QuadConfig, | ||
| ManualSOS2QuadConfig, | ||
| NMDTQuadConfig, | ||
| DNMDTQuadConfig, | ||
| }, | ||
| } | ||
| return tolerance_depth(Q; | ||
| tolerance = 2 * tolerance, | ||
| max_delta = max_delta_x + max_delta_y, | ||
| ) | ||
| end | ||
| Bin2Config(quad_config::QuadraticApproxConfig) = Bin2Config(quad_config, true) | ||
|
|
||
| # --- Unified bilinear approximation dispatch --- | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,21 +9,113 @@ struct HybSBoundConstraint <: ConstraintType end | |
| """ | ||
| Config for HybS (Hybrid Separable) bilinear approximation. | ||
|
|
||
| Combines Bin2 lower bound and Bin3 upper bound with shared quadratic for x², y² | ||
| and LP-only epigraph for (x+y)², (x−y)². | ||
| Adds two inequalities that sandwich `z ≈ x·y`: | ||
| - lower: `z ≥ ½(z_p1 − z_x − z_y)` where `z_p1 ≤ (x+y)²` is an epigraph (LP-only) | ||
| lower bound and `z_x ≥ x²`, `z_y ≥ y²` come from the inner quadratic `Q`. | ||
| - upper: `z ≤ ½(z_x + z_y − z_p2)` where `z_p2 ≤ (x−y)²` is an epigraph (LP-only) | ||
| lower bound on the cross-difference. | ||
|
|
||
| # Fields | ||
| - `quad_config::QuadraticApproxConfig`: quadratic method used for the shared x² and y² terms | ||
| - `epigraph_depth::Int`: depth for the epigraph Q^{L1} LP-only approximation of cross-terms (x±y)² | ||
| - `quad_config::Q`: quadratic method for the shared x² and y² terms | ||
| - `epigraph_depth::Int`: depth for the epigraph approximation of cross-terms (x±y)² | ||
| - `add_mccormick::Bool`: whether to add standard McCormick envelope cuts on the product variable (default false) | ||
|
|
||
| `Q` must be a one-sided-over quadratic approximation (so `z_x ≥ x²` and | ||
| `z_y ≥ y²` hold). The reason is asymmetry: `z_x` and `z_y` appear with sign `−` | ||
| in the lower bound and sign `+` in the upper bound, so if `z_x` could | ||
| *under*-estimate `x²` then the `−z_x` term in the lower can drive `lower > xy` | ||
| and the sandwich is invalid. This rules out `EpigraphQuadConfig` (one-sided | ||
| under) and the two-sided `NMDTQuadConfig` / `DNMDTQuadConfig` for the tolerance | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So are they one sided or two sided, the nmdt quads? |
||
| helpers below; only `SawtoothQuadConfig`, `SolverSOS2QuadConfig`, and | ||
| `ManualSOS2QuadConfig` are supported. See `tolerance_depth(::Type{HybSConfig{Q}}; | ||
| …)` and `tolerance_epigraph_depth(::Type{HybSConfig{Q}}; …)`. | ||
| """ | ||
| struct HybSConfig <: BilinearApproxConfig | ||
| quad_config::QuadraticApproxConfig | ||
| struct HybSConfig{Q <: QuadraticApproxConfig} <: BilinearApproxConfig | ||
| quad_config::Q | ||
| epigraph_depth::Int | ||
| add_mccormick::Bool | ||
|
|
||
| HybSConfig( | ||
| quad_config::Q; | ||
| epigraph_depth::Int, | ||
| add_mccormick::Bool = false, | ||
| ) where {Q <: QuadraticApproxConfig} = | ||
| new{Q}(quad_config, epigraph_depth, add_mccormick) | ||
| end | ||
|
|
||
| # --- Tolerance helpers --- | ||
| # | ||
| # Notation: Δx, Δy are domain lengths; ε denotes errors. Let | ||
| # ε_q = max(x² − z_x, y² − z_y, 0) inner-quad one-sided-over error | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace your math explanation with mine: Since
We are using the same approximation for both inner quadratics, and the same for both
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other docstrings should be notationally consistent with what I've used here, if not already.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And also, if you can modify the other docstrings to approach a similar proof style as I've done, do that too. |
||
| # ε_e = max((x+y)² − z_p1, (x−y)² − z_p2, 0) epigraph one-sided-under error | ||
| # both ≥ 0 by construction. | ||
| # | ||
| # Where do the lower/upper expressions come from? Plug z_p1 = (x+y)² − ε_p1, | ||
| # z_x = x² + ε_x, z_y = y² + ε_y into the lower-bound inequality: | ||
| # z ≥ ½(z_p1 − z_x − z_y) | ||
| # = ½((x+y)² − ε_p1 − x² − ε_x − y² − ε_y) | ||
| # = ½(2xy − ε_p1 − ε_x − ε_y) | ||
| # = xy − ½(ε_p1 + ε_x + ε_y) | ||
| # So lower − xy ∈ [−ε_q − ½ε_e, 0]. Similarly substituting into | ||
| # z ≤ ½(z_x + z_y − z_p2) gives upper − xy ∈ [0, ε_q + ½ε_e]. Combining, | ||
| # |z − xy| ≤ ε_q + ½ε_e. | ||
| # | ||
| # The total error is ε_q + ½ε_e. To meet τ, the helpers below allocate | ||
| # ε_q ≤ τ/2 → inner Q at tolerance τ/2 over max_delta = max(Δx, Δy) | ||
| # ε_e ≤ τ → epigraph at tolerance τ over max_delta = Δx + Δy | ||
| # so the sum is ≤ τ/2 + ½·τ = τ. The 50/50 split between the two error | ||
| # sources is an arbitrary design choice — any other allocation (e.g. 30/70) | ||
| # that keeps the sum ≤ τ would also be valid; 50/50 is chosen for simplicity. | ||
| # | ||
| # Restricted to one-sided-over Q (see struct docstring for the asymmetry | ||
| # argument). Other Q raise MethodError. | ||
|
|
||
| """ | ||
| tolerance_depth(::Type{HybSConfig{Q}}; tolerance, max_delta_x, max_delta_y)::Int | ||
|
|
||
| Inner-quad depth for HybS at target tolerance `τ`. Returns the smallest depth | ||
| whose inner-quad error on `[ax, ax+Δx]` (and `[ay, ay+Δy]`) is `≤ τ/2`, which | ||
| satisfies the `ε_q ≤ τ/2` half of the HybS budget. | ||
|
|
||
| Only defined for `Q ∈ {SawtoothQuadConfig, SolverSOS2QuadConfig, | ||
| ManualSOS2QuadConfig}` — the one-sided-over inner quads for which the HybS | ||
| sandwich is valid. Other Q raise `MethodError`. | ||
|
|
||
| See also `tolerance_epigraph_depth(::Type{HybSConfig{Q}}; …)` for the second knob. | ||
| """ | ||
| function tolerance_depth( | ||
| ::Type{HybSConfig{Q}}; | ||
| tolerance::Float64, | ||
| max_delta_x::Float64, | ||
| max_delta_y::Float64, | ||
| ) where {Q <: Union{SawtoothQuadConfig, SolverSOS2QuadConfig, ManualSOS2QuadConfig}} | ||
| return tolerance_depth(Q; | ||
| tolerance = tolerance / 2, | ||
| max_delta = max(max_delta_x, max_delta_y), | ||
| ) | ||
| end | ||
|
|
||
| """ | ||
| tolerance_epigraph_depth(::Type{HybSConfig{Q}}; tolerance, max_delta_x, max_delta_y)::Int | ||
|
|
||
| Epigraph depth for HybS at target tolerance `τ`. Returns the smallest depth | ||
| whose epigraph error on the cross-term range `Δx + Δy` is `≤ τ`, which | ||
| satisfies the `½ε_e ≤ τ/2` half of the HybS budget. | ||
|
|
||
| Only defined for `Q ∈ {SawtoothQuadConfig, SolverSOS2QuadConfig, | ||
| ManualSOS2QuadConfig}`. Other Q raise `MethodError`. | ||
| """ | ||
| function tolerance_epigraph_depth( | ||
| ::Type{HybSConfig{Q}}; | ||
| tolerance::Float64, | ||
| max_delta_x::Float64, | ||
| max_delta_y::Float64, | ||
| ) where {Q <: Union{SawtoothQuadConfig, SolverSOS2QuadConfig, ManualSOS2QuadConfig}} | ||
| return tolerance_depth(EpigraphQuadConfig; | ||
| tolerance = tolerance, | ||
| max_delta = max_delta_x + max_delta_y, | ||
| ) | ||
| end | ||
| HybSConfig(quad_config::QuadraticApproxConfig, epigraph_depth::Int) = | ||
| HybSConfig(quad_config, epigraph_depth, false) | ||
|
|
||
| # --- Unified HybS dispatch methods --- | ||
|
|
||
|
|
@@ -145,7 +237,7 @@ function _add_bilinear_approx!( | |
| end | ||
|
|
||
| # --- Epigraph Q^{L1} lower bound for (x+y)² and (x−y)² (no binaries) --- | ||
| epi_cfg = EpigraphQuadConfig(config.epigraph_depth) | ||
| epi_cfg = EpigraphQuadConfig(; depth = config.epigraph_depth) | ||
| zp1_expr = _add_quadratic_approx!( | ||
| epi_cfg, | ||
| container, C, names, time_steps, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking out loud: can we do Bin3 + epigraph?