Skip to content

[WIP] Add tolerance constructor to approximation configs#105

Draft
acostarelli wants to merge 4 commits into
mainfrom
ac/tolerance-option
Draft

[WIP] Add tolerance constructor to approximation configs#105
acostarelli wants to merge 4 commits into
mainfrom
ac/tolerance-option

Conversation

@acostarelli
Copy link
Copy Markdown
Member

TODO:

  • There are still a few approximations that need to be filled in
  • Confirm the error bounds with the papers
  • The constructors are kinda ugly now

… configs

Users can now build a config from an error tolerance instead of inverting the
method's error formula by hand. Each config with a documented closed-form gap
gets a keyword-only outer constructor that takes `tolerance` and `max_delta`
(plus `max_delta_x`/`max_delta_y` for bilinear), resolves `depth`, and
delegates to the existing positional constructor. Bin2 and HybS get
informative error stubs since their composite gap formula isn't documented
in-source yet; the no-op configs fall through to Julia's natural MethodError
because an empty-struct kw stub would clobber the auto-generated zero-arg
constructor.

Replaces the four "Relaxation gap <= 2^(...)" tests in
test_nmdt_approximations.jl with a new test_tolerance_dispatch.jl that
requests several tolerances and asserts achieved gap <= tolerance for each
implemented method. Monotonic convergence and structural-correctness tests
are retained — only the one-to-one bound checks are replaced.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

Performance Results
Main


This branch


Comment thread src/quadratic_approximations/no_approx.jl Outdated
Comment thread src/quadratic_approximations/sawtooth.jl Outdated
Comment thread test/test_nmdt_approximations.jl Outdated
Comment thread src/bilinear_approximations/bin2.jl Outdated
Comment thread src/quadratic_approximations/solver_sos2.jl Outdated
Comment thread test/test_tolerance_dispatch.jl Outdated
Comment thread test/test_tolerance_dispatch.jl Outdated
Comment thread test/test_tolerance_dispatch.jl Outdated
Comment thread test/test_tolerance_dispatch.jl Outdated
- Move all quadratic and bilinear config constructors inside the struct
  body as inner constructors using new(...). Quadratic configs take either
  depth or (tolerance, max_delta) via a single merged kwargs constructor;
  Bin2Config/HybSConfig keep quad_config positional with kwargs for the
  remaining fields. Auto-generated all-positional constructors are now
  suppressed, so every call site moves to kwargs.
- Update every positional call site in src/ and test/ accordingly,
  including internal EpigraphQuadConfig calls in sawtooth.jl, hybs.jl,
  and nmdt_common.jl, plus the bilinear_delta_benchmark wrappers.
- Drop explanatory comments at the top of both no_approx.jl files and
  the stale "see test_tolerance_dispatch.jl" comment in
  test_nmdt_approximations.jl.
- In test_tolerance_dispatch.jl: rename greek-letter variables to ASCII
  (eps -> tol, delta), drop the +1e-6 solver-tolerance slack in favor of
  +1e-10 (pure float-roundoff guard at SOS2 exact-integer boundaries),
  remove the stub-configs-error-informatively testset, and generalize
  the non-unit-domain testset to iterate over every config.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Member Author

@acostarelli acostarelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review 2

Comment thread src/bilinear_approximations/nmdt.jl Outdated
Comment thread src/quadratic_approximations/nmdt.jl Outdated
Comment thread src/quadratic_approximations/solver_sos2.jl Outdated
Comment thread src/quadratic_approximations/sawtooth.jl Outdated
Comment thread src/quadratic_approximations/epigraph.jl Outdated
Comment thread src/bilinear_approximations/hybs.jl Outdated
Comment thread src/bilinear_approximations/bin2.jl Outdated
Approximation config constructors take only depth/segments now. A new
`tol_depth(::Type{Cfg}; tolerance, max_delta, …)::Int` family derives the
discrete knob from a target gap. Bilinear configs `Bin2Config{Q}` and
`HybSConfig{Q}` are parameterized on their inner quad type so helpers can
dispatch on the pair; HybS also exposes `tol_epigraph_depth` for its cross-
term knob. Both bilinear helpers restrict to the inner Qs for which the
formulation is structurally valid and raise MethodError otherwise.

Helper docstrings carry the math derivations (one-sided 2× factor for
Bin2 over Sawtooth/SOS2, half-and-half budget split for HybS), so the
inversion is auditable from the source.

Sawtooth's `epigraph_depth` docstring is corrected: when nonzero it gates
a free variable z in [epigraph(x), sawtooth(x)], so the worst-case error
becomes `max(Δ²·2^{-2L_s-2}, Δ²·2^{-2L_e-2})` and the epigraph side can
dominate when `L_e < L_s`. SOS2 `pwmcc_segments` is documented as an LP-
relaxation tightener that does not enter the PWL error bound.

Tests cover analytical worst-case points plus a dense grid and log
`max_gap / tolerance` per case; ratios stay ≤ 1 across the suite.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Member Author

@acostarelli acostarelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review 5/29 1

Comment thread src/bilinear_approximations/bin2.jl Outdated
Comment thread src/bilinear_approximations/bin2.jl Outdated
Comment thread src/bilinear_approximations/bin2.jl Outdated
Comment thread src/bilinear_approximations/bin2.jl Outdated
Comment thread src/bilinear_approximations/hybs.jl Outdated
Comment thread test/test_tolerance_dispatch.jl
Comment thread test/test_tolerance_dispatch.jl Outdated
Comment thread test/test_tolerance_dispatch.jl Outdated
Comment thread test/test_tolerance_dispatch.jl Outdated
Comment thread test/test_tolerance_dispatch.jl Outdated
- rename tol_depth → tolerance_depth, tol_epigraph_depth → tolerance_epigraph_depth
- share _ceil_positive(x::Float64) helper in quadratic common.jl
- generalize _normed_variable! to accept any AbstractJuMPScalar (AffExpr too)
- extend Bin2Config{Q} tolerance_depth to support NMDTQuadConfig / DNMDTQuadConfig
  inner Qs (require epigraph_depth = 0); rewrite bin2 math/notation
- rewrite hybs.jl docstring: explain why Q must over-estimate, derive
  lower/upper from the sandwich inequalities, drop the "budget" wording
- state the epigraph_depth ∈ {0} ∪ [depth, ∞) contract in Sawtooth/NMDT/DNMDT
  tolerance_depth docstrings; add tolerance_epigraph_depth helpers; clarify
  in struct docstrings that epigraph_depth is a structural MIP change,
  contrasted with the LP-only pwmcc_segments
- drop pwmcc_segments = 0 overrides from tolerance/quadratic tests (no effect
  on MIP optimum)
- split NMDT tightening test into named with/without-tightening cases
- test_tolerance_dispatch: multiple dispatch over the if/elseif sampler,
  include boundary corner points, assert ratio ≤ 1, Δ = 4 → Δ = 7 for
  SolverSOS2, rename shadowing loop vars, drop @test_throws MethodError
  testsets

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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 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
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So are they one sided or two sided, the nmdt quads?

# --- 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
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If $z_x$ is an over-error, then isn't $\epsilon_q$ forced to be 0?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace your math explanation with mine:

Since $z_x$ is an over-estimation of $x^2$, let $\epsilon_x=x^2-z_x$ represent the maximum error in the domain of $x$ . Then, compute the bounds of $z$, our approximation. The lower bound is Bin2:

$\frac{1}{2}(z_x+z_y-z_{p1})$
$\frac{1}{2}(z_p-z_x-z_y)$
$\frac{1}{2}((x+y)^2-\epsilon_{p1}-x^2-\epsilon_x-y^2-\epsilon_y)$
$\frac{1}{2}((x+y^2)-x^2-y^2)-\frac{1}{2}(\epsilon_{p1}+\epsilon_x+\epsilon_y)$
$xy-\frac{1}{2}(\epsilon_{p1}+\epsilon_x+\epsilon_y)$

We are using the same approximation for both inner quadratics, and the same for both $p$ terms, so let $\epsilon_x=\epsilon_y=\epsilon_Q$ and $\epsilon_{p1}=\epsilon_{p2}=\epsilon_E$. Further simplifying above we arrive at $xy-\frac{\epsilon_E}{2}-\epsilon_Q$. A similar computation with the Bin3 upper bound reveals $|z-xy|\le\frac{\epsilon_E}{2}+\epsilon_Q$. If our desired tolerance is $\tau$, we can achieve this with $\epsilon_E=\tau$ and $\epsilon_Q=\frac{\tau}{2}$, arbitrarily.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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.

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
Copy link
Copy Markdown
Member Author

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant