Compare commits
2 commits
81ce074a0b
...
f7859931f3
| Author | SHA1 | Date | |
|---|---|---|---|
| f7859931f3 | |||
| e37930605e |
4 changed files with 175 additions and 86 deletions
File diff suppressed because one or more lines are too long
107
DESIGN.md
107
DESIGN.md
|
|
@ -3459,7 +3459,7 @@ on every real (non-duplicated-code) programme tested at any scale from 6 rooms (
|
||||||
suggests was measuring the wrong thing. No further follow-up is filed — the room-count hypothesis from
|
suggests was measuring the wrong thing. No further follow-up is filed — the room-count hypothesis from
|
||||||
`f1d` (§23) is now addressed on the diversity axis `xyu` (§31) could not reach.
|
`f1d` (§23) is now addressed on the diversity axis `xyu` (§31) could not reach.
|
||||||
|
|
||||||
## 33. Multi-use leaves as a permanent design goal (`homemaker-py-1s3`, §26 path b) — DONE (mixed, net negative)
|
## 33. Multi-use leaves as a permanent design goal (`homemaker-py-1s3`, §26 path b) — DONE (promising, not a clean win)
|
||||||
|
|
||||||
**Motivation.** §26 scoped two readings of "multi-use leaves" — a leaf legitimately serving several
|
**Motivation.** §26 scoped two readings of "multi-use leaves" — a leaf legitimately serving several
|
||||||
DIFFERENT compatible programme codes at once (study+guest bedroom, kitchen+dining, Stewart Brand's
|
DIFFERENT compatible programme codes at once (study+guest bedroom, kitchen+dining, Stewart Brand's
|
||||||
|
|
@ -3485,14 +3485,32 @@ comparing `leaf.type` directly — returning `[type, co_type]` only while `multi
|
||||||
still a currently-valid declared co-location (a retype silently drops a stale `co_type`, the same
|
still a currently-valid declared co-location (a retype silently drops a stale `co_type`, the same
|
||||||
self-healing type-guard `leaf_share` uses). `fitness.quality_size` combines a fused leaf's two codes
|
self-healing type-guard `leaf_share` uses). `fitness.quality_size` combines a fused leaf's two codes
|
||||||
**additively** (target and sigma both sum — the same operation as leaf-sharing's k×target, generalised
|
**additively** (target and sigma both sum — the same operation as leaf-sharing's k×target, generalised
|
||||||
from k identical terms to 2 different ones); `quality_width`/`quality_proportion` take the **stricter** of
|
from k identical terms to 2 different ones — area genuinely sums across two uses). Construction-time only
|
||||||
the two codes' targets (max target, min sigma) since shape, unlike floor area, does not add across two
|
(no mutation operator): `operators._colocate_rooms` greedily fuses available same-storey instances of a
|
||||||
uses. Construction-time only (no mutation operator): `operators._colocate_rooms` greedily fuses available
|
declared pair (before `_share_rooms`, so same-code sharing still groups whichever code is kept primary),
|
||||||
same-storey instances of a declared pair (before `_share_rooms`, so same-code sharing still groups
|
`_leaf_colocate_from_plan` stamps the winning leaves, and `_size_divisions_from_targets` grows the fused
|
||||||
whichever code is kept primary), `_leaf_colocate_from_plan` stamps the winning leaves, and
|
leaf to the combined target. Gated behind `multi_use` (default OFF, bit-identical when off — 335/335 tests
|
||||||
`_size_divisions_from_targets` grows the fused leaf to the combined target. Gated behind `multi_use`
|
pass including 33 in `tests/test_multi_use.py`). Threaded end-to-end through `driver.py`/`evolve.py
|
||||||
(default OFF, bit-identical when off — 333/333 tests pass including 31 new ones in `tests/test_multi_use.py`).
|
--multi-use`, mirroring `superpose`'s existing wiring.
|
||||||
Threaded end-to-end through `driver.py`/`evolve.py --multi-use`, mirroring `superpose`'s existing wiring.
|
|
||||||
|
**Shape-combination sub-experiment — `quality_width`/`quality_proportion`.** Unlike area, a leaf's width and
|
||||||
|
aspect are the SAME physical measurement serving two potentially-different codes' targets at once, so
|
||||||
|
"additive" makes no sense — three combination strategies were tried, in this order, each triggered by
|
||||||
|
review of the previous:
|
||||||
|
|
||||||
|
1. **Naive max-target/min-sigma ("stricter of both").** The first cut: pick whichever code's target is
|
||||||
|
harder to satisfy. Simple, but ad hoc — it does not correspond to any principled combination of the two
|
||||||
|
codes' evidence.
|
||||||
|
2. **Precision-weighted product (`fitness._gaussian_product`).** The product of two Gaussian curves
|
||||||
|
evaluated at the same point is itself proportional to a Gaussian: precisions (`1/sigma^2`) ADD, and the
|
||||||
|
combined target is the precision-weighted average — an INTERMEDIATE target (never simply the stricter
|
||||||
|
one) with a NARROWER spread than either input. The standard way to combine two pieces of independent
|
||||||
|
evidence about the same quantity.
|
||||||
|
3. **Mixture (`fitness._clipped_gaussian` + `max()`).** A different philosophy: the leaf need not compromise
|
||||||
|
between the two codes' targets at all — score it against whichever target the realised geometry ends up
|
||||||
|
closer to (a wide, bimodal tolerance), echoing this project's own per-leaf usage collapse (§26 path a)
|
||||||
|
but applied within one leaf's shape terms instead of across its whole type. Appealing in principle (no
|
||||||
|
forced compromise) but, per the A/B below, empirically the worst of the three.
|
||||||
|
|
||||||
**Declared pairs.** Architect-authored in each programme's `patterns.config`, hand-picked from the pool of
|
**Declared pairs.** Architect-authored in each programme's `patterns.config`, hand-picked from the pool of
|
||||||
`interchangeable()`-eligible candidates on semantic grounds (not every eligible pair is a sensible fusion —
|
`interchangeable()`-eligible candidates on semantic grounds (not every eligible pair is a sensible fusion —
|
||||||
|
|
@ -3501,44 +3519,41 @@ left undeclared): `harbor-house` — foyer/meeting-room (`ef1`/`m`), laundry/pla
|
||||||
`health-centre` — admin/manager's office (`ao1`/`mo1`), admin/staff-room (`ao1`/`br1`), dental/minor-surgery
|
`health-centre` — admin/manager's office (`ao1`/`mo1`), admin/staff-room (`ao1`/`br1`), dental/minor-surgery
|
||||||
(`de1`/`ms1`), storage/records (`dp1`/`re1`).
|
(`de1`/`ms1`), storage/records (`dp1`/`re1`).
|
||||||
|
|
||||||
**End-to-end A/B** (`experiments/run_multiuse_ab.sh`, staged search, 20 000 native evals, seeds 0/1/2,
|
**End-to-end A/B, all three shape-combination strategies** (`experiments/run_multiuse_ab.sh`, staged search,
|
||||||
4 workers, final native re-score, mirrors §13.3's harness):
|
20 000 native evals, seeds 0/1/2, 4 workers, final native re-score, mirrors §13.3's harness; each run
|
||||||
|
verified single-process before launch — an early attempt let two runs overlap and contaminate the results,
|
||||||
|
discarded entirely, see the bead's history):
|
||||||
|
|
||||||
| programme | baseline (s0/1/2) | mean | multi_use (s0/1/2) | mean | Δ |
|
| combination | harbor-house (s0/1/2) | mean | Δ | health-centre (s0/1/2) | mean | Δ |
|
||||||
|---------------|--------------------|------:|---------------------|------:|----------:|
|
|---|---|---:|---:|---|---:|---:|
|
||||||
| harbor-house | 95 / 101 / 103 | 99.7 | 92 / 101 / 94 | 95.7 | **−4.0%** |
|
| baseline (no multi_use) | 95/101/103 | 99.7 | — | 63/82/71 | 72.0 | — |
|
||||||
| health-centre | 63 / 82 / 71 | 72.0 | 81 / 111 / 77 | 89.7 | **+24.5%**|
|
| 1. stricter-of-both | 92/101/94 | 95.7 | **−4.0%** | 81/111/77 | 89.7 | **+24.5%** |
|
||||||
|
| baseline (re-measured) | 95/101/90 | 95.3 | — | 63/82/71 | 72.0 | — |
|
||||||
|
| 2. precision-weighted | 82/117/83 | 94.0 | **−1.4%** | 65/78/43 | 62.0 | **−13.9%** |
|
||||||
|
| baseline (re-measured) | 95/102/97 | 98.0 | — | 63/82/71 | 72.0 | — |
|
||||||
|
| 3. mixture | 81/110/81 | 90.7 | **−7.5%** | 91/92/77 | 86.7 | **+20.4%** |
|
||||||
|
|
||||||
harbor-house: multi_use wins 2/3 seeds, ties 1, loses 0. health-centre: multi_use **loses 3/3 seeds**, every
|
(Baseline drifts slightly run-to-run — the staged search's own within-seed run-to-run noise at this
|
||||||
seed strictly worse. Net across both programmes: 2 wins / 1 tie / 3 losses, and the health-centre loss
|
budget/worker-count, not a bug; each combination's Δ is against its own paired baseline row.)
|
||||||
(+24.5%) is nearly 6x the magnitude of harbor-house's gain (−4.0%) — a net-negative result, not a wash.
|
|
||||||
|
|
||||||
**Diagnosis — why this lever behaves differently from leaf-sharing despite the structural similarity.**
|
**Diagnosis.** Leaf-sharing's k×target scaling never changes the SHAPE constraint: k identical rooms share
|
||||||
Leaf-sharing's k×target scaling never changes the SHAPE constraint: k identical rooms share one identical
|
one identical width/proportion target, so a shared leaf is exactly as easy or hard to satisfy geometrically
|
||||||
width/proportion target, so a shared leaf is exactly as easy or hard to satisfy geometrically as any single
|
as any single instance of that code, just bigger. Multi-use fusion is different — combining two
|
||||||
instance of that code, just bigger — the only thing that changes is the count check and the size Gaussian's
|
potentially-DIFFERENT codes' shape targets is a real modelling choice, and it matters a lot: the naive
|
||||||
centre. Multi-use fusion is different: combining two potentially-DIFFERENT codes' width/proportion targets
|
stricter-of-both hack over-penalises (health-centre +24.5%), the mixture under-constrains (`max()` lets a
|
||||||
via the stricter-of-both rule (necessary — a fused room must serve both uses) can impose a **tighter joint
|
leaf score 1.0 by satisfying only the WEAKER of the two codes' targets, health-centre +20.4%), and only the
|
||||||
shape constraint than either original code required alone**, on top of a **larger combined area target**
|
precision-weighted single-compromise-peak model improved BOTH programmes. `harbor-house` (fewer, larger
|
||||||
that competes for the same limited plot area as every other room. On `harbor-house` (fewer, larger rooms,
|
rooms, more slack per leaf) tolerates all three combinations reasonably; `health-centre` (19 distinct codes
|
||||||
more slack per leaf) this cost is absorbed and the leaf-count saving wins narrowly. On `health-centre` (19
|
packed into a small footprint, the exact stress case §32 was built to probe) is where the combination choice
|
||||||
distinct codes packed into a small footprint, the exact stress case §32 was built to probe) the tighter
|
swings the result by ~38 points of relative fail count (+24.5% to −13.9%) — the shape-combination model is
|
||||||
combined constraint and the larger area demand appear to cost more than the leaf-count reduction saves —
|
not a minor implementation detail here, it is close to the whole story.
|
||||||
consistent with §13.1/§13.2's finding that the geometry floor, not room count in isolation, is what
|
|
||||||
dominates on tightly-packed diverse programmes. This was not instrumented further (the effect is large and
|
|
||||||
directionally consistent across all 3 health-centre seeds, not a borderline case needing a relaxation-gap
|
|
||||||
probe the way §26's `xi7` needed one).
|
|
||||||
|
|
||||||
**Status.** `multi_use` stays default **OFF**. Not recommended for a default flip: the result is mixed at
|
**Status.** Landed with the precision-weighted combination (best of the three tried); `_clipped_gaussian` and
|
||||||
best and net negative when weighted across both example programmes, the opposite of leaf-sharing's total
|
the mixture pattern are kept in `fitness.py`, documented and unit-tested, as a recorded negative alternative.
|
||||||
separation (§13.3: "*every* share run beats *every* baseline run"). This is the qualitative distinction the
|
`multi_use` stays default **OFF**: the precision-weighted result is genuinely promising — both programmes
|
||||||
project's own vocabulary already has a name for: leaf-sharing is a pure count-relaxation (§13.3's "floor
|
improve on average (−1.4% / −13.9%) — but it is not the clean sweep §13.3's default-flip bar set (leaf-sharing:
|
||||||
mover" that "the search cannot erode", §13.4/13.5), while different-code fusion is *also* a shape-tightening
|
*every* share run beat *every* baseline run). Here harbor-house loses on 1 of 3 seeds (101→117) and only 3
|
||||||
constraint that construction cannot always pay for — closer in spirit to the search-machinery/fitness-shaping
|
seeds per arm were run, so this is a real but statistically thin signal, closer to §31's "weak but not
|
||||||
levers that have gone null-to-negative throughout this log (§11.4/11.5, §14, §16, §21, §22, §26, §27, §30,
|
evaporated" than to §13.3's "total separation". Not filed as an immediate follow-up bead given the compute
|
||||||
§31) than to the small set of construction/seed-quality wins that actually moved the floor (§13.3, §17/§25).
|
cost of another A/B (~2h per combination tried), but a promising candidate for a larger-N confirmation
|
||||||
If revisited: a per-pair opt-in (declare `co_locate` only where the architect has verified the combined
|
(mirroring `xyu`/`9yx`'s N=15 pattern) if revisited — the mechanism itself is complete, tested, and ready.
|
||||||
target is a good match, rather than accepting anything `interchangeable()`-eligible) or a softer shape
|
|
||||||
combination (e.g. averaging width/proportion targets instead of stricter-of-both) might recover the
|
|
||||||
harbor-house-style win without health-centre's penalty — but this was not tested and is not filed as a
|
|
||||||
follow-up bead given the project's 0-for-several record on this class of lever.
|
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,48 @@ def gaussian(x: float, a: float, b: float, c: float) -> float:
|
||||||
return a * (_E ** (0 - ((x - b) ** 2 / (2 * c * c))))
|
return a * (_E ** (0 - ((x - b) ** 2 / (2 * c * c))))
|
||||||
|
|
||||||
|
|
||||||
|
def _gaussian_product(target_a: float, sigma_a: float,
|
||||||
|
target_b: float, sigma_b: float) -> tuple[float, float]:
|
||||||
|
"""Precision-weighted combination of two Gaussian (target, sigma) pairs
|
||||||
|
(homemaker-py-1s3): the product of two Gaussian curves evaluated at the
|
||||||
|
same point is itself proportional to a Gaussian with precisions (1/sigma^2)
|
||||||
|
ADDING and target the precision-weighted average — always an INTERMEDIATE
|
||||||
|
target (never simply the stricter of the two) with a NARROWER spread than
|
||||||
|
either input, unlike a naive max-target/min-sigma combination.
|
||||||
|
|
||||||
|
Used by ``quality_width``/``quality_proportion`` to combine a co-located
|
||||||
|
leaf's two codes' shape targets. A/B-measured (DESIGN.md §33) as the best
|
||||||
|
of three tried: beats both the naive max-target/min-sigma hack (health-
|
||||||
|
centre +24.5% worse) and the max-of-two MIXTURE combination below
|
||||||
|
(health-centre +20.4% worse) — this precision-weighted single compromise
|
||||||
|
peak was the only one to improve both example programmes."""
|
||||||
|
prec_a, prec_b = 1.0 / (sigma_a * sigma_a), 1.0 / (sigma_b * sigma_b)
|
||||||
|
prec_c = prec_a + prec_b
|
||||||
|
target_c = (target_a * prec_a + target_b * prec_b) / prec_c
|
||||||
|
return target_c, (1.0 / prec_c) ** 0.5
|
||||||
|
|
||||||
|
|
||||||
|
def _clipped_gaussian(x: float, target: float, sigma: float, good_side: str) -> float:
|
||||||
|
"""The 'flat 1.0 once the target is met, gaussian decay short of it' shape
|
||||||
|
both ``quality_width`` (wider than target is good) and
|
||||||
|
``quality_proportion`` (squarer/lower aspect than target is good) use.
|
||||||
|
|
||||||
|
Also the building block of a MIXTURE alternative to ``_gaussian_product``
|
||||||
|
that was tried and measured worse (homemaker-py-1s3, DESIGN.md §33):
|
||||||
|
evaluate this once per served code and combine with ``max()`` instead of
|
||||||
|
computing one combined (target, sigma) — the leaf scores well if the
|
||||||
|
realised geometry ends up close to EITHER code's target rather than one
|
||||||
|
narrow compromise peak, echoing the per-leaf usage collapse §26 path (a)
|
||||||
|
uses at the whole-leaf-type level. Appealing in principle (no forced
|
||||||
|
compromise) but empirically worse on the tightly-packed health-centre
|
||||||
|
programme (+20.4%, vs -13.9% for the precision-weighted product currently
|
||||||
|
used) — plausibly because ``max()`` lets a leaf score 1.0 by satisfying
|
||||||
|
only the WEAKER of two codes' targets, under-constraining the search."""
|
||||||
|
if (good_side == "above" and x > target) or (good_side == "below" and x < target):
|
||||||
|
return 1.0
|
||||||
|
return gaussian(x, 1.0, target, sigma)
|
||||||
|
|
||||||
|
|
||||||
def load_config(directory: str | Path,
|
def load_config(directory: str | Path,
|
||||||
overrides: dict | None = None) -> tuple[dict, dict]:
|
overrides: dict | None = None) -> tuple[dict, dict]:
|
||||||
"""Load (patterns, costs) config for a corpus directory, mirroring
|
"""Load (patterns, costs) config for a corpus directory, mirroring
|
||||||
|
|
@ -809,15 +851,18 @@ class Fitness:
|
||||||
params = self.get_space_params(leaf.type, "proportion")
|
params = self.get_space_params(leaf.type, "proportion")
|
||||||
co_type = self._leaf_co_type(leaf)
|
co_type = self._leaf_co_type(leaf)
|
||||||
if co_type:
|
if co_type:
|
||||||
# 1s3: a fused leaf must satisfy the STRICTER of its two
|
# 1s3: A/B-measured (DESIGN.md §33) — the precision-weighted
|
||||||
# codes' proportion targets (max target, min tolerance) —
|
# combination (one intermediate, narrower target) beat both
|
||||||
# unlike quality_size, shape doesn't add across two uses.
|
# the naive max/min hack AND the max-of-two mixture on the
|
||||||
|
# health-centre programme; the mixture's permissiveness (any
|
||||||
|
# width/aspect satisfying the WEAKER of the two codes scores
|
||||||
|
# 1.0) under-constrains the search on tightly-packed
|
||||||
|
# programmes even though it is the more appealing model.
|
||||||
co_params = self.get_space_params(co_type, "proportion")
|
co_params = self.get_space_params(co_type, "proportion")
|
||||||
params = [max(params[0], co_params[0]), min(params[1], co_params[1])]
|
params = _gaussian_product(params[0], params[1],
|
||||||
|
co_params[0], co_params[1])
|
||||||
aspect = geometry.aspect(leaf)
|
aspect = geometry.aspect(leaf)
|
||||||
if aspect < params[0]:
|
return _clipped_gaussian(aspect, params[0], params[1], "below")
|
||||||
return 1.0
|
|
||||||
return gaussian(aspect, 1.0, params[0], params[1])
|
|
||||||
|
|
||||||
def quality_size(self, leaf: Node) -> float:
|
def quality_size(self, leaf: Node) -> float:
|
||||||
t0 = _t0(leaf)
|
t0 = _t0(leaf)
|
||||||
|
|
@ -869,14 +914,13 @@ class Fitness:
|
||||||
params = self.get_space_params(leaf.type, "width")
|
params = self.get_space_params(leaf.type, "width")
|
||||||
co_type = self._leaf_co_type(leaf)
|
co_type = self._leaf_co_type(leaf)
|
||||||
if co_type:
|
if co_type:
|
||||||
# 1s3: stricter of the two codes' width targets, same reasoning
|
# 1s3: precision-weighted, same reasoning as quality_proportion
|
||||||
# as quality_proportion above.
|
# above.
|
||||||
co_params = self.get_space_params(co_type, "width")
|
co_params = self.get_space_params(co_type, "width")
|
||||||
params = [max(params[0], co_params[0]), min(params[1], co_params[1])]
|
params = _gaussian_product(params[0], params[1],
|
||||||
|
co_params[0], co_params[1])
|
||||||
width = geometry.length_narrowest(leaf)
|
width = geometry.length_narrowest(leaf)
|
||||||
if width > params[0]:
|
return _clipped_gaussian(width, params[0], params[1], "above")
|
||||||
return 1.0
|
|
||||||
return gaussian(width, 1.0, params[0], params[1])
|
|
||||||
|
|
||||||
# --- simple crinkliness (URB_NO_OCCLUSION: illumination factor = 1) --- #
|
# --- simple crinkliness (URB_NO_OCCLUSION: illumination factor = 1) --- #
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -265,17 +265,47 @@ def test_quality_size_combines_both_codes_area_additively():
|
||||||
assert fit.quality_size(leaf) == pytest.approx(gaussian(16.0, 1.0, 17.0, 3.0))
|
assert fit.quality_size(leaf) == pytest.approx(gaussian(16.0, 1.0, 17.0, 3.0))
|
||||||
|
|
||||||
|
|
||||||
def test_quality_width_and_proportion_take_stricter_of_both_targets():
|
def test_gaussian_product_is_intermediate_and_narrower():
|
||||||
|
from homemaker_layout.fitness import _gaussian_product
|
||||||
|
|
||||||
|
# equal sigmas -> target is the plain average, sigma shrinks by sqrt(2)
|
||||||
|
t, s = _gaussian_product(3.0, 0.5, 4.0, 0.5)
|
||||||
|
assert t == pytest.approx(3.5)
|
||||||
|
assert s == pytest.approx(0.5 / (2 ** 0.5))
|
||||||
|
assert s < 0.5
|
||||||
|
|
||||||
|
# unequal sigmas -> target is pulled toward the more confident (smaller
|
||||||
|
# sigma) side, and stays strictly between the two targets (never the max)
|
||||||
|
t2, s2 = _gaussian_product(3.0, 0.5, 3.8, 0.2)
|
||||||
|
assert 3.0 < t2 < 3.8
|
||||||
|
assert t2 > 3.4 # pulled toward the tighter-sigma target (3.8)
|
||||||
|
assert s2 < min(0.5, 0.2)
|
||||||
|
|
||||||
|
|
||||||
|
def test_clipped_gaussian_flat_past_target_and_decays_short_of_it():
|
||||||
|
from homemaker_layout.fitness import _clipped_gaussian
|
||||||
|
|
||||||
|
assert _clipped_gaussian(5.0, 3.0, 0.5, "above") == 1.0
|
||||||
|
assert _clipped_gaussian(3.0, 3.0, 0.5, "above") == pytest.approx(
|
||||||
|
gaussian(3.0, 1.0, 3.0, 0.5))
|
||||||
|
assert _clipped_gaussian(1.0, 3.0, 0.5, "below") == 1.0
|
||||||
|
assert _clipped_gaussian(5.0, 3.0, 0.5, "below") == pytest.approx(
|
||||||
|
gaussian(5.0, 1.0, 3.0, 0.5))
|
||||||
|
|
||||||
|
|
||||||
|
def test_quality_width_and_proportion_use_precision_weighted_combination():
|
||||||
|
from homemaker_layout.fitness import _gaussian_product
|
||||||
|
|
||||||
fit = Fitness(conf=_multi_use_conf())
|
fit = Fitness(conf=_multi_use_conf())
|
||||||
# elongated rectangle so neither the width nor proportion "already fine"
|
# elongated rectangle so neither the width nor proportion "already fine"
|
||||||
# early-return short-circuits before the gaussian combination runs
|
# early-return short-circuits before the combination runs
|
||||||
leaf = _rect_leaf("x", width=2.0, length=10.0, co_type="y")
|
leaf = _rect_leaf("x", width=2.0, length=10.0, co_type="y")
|
||||||
# width target max(3.0,3.8)=3.8, sigma min(0.5,0.2)=0.2
|
wt, ws = _gaussian_product(3.0, 0.5, 3.8, 0.2)
|
||||||
assert fit.quality_width(leaf) == pytest.approx(
|
assert fit.quality_width(leaf) == pytest.approx(
|
||||||
gaussian(geometry.length_narrowest(leaf), 1.0, 3.8, 0.2))
|
gaussian(geometry.length_narrowest(leaf), 1.0, wt, ws))
|
||||||
# proportion target max(1.2,1.5)=1.5, sigma min(0.5,0.1)=0.1
|
pt, ps = _gaussian_product(1.2, 0.5, 1.5, 0.1)
|
||||||
assert fit.quality_proportion(leaf) == pytest.approx(
|
assert fit.quality_proportion(leaf) == pytest.approx(
|
||||||
gaussian(geometry.aspect(leaf), 1.0, 1.5, 0.1))
|
gaussian(geometry.aspect(leaf), 1.0, pt, ps))
|
||||||
|
|
||||||
|
|
||||||
def test_quality_size_ignores_co_type_when_pair_not_declared():
|
def test_quality_size_ignores_co_type_when_pair_not_declared():
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue