Skip to content

Minimum-lengthscale constraints in Python - #12

Draft
smartalecH wants to merge 1 commit into
docs/supported-featuresfrom
python/lengthscale-constraints
Draft

Minimum-lengthscale constraints in Python#12
smartalecH wants to merge 1 commit into
docs/supported-featuresfrom
python/lengthscale-constraints

Conversation

@smartalecH

Copy link
Copy Markdown
Collaborator

Stacked on #11 (base branch is docs/supported-features) — review that one first.

Ports the geometric minimum-lengthscale constraints from the Julia package to ssp_topopt, closing one of the gaps recorded in docs/features.md.

What's here

ssp_topopt.constraints adds constraint_solid and constraint_void, following the Julia implementation in src/julia/SSP/src/constrain.jl:

  • the geometric constraints of Zhou et al. (2015), with the hyperparameter-free thresholds derived in Arrieta, Romano & Johnson, arXiv:2507.16108 — decay rate c = 64 R^2, threshold eps = 1e-8, and the conic-filter threshold points eta_e / eta_d of Qian & Sigmund (2013) (eqs. 7-12 of that paper);
  • the gradient of the filtered density is obtained from the same differentiable cubic interpolation (utils.gradient) that ssp2 uses;
  • the return value is normalized by the constraint threshold, matching the Julia API, so it can go straight into an nlopt inequality constraint: nonpositive means feasible.

Any SSP order works. The constraints only take rho_filtered and rho_projected, so they compose with ssp1_bilinear, ssp2, or a plain tanh_projection; a test exercises all three end to end.

Example

examples/python/ssp_constrained_example.py has two parts:

  1. A sweep over a stripe geometry, which shows the solid constraint crossing the threshold right at the target lengthscale (qualitatively fig. 1c of the paper).
  2. A two-stage optimization against a target pattern with bars thinner than the target lengthscale. Stage 1 is unconstrained and reproduces the sub-lengthscale bars (constraints at ~1e6 times the threshold); stage 2 turns the constraints on and repairs the design (both constraints negative, i.e. feasible) at a cost of FOM 1.2e-4 -> 8.7e-2.

Testing

tests/python/test_constraints.py (14 tests, 20 subtests; the full suite is 21 passing):

  • threshold functions against the closed-form values, plus the eta_e + eta_d = 1 invariant;
  • thin stripes/gaps violate the constraint and thick ones satisfy it, with the physical width measured from the projected design rather than assumed;
  • reverse-mode gradients w.r.t. both rho_filtered and rho_projected match central finite differences to ~1e-7 (in float64 — the normalized constraint is O(1/eps), so single-precision differencing is pure roundoff);
  • composition with each projection, jit parity, and argument validation.

As an extra check outside the test suite, I compared against the Julia implementation on identical arrays: solid 1.4147e6 (Python) vs 1.4174e6 (Julia) and void 1.0382e6 vs 1.0388e6 — 0.2% and 0.06%, consistent with the two packages using different cubic interpolation schemes (interpax cubic2 vs FastInterpolations CubicFit) for the density gradient.

Not included

Dilation/erosion and cubic-interpolation SSP1 remain Julia-only; docs/features.md is updated to reflect only what this PR adds.

Ports the geometric lengthscale constraints from the Julia package:
constraint_solid and constraint_void, with the hyperparameter-free
thresholds of Arrieta et al. (arXiv:2507.16108). Both are written in
terms of rho_filtered and rho_projected only, so they compose with any
projection order (ssp1_bilinear, ssp2, or a plain tanh projection).

Includes a test suite covering the threshold functions, lengthscale
detection on stripe/gap geometries, gradients against finite differences,
composition with each projection, jit, and argument validation, plus a
worked example that sweeps the constraint over feature widths and runs a
two-stage constrained optimization.
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