Commit b788ffc
fix: complete pinned_step body, restore driver-set search, with strict pcstg sufficiency check
This PR completes Alex Gates' 2018 pinning-controllability design,
fixes a long-standing bug in pinned_step, and restores two utility
functions for minimum-driver-set search with a strict pcstg
sufficiency criterion.
The pinned_step bug fix
-----------------------
The pcstg edge-loop scaffolding for limit-cycle attractors has been
in place since Alex Gates' 2018 introduction of pinning
controllability (98792ed): the iteration over attractor STG edges,
the (attsource, attsink) pair, and the call-site form
pinned_step(initial, pinned_binstate=attsink, pinned_var=...). What
was missing was the body of pinned_step that would honor the
pinned_binstate argument — pinned positions in the output were
copied from initial instead of being written to pinned_binstate.
For fixed-point attractors src and sink share the same pin pattern,
so the missing-body path happened to produce correct results. For
limit-cycle attractors where the pinned variable flips between cycle
states, src and sink differ; the buggy body kept pin=src in the
output, fragmenting the pcstg into one disjoint snapshot per cycle
position. The cycle's states were split across snapshots,
set(att) was never a subset of any single weakly-connected
component, and fraction_pinned_configurations returned 0 for every
such attractor.
A related independent fix was made in 2019 (44a96d3, on
python3-friendly) via a different mechanism — splitting pinned_step
into two functions and changing the call site. That fix shipped on
its branch but didn't make it to upstream when subsequent merges
branched off pre-fix history.
This commit completes Alex's original design: the body of
pinned_step now writes pinned_binstate into the destination's pinned
positions while still using the source pin pattern (from initial)
to compute the unpinned step. The call site stays exactly as it
was written in 2018.
Changes
-------
* pinned_step: body now reads pin from `initial` (as inputs to node
update functions) and writes pin from `pinned_binstate`. Signature
unchanged from Alex's original. Docstring rewritten to document
all three arguments correctly (the original docstring had a
phantom `n` arg from a copy-paste and never mentioned
`pinned_binstate`).
* pinning_controlled_state_transition_graph: rename loop variables
attsource/attsink → src_pin/dst_pin. Same variables, just renamed
— the old names suggested *attractor states* when they actually
hold the *pin-bit projections* of those states. The rename is
purely internal: these are local names inside one method's body,
not part of any signature, attribute, or external API. An inline
comment in the function explains the meaning and notes the rename.
Restored search and Stage-2 design
----------------------------------
pinning_control_driver_nodes — minimum-driver-set search via
exhaustive enumeration bracketed by ceil(log2(N_attractors)) lower
bound and FVS upper bound. Discrete/Boolean analog of FVS-based
open-loop control (Mochizuki & Fiedler 2013, JTB §7).
The search uses a two-stage filter:
* Stage 1 (necessary) — _signatures_distinguish_attractors. Cheap
pre-check that the candidate's per-attractor signatures are
distinct. Handles fixed-point, pin-constant cycle, and
pin-flipping cycle attractors separately.
* Stage 2 (strict sufficient) — attracting_components(pcstg) ==
[set(att)]. Each pcstg must have exactly one attracting SCC,
equal to the target attractor's state set. Pure integer/set
comparison — no floating-point exposure even at large pcstg
sizes.
The historically-tempting weaker criterion is WCC == 1 per pcstg.
WCC=1 is necessary (no orphan basins) but not sufficient: it only
verifies the pcstg is connected, not that the pcstg's attractor
equals the intended target. For deterministic pcstg (fixed-point
pinning) each WCC has exactly one attracting SCC, but that SCC may
be a different attractor than the target if the pin pattern's
projection collides with multiple original attractors and the
unpinned dynamics converge to one of the others.
Concrete example: Arabidopsis thaliana under pin = {AP3, UFO, AP1,
LFY, WUS} — fpc = [1, 0, 1, 1, 1, 0, 1, 1, 1, 0] across the ten
attractors, so three target attractors are not the pcstg's attractor
under that pinning. Yet all ten pcstg are single-WCC. The size-6
sets Alex Gates' 2018 slide-3 reports for Thaliana pass the strict
check; the WCC=1-only check would have admitted the size-5 set as
a false positive. Drosophila and Yeast cell-cycle happen to be
robust to the difference because their pin signatures distinguish
all attractors uniquely.
_signatures_distinguish_attractors — module-level helper used by
the Stage-1 filter above. Three attractor types handled inline.
A third orphaned function from a 2019 effort
(full_control_driver_nodes) is deferred to a separate PR. The right
name (configuration_perturbation_driver_nodes) needs lab consensus
on the broader 4-quadrant control taxonomy
(perturbation/pinning × attractor-level/configuration-level).
Tests
-----
20 new tests in tests/test_pinning_control.py:
- 18 tests on a 3-node BN fixture (A *= B; B *= A; C *= A and B)
with all three attractor types simultaneously (length-2 cycle,
two fixed points). Exercises:
* bug surface (pin A or {A,B} — pin flips in cycle): pcstg
single-WCC + mpcf=1.0; verifies the bug is fixed without
tripping over fixed-point cases that incidentally worked
pre-fix.
* pin-constant control case (pin C — pin invariant in cycle):
values unchanged from pre-fix; guards against regressing the
fixed-point-equivalent case.
* minimum driver-set search: returns {A,B} as the unique
2-element pinning control set (ceil(log2(3))=2 lower bound).
* pinned_step unit tests: pin-unchanged (pinned_binstate matches
initial[pinned_var]), pin-advanced (pinned_binstate differs),
multi-pin, length-mismatch error.
* _signatures_distinguish_attractors: four cases covering distinct
fixed points, colliding fixed points, pin-constant cycle, and
pin-flipping cycle.
- 2 real-network regression tests on Arabidopsis thaliana (Chaos
2006), the network that motivated the strict Stage-2 criterion:
* test_pinning_control_driver_nodes_thaliana_strict_criterion:
asserts the search returns exactly Alex's three size-6 sets.
* test_thaliana_size5_false_positive_rejected_by_strict_check:
direct assertion on the strict criterion against the
historical false-positive size-5 set; lighter-weight regression
that survives even if the search algorithm changes shape.
All 20 new tests pass; full CANA test suite (81 tests total) passes.
Sanity check on real CC model: Fanconi anemia (length-2 cycle with
CHKREC flipping), pinning CHKREC → pcf=1.0 (pcstg single WCC,
correct full-control on the cycle). Lac Operon (fixed-point only)
— unchanged.
References
----------
- Fiedler, Mochizuki, Kurosawa, Saito (2013). Dynamics and Control
at Feedback Vertex Sets I. J. Dyn. Diff. Equat. 25, 563-604.
- Mochizuki, Fiedler, Kurosawa, Saito (2013). Dynamics and Control
at Feedback Vertex Sets II. J. Theor. Biol. 335, 130-146.
- Zañudo, Yang, Albert (2017). Structure-based control of complex
networks with nonlinear dynamics. PNAS 114, 7234-7239.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent cb2a538 commit b788ffc
2 files changed
Lines changed: 636 additions & 21 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
| |||
51 | 52 | | |
52 | 53 | | |
53 | 54 | | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
54 | 113 | | |
55 | 114 | | |
56 | 115 | | |
| |||
1050 | 1109 | | |
1051 | 1110 | | |
1052 | 1111 | | |
| 1112 | + | |
| 1113 | + | |
| 1114 | + | |
| 1115 | + | |
| 1116 | + | |
| 1117 | + | |
| 1118 | + | |
| 1119 | + | |
| 1120 | + | |
| 1121 | + | |
| 1122 | + | |
| 1123 | + | |
| 1124 | + | |
| 1125 | + | |
| 1126 | + | |
| 1127 | + | |
| 1128 | + | |
| 1129 | + | |
| 1130 | + | |
| 1131 | + | |
| 1132 | + | |
| 1133 | + | |
| 1134 | + | |
| 1135 | + | |
| 1136 | + | |
| 1137 | + | |
| 1138 | + | |
| 1139 | + | |
| 1140 | + | |
| 1141 | + | |
| 1142 | + | |
| 1143 | + | |
| 1144 | + | |
| 1145 | + | |
| 1146 | + | |
| 1147 | + | |
| 1148 | + | |
| 1149 | + | |
| 1150 | + | |
| 1151 | + | |
| 1152 | + | |
| 1153 | + | |
| 1154 | + | |
| 1155 | + | |
| 1156 | + | |
| 1157 | + | |
| 1158 | + | |
| 1159 | + | |
| 1160 | + | |
| 1161 | + | |
| 1162 | + | |
| 1163 | + | |
| 1164 | + | |
| 1165 | + | |
| 1166 | + | |
| 1167 | + | |
| 1168 | + | |
| 1169 | + | |
| 1170 | + | |
| 1171 | + | |
| 1172 | + | |
| 1173 | + | |
| 1174 | + | |
| 1175 | + | |
| 1176 | + | |
| 1177 | + | |
| 1178 | + | |
| 1179 | + | |
| 1180 | + | |
| 1181 | + | |
| 1182 | + | |
| 1183 | + | |
| 1184 | + | |
| 1185 | + | |
| 1186 | + | |
| 1187 | + | |
| 1188 | + | |
| 1189 | + | |
| 1190 | + | |
| 1191 | + | |
| 1192 | + | |
| 1193 | + | |
| 1194 | + | |
| 1195 | + | |
| 1196 | + | |
| 1197 | + | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
1053 | 1205 | | |
1054 | 1206 | | |
1055 | 1207 | | |
| |||
1124 | 1276 | | |
1125 | 1277 | | |
1126 | 1278 | | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
1127 | 1289 | | |
1128 | 1290 | | |
1129 | 1291 | | |
| |||
1136 | 1298 | | |
1137 | 1299 | | |
1138 | 1300 | | |
1139 | | - | |
| 1301 | + | |
1140 | 1302 | | |
1141 | 1303 | | |
1142 | 1304 | | |
1143 | 1305 | | |
1144 | | - | |
| 1306 | + | |
1145 | 1307 | | |
1146 | 1308 | | |
1147 | 1309 | | |
| |||
1155 | 1317 | | |
1156 | 1318 | | |
1157 | 1319 | | |
1158 | | - | |
| 1320 | + | |
1159 | 1321 | | |
1160 | 1322 | | |
1161 | 1323 | | |
1162 | | - | |
| 1324 | + | |
1163 | 1325 | | |
1164 | 1326 | | |
| 1327 | + | |
| 1328 | + | |
| 1329 | + | |
| 1330 | + | |
| 1331 | + | |
| 1332 | + | |
| 1333 | + | |
| 1334 | + | |
1165 | 1335 | | |
1166 | 1336 | | |
1167 | 1337 | | |
1168 | 1338 | | |
1169 | 1339 | | |
1170 | | - | |
| 1340 | + | |
1171 | 1341 | | |
1172 | 1342 | | |
1173 | 1343 | | |
| |||
1178 | 1348 | | |
1179 | 1349 | | |
1180 | 1350 | | |
1181 | | - | |
1182 | | - | |
| 1351 | + | |
| 1352 | + | |
| 1353 | + | |
| 1354 | + | |
| 1355 | + | |
| 1356 | + | |
| 1357 | + | |
| 1358 | + | |
| 1359 | + | |
1183 | 1360 | | |
1184 | 1361 | | |
1185 | | - | |
1186 | | - | |
| 1362 | + | |
| 1363 | + | |
| 1364 | + | |
| 1365 | + | |
| 1366 | + | |
1187 | 1367 | | |
1188 | 1368 | | |
1189 | | - | |
| 1369 | + | |
| 1370 | + | |
| 1371 | + | |
| 1372 | + | |
| 1373 | + | |
| 1374 | + | |
| 1375 | + | |
1190 | 1376 | | |
1191 | | - | |
1192 | | - | |
1193 | | - | |
1194 | | - | |
1195 | | - | |
1196 | 1377 | | |
| 1378 | + | |
| 1379 | + | |
| 1380 | + | |
| 1381 | + | |
1197 | 1382 | | |
1198 | | - | |
1199 | | - | |
1200 | | - | |
1201 | | - | |
1202 | | - | |
1203 | | - | |
| 1383 | + | |
| 1384 | + | |
| 1385 | + | |
| 1386 | + | |
1204 | 1387 | | |
1205 | 1388 | | |
1206 | 1389 | | |
| |||
0 commit comments