Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Base/modernise - #7

Merged
remislp merged 28 commits into
DCPROGS:masterfrom
remislp:base/modernise
Aug 6, 2026
Merged

Base/modernise#7
remislp merged 28 commits into
DCPROGS:masterfrom
remislp:base/modernise

Conversation

@remislp

@remislp remislp commented Aug 6, 2026

Copy link
Copy Markdown
Member

No description provided.

remislp and others added 28 commits June 6, 2026 21:49
- Add pyproject.toml (setuptools backend, static version string)
- Remove legacy setup.py (distutils, generated version.py)
- Update .gitignore: replace old Mercurial-syntax file with modern
  Python/pytest/editor patterns
- Add scalcs/samples/__init__.py so setuptools discovers the subpackage
- Simplify scalcs/version.py: static __version__ = 1.0.0; drop
  generated-file boilerplate

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
…uite

mechanism.py
- Add __future__.annotations, collections.abc.Callable, __all__
- Annotate State, Cycle, Rate, Graph, Mechanism __init__ signatures
- Annotate helper functions identity(), multiply(), constrain_rate_multiple()
- Fix mutable default arg bugs:
    Cycle(mrconstr=[])      -> mrconstr=None, set to [] in body
    Rate(limits=[])         -> limits=None, set to [] in body
    Mechanism(Cycles=[])    -> Cycles=None, set to [] in body
- Convert class X(object) -> class X for all five classes
- Add docstrings to State, Cycle, Rate, Mechanism classes

samples/samples.py
- Fix pre-existing bug: mechanism.mechanism.constrain_rate_multiple
  -> mechanism.constrain_rate_multiple (double-module reference)

tests/test_mechanism.py (new, 46 tests)
- TestHelperFunctions: identity, multiply, constrain_rate_multiple
- TestState: valid/invalid statetype, attributes, no=None before Mechanism
- TestCycle: storage, mrconstr default, mutable-default independence
- TestRate: scalar/list rateconstants, unit_rate, calc, TypeError, limits,
  fixed flag, name property, mutable-default independence
- TestMechanismConstruction: CH82 state counts (kA=2,kB=2,kC=1,k=5),
  derived counts, Q shape, row sums=0, diagonal<=0, off-diagonal>=0,
  state sort order, zero-based indices, Cycles mutable-default independence
- TestSubmatrices: QAA/QFF shapes, consistency with full Q
- TestSetEff: Q changes with concentration, row sums still zero, unknown eff
- TestTheta: free-param count, roundtrip, rateconstants update
- TestMicroscopicReversibility: forward/backward products equal, MR rate
  excluded from theta
- TestConstraints: constrained rates excluded from theta, row sums after
  update_constrains
- TestLimits: check_limits True, impose_limits clips out-of-range

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Tests cover:
  eigs / eigs_sorted
  - eigenvalue values for 2-state CO: {0, -70}
  - spectral decomposition: sum(A_i) = I; Q = sum(lambda_i * A_i)
  - eigs_sorted returns ascending real parts
  - same eigenvalue set as eigs

  expQt
  - expQt(Q, 0) = I
  - semigroup: expQt(t1+t2) = expQt(t1) @ expQt(t2)
  - row sums = 1 (stochastic matrix)
  - all entries non-negative
  - analytical diagonal and off-diagonal for 2-state CO
  - large-t convergence to pinf (equilibrium)

  pinf / pinf1
  - analytical values for CO: [2/7, 5/7]
  - sums to 1; all non-negative
  - satisfies pinf @ Q = 0 (global balance)
  - pinf and pinf1 agree to 1e-8

  iGs
  - shapes (kA x kB) and (kB x kA) for CO and CH82
  - CO: GAB = GBA = [[1.0]] (only one exit class)
  - definition check: GAB = -inv(QAA) @ QAB; GBA = -inv(QBB) @ QBA
  - row sums of GBA and GAB in [0, 1]

  phiA / phiF
  - shape (kA,) / (kI,); sums to 1; non-negative
  - CO: phiA = [1.0]

  H, W, detW, dW
  - shape (kA, kA)
  - W(s) = s*I - H(s) identity verified
  - detW consistent with det(W) computed directly
  - dW shape

  iGt
  - shape (kA, kF)
  - iGt(0) = QAB (Eq. 1.20, CH82)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
NumPy 2.0 raises ValueError when assigning a shape-(1,) or (1,1) array to
a scalar slot (w[i] = ...) and TypeError for float() on non-0-d arrays.
Fixed all occurrences with .item() / np.asarray(...).item() in:
  - scalcslib.ideal_dwell_time_pdf
  - scalcslib.ideal_dwell_time_pdf_components
  - scalcslib.adjacent_open_to_shut_range_pdf_components
  - scburst.length_pdf_components
  - scburst.length_no_single_openings_pdf_components
  - scburst.open_time_total_pdf_components
  - scburst.shut_times_inside_burst_pdf_components
  - scburst.shut_time_total_pdf_components_2more_openings
  - scburst.first_opening_length_pdf_components
  - scburst.openings_distr

Added 61 new tests (155 total, all green in 2.05 s):
  tests/test_scalcslib.py — ideal pdf: CO analytical, CH82 regression + props
  tests/test_scburst.py   — burst quantities: phiBurst/endBurst, mean lengths,
                            openings distribution (CH82 @ 100 nM regression)
  tests/test_popen.py     — Popen ideal: pinf identity, monotonicity,
                            range, Popen0, maxPopen (CH82 @ 100 nM / 1 mM)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
`uF` has shape (kF, 1), so the dot product in the areas[i] loop
returns shape (1,) which NumPy >= 2.0 refuses to assign into a
scalar array slot.  Fix: append .item() to extract the scalar.

Fixes printout_distributions / asymptotic_areas crash seen when
calling scl.printout_distributions(mec, tres) in the dcprogs env
(Python 3.13, NumPy 2.4.6).

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Three NumPy 2.0 / API-removal fixes:
- scalcslib.asymptotic_areas: add .item() to shut-time areas[i] loop
  (same shape-(1,) issue as previously fixed open-time loops)
- scalcslib.printout_correlations / corr_*: replace np.rank() with
  np.linalg.matrix_rank() — np.rank was removed in NumPy 2.0
- version.py: add full_version alias (used by demo-dc.py argparse)

Notebook fixes:
- FirstLatency-CH82/CHME97: add .item() to local asymptotic_areas loop
- demo-scalcs: replace fig.gca(projection='3d') with
  fig.add_subplot(111, projection='3d') — gca(projection=) removed
  in matplotlib 3.4+

All 155 tests green; all notebooks and demo-*.py execute cleanly.

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Deleted:
- AUTHORS, COPYING, ChangeLog, NEWS  — all empty (0 bytes)
- MANIFEST.in                        — empty; not used with pyproject.toml
- INSTALL                            — stale (references setup.py which is gone)
- update_version.sh                  — stale (sed-patches setup.py/configure.in)
- dc-unit-test.py                    — broken import (scalcs.test.test); replaced by pytest
- old/                               — pre-Python C/Fortran/SWIG build artefacts
- notebooks/.ipynb_checkpoints/      — Jupyter auto-save artefacts

.gitignore: add **/.ipynb_checkpoints/ so subdirectory checkpoints
are also excluded (root-level pattern did not cover notebooks/).

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Kernel display name, language_info version (3.6.5 to 3.13.13) and
nbformat_minor (1 to 4) updated after execution in dcprogs env.
Stale pre-existing outputs cleared from Simulate_Single_Channel_Intervals.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Pre-fix, the non-monotonic branch of maxPopen (triggered when a Popen
curve goes through a maximum, e.g. with a fast pore blocker) contained
five errors:

1. P1 evaluated at conc instead of conc/fac
2. conc1 overwritten by conc*fac, destroying the conc/fac probe point
3. P2 evaluated at conc instead of conc*fac
4. Bracket updates backwards; referenced conc1 instead of midpoint
5. nstep never incremented

Net effect: P1 == P2 always, so perr = 0 after the first iteration and
the loop exited with no real bisection.  The returned cmax was inaccurate
for any non-monotonic curve; standard monotonic mechanisms were unaffected
because the bisection branch was never entered.

Also add conc = 0.5*(c1+c2) after the loop to return the final bracket
midpoint rather than the penultimate iteration midpoint.

Tests: expand test_popen.py with TestMaxPopenNonMonotonic (7 new tests
using CH82 + fast blocker KB=1 mM); all 168 tests pass.

Notebook: add Popen_and_Dose_Response.ipynb demonstrating ideal/HJC
Popen, dose-response curves, maxPopen/EC50/nH, and the non-monotonic
case, with executed outputs.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…b crash

Pulse profiles replace anonymous positional tuples: ErfPulse, SquarePulse,
InstExpPulse, PairedSquarePulse.  Each carries named, validated parameters
and a profile(t) method.

JumpResult replaces the raw 4-tuple return from solve() and supports
backward-compatible unpacking: t, c, Popen, P = result.

solve() is the single public entry point for time-course calculations,
dispatching to ODE integration or step-wise Q-matrix method.

relaxation_taus() returns RelaxationResult with all four arrays fixing the
live crash in scplotlib.conc_jump_on_off_taus_versus_conc_plot, which
expected 4 return values but received only 2 from old weighted_taus().

jump_summary() separates physics computation from formatting; gamma and Vm
are explicit parameters, no longer hard-coded as 30 pS and -80 mV.
printout() builds the formatted report from jump_summary().

Private helpers prefixed with underscore: _dPdt, _P_t, _coefficient_calc,
_solve_ode, _solve_matrix.

Call sites updated: scplotlib uses relaxation_taus(); jumpmenu.py returns
pulse objects from dialogs instead of raw tuples; demo-rcj.py demonstrates
the new API.  86 new tests in tests/test_cjumps.py.  254/254 passing.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…rated

Covers ErfPulse, SquarePulse, InstExpPulse and PairedSquarePulse with
worked examples: effect of rise time, synaptic decay time constant,
paired-pulse recovery ratio vs inter-pulse interval, ODE vs matrix
solver comparison, weighted on/off taus vs concentration, and the full
API reference summary.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Cell 4: replace relative path open() with pathlib resolution via
scalcs.samples.__file__, add Loader=yaml.UnsafeLoader for Python-object
YAML tags (required by modern PyYAML).

Cell 14: replace removed cjumps.pulse_instexp + cjumps.solve_jump with
InstExpPulse dataclass + cjumps.solve() from the refactored API.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Remove all TODO-marked and equivalent scalar loops, replacing them with
NumPy broadcasting and einsum operations.

qmatlib.py
- eigs(), eigs_sorted(): spectral matrix A now built by batch outer
  product `M.T[:,:,np.newaxis] * N[:,np.newaxis,:]` — no loop over k.
- Qpow(): `np.sum(A * (eig**n).reshape(k,1,1), axis=0)` — no loop.
- Zxx(): C11 = np.matmul(D, C00); C10 assembled via (k,k,kA,kA) batch
  matmul + off-diagonal mask + sum over j axis — both TODO loops gone.
  Z00/Z10/Z11 also converted from list-comprehension to np.matmul.

scalcslib.py
- ideal_dwell_time_pdf_components(): einsum `'j,ijk->ik'` + matmul
  replaces scalar loop over eigenvalue index (TODO removed).
- asymptotic_areas(): einsum `'j,ijk,k->i'` replaces loop over roots.
- corr_limit_A(): one-line np.sum with broadcast coeff replaces loop.
- ideal_subset_mean_life_time(): np.ix_ mask replaces nested i/j loops.

All 254 tests pass unchanged — numerical outputs are identical.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Implements ideal, asymptotic (HJC) and exact first-latency pdfs for the
concentration-step-from-zero scenario. All three levels use phi_shut
(equilibrium shut occupancies at c0=0) as the initial vector and the
shut-side Q-matrix partitioning (A<->F transposition of open-time formulas).

Public API:
  fl.ideal_components(QFF, phi_shut)  -> (eigs, areas)
  fl.ideal_pdf(t, QFF, phi_shut)
  fl.asymptotic_roots(tres, mec)
  fl.asymptotic_areas(tres, roots, phi_shut, mec)
  fl.asymptotic_pdf(t, tres, tau, areas)
  fl.gamma_coefficients(tres, phi_shut, mec)  -> (eigvals, g00, g10, g11)
  fl.exact_pdf(t, tres, roots, areas, eigvals, g00, g10, g11)

All functions handle both scalar and array t (fixes AttributeError in
scl.exact_pdf when called with scalar t via scipy.integrate.quad).

Also adds:
- CHME97() to scalcs/samples/samples.py (5-state, kA=1, kF=4)
- 60 tests in tests/test_firstlatency.py (CH82 + CHME97 regression,
  CO analytical ground truth, tres=0 limit, integration properties)
- Refactored FirstLatency-CH82.ipynb and FirstLatency-CHME97.ipynb to
  use fl.* instead of copy-pasted helper functions; notebooks execute clean

Mathematical ground truth: cross-checked against Fortran pdfLATs.for,
HJCASYMP.FOR and F0HJC.FOR (CHME97 paper, Phil Trans R Soc Lond A 355).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Reproduces tables and figures from Colquhoun, Hawkes, Merlushkin & Edmonds
(1997) Phil Trans R Soc Lond A 355, 1743-1786, using scalcs.firstlatency.

14 cells covering:
- Q matrix at c = 1 mM (full and submatrices)
- Equilibrium occupancies at c = 0 (phi_shut = [0,0,0,1])
- Eigenvalues of -Q (5 values, one near zero)
- Ideal pdf components (eigenvalues of -QFF, areas, tau)
- Asymptotic pdf components at tres = 0.7 ms (roots, areas)
- Ideal vs asymptotic comparison table
- Gamma coefficients (g00, g10, g11) for exact pdf
- Spot-check table: ideal / asymptotic / exact at 6 time points
- Figure 1: all three pdfs on log time axis (0.1 ms – 2 s)
- Figure 2: exact correction zoomed near tres (linear axis)
- Figure 3: asymptotic pdf for varying tres (0.1–0.7 ms)
- Mean first-latency table vs tres
- Numerical verification: exact == asymptotic for t >= 3*tres
- Limit check: tres = 0 recovers ideal roots and areas

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Literal backslash-u escape sequences (₁ etc.) in markdown cells
were stored as 6-char ASCII rather than actual Unicode characters,
causing headings to render as raw escape sequences in Jupyter.

Applied regex replacement to decode all \uXXXX -> actual chars:
  ₁ -> subscript 1 (c_1 headings)
  ₀ -> subscript 0 (c_0 headings)
  φ -> phi (phi_shut vector heading)
  ᴾ/ᵉ/ˢ -> t_res modifier letters
  ...and 8 others (—, →, ∞, §, etc.)

All 14 cells execute clean; 261 kB output.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Complete rewrite to properly reproduce the numerical example of a
concentration step from c0=0 to c1=1mM in CHME97 (Phil Trans R Soc
Lond A 355, 1743-1786).

Key changes from previous version:
- Scope explicitly stated: jump from zero concentration throughout
- Initial condition phi: documents two physically distinct scenarios
    paper: equilibrium of {A2D<->A2R<->A2R*} sub-block at c0=0
           phi ~ [0.819, 0.175, 0, ~0] for [A2D, A2R, AR, R]
    code:  pinf(Q_c0)[kA:] = [0, 0, 0, 1] (all in unliganded R)
    notebook computes both and uses the paper's phi
- Figure 5(b) reproduction: FL (ideal) and AFL (apparent, t_res=0.7ms)
    on linear 0-100 ms axes matching the paper; notes that paper uses
    t_res=1ms which is numerically inaccessible for this mechanism
- Table 5 reproduction: actual (xi=0) and apparent (xi=0.7ms) shut-time
    components using equilibrium initial vector phi_eq=[0,1,0,0]
    (channel re-enters A2R when A2R* closes via alpha=916 s^-1)
- Comparison panel: FL/AFL for paper's phi vs code's phi, showing the
    physically distinct first-latency distributions and mean latencies

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
The unittest-based test.py (Python 3.4 era) inside the package is
entirely superseded by the modern tests/ suite at the project root
(314 pytest tests). Having it inside the package caused it to be
shipped with pip install unnecessarily.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
asymptotic_roots and gamma_coefficients were recomputed inside every
test method that needed them (~22 and ~11 calls respectively).  Promote
them to six module-scoped fixtures so each is computed exactly once per
session:

    ch82_roots / chme97_roots   -- asymptotic_roots(tres, mec)
    ch82_areas / chme97_areas   -- asymptotic_areas(...)
    ch82_gamma / chme97_gamma   -- gamma_coefficients(...)

All 60 tests pass; no logic changed.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
The lower search bound sas = -1e6 caused exp((QFF - s·I)·tres) to
overflow when |sas| * tres > ln(float64_max) ≈ 709, i.e. tres > ~0.7 ms.
The overflow propagated inf → nan into H(s), breaking the bisection
count and raising LinAlgError.

Fix: clamp sas to -700/tres so the matrix exponential exponent stays
within float64 range.  The bound only tightens when necessary (tres >
0.7 ms); at smaller tres the original -1e6 is kept so the bisection
counting algorithm retains its full working range.

Tested: asymptotic_roots now succeeds for CHME97 at tres = 1 ms.
All 61 firstlatency tests pass.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1. bisect_intervals infinite loop (scalcslib.py)
   Subintervals with zero roots were pushed back onto todo unconditionally,
   causing infinite splitting when H(s) is constant (e.g. tres=0).  Fix:
   discard zero-root subintervals silently instead of re-queuing them.

2. exact_pdf asymptotic branch O(N) scalar loop (firstlatency.py)
   Each t>=3*tres point built a 1-element array and called expPDF
   separately.  Replaced with a single vectorised expPDF call over all
   qualifying points using a boolean mask.

3. test_ideal_pdf_integrates_to_one_co used scipy.integrate.quad
   Adaptive quadrature called ideal_pdf ~100+ times, each recomputing
   the eigendecomposition.  Replaced with the exact analytical result:
   integral of a sum-of-exponentials pdf = sum(areas).

Additional changes:
- Added co_tres0 module-scoped fixture so CO at tres=0 roots/gamma are
  computed once and shared by test_tres_zero_limit_co and
  test_tres_zero_pdf_equals_ideal_co.
- Corrected test_ideal_decreasing_ch82: CH82 first-latency pdf has a
  mode (rises then falls); test now checks positivity + tail monotonicity.
- Removed unused scipy.integrate import from test_firstlatency.py.

Result: 61/61 tests pass in ~13 s (was >1 hour due to infinite loop).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…strings

- New doc/source/firstlatency.rst: Sphinx automodule page with background
  on the three approximation levels (ideal/asymptotic/exact), numerical
  stability notes (adaptive sas bound, tres=0 infinite-loop guard), and
  references (CH82, HJC92, CHME97)
- doc/source/index.rst: add firstlatency.rst to toctree
- README.md: module overview table, firstlatency usage example, CHME97
  reference added
- scalcs/scalcslib.py: expanded docstrings for asymptotic_roots and
  bisect_intervals documenting the float64 overflow constraint and the
  zero-root discard fix

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Remove the entire Sphinx infrastructure (doc/source/*.rst, doc/sphinxext/,
Makefile, conf.py) and replace with hand-written Markdown documentation
covering all nine SCALCS modules.

New files in docs/:
  index.md       - module overview and quick-start
  mechanism.md   - Mechanism class, state classification, Rate functions
  qmatlib.md     - Q-matrix algebra, HJC matrices, gamma coefficients
  scalcslib.md   - dwell-time pdfs, asymptotic root-finding, correlations
  firstlatency.md - first-latency pdf with numerical stability notes
  cjumps.md      - concentration-jump macroscopic calculations
  pdfs.md        - exponential mixture pdf, tcrit utilities
  popen.md       - Popen, EC50, Hill slope
  scburst.md     - burst length and opening-number distributions
  scalcsio.md    - MEC/SCN file I/O
  references.md  - full citation list

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
When dcio is installed, mec_get_list() and mec_load() delegate to
dcio.formats.mec.read_list() / read() and convert the MECRecord result
to a scalcs Mechanism.  The full legacy implementation is kept as a
fallback when dcio is not available.

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Add the first-latency pdf of a finite concentration pulse, both ideal
(no missed events, §3) and apparent (time-interval omission, §4):

- ideal_pulse_pdf / ideal_pulse_components / pulse_PR_ge_one (Eqs 3.6-3.11)
- shut_survivor (+_components): HJC shut-time survivor matrix (Appendix A)
- apparent_pulse_pdf / apparent_pulse_PR_ge_one (Eqs 4.2-4.4), including the
  after-pulse double integral via a reduced zero-concentration {A,B} survivor
- _asymptotic_R: SVD-robust residue matrices (qmatlib.AR's pinf-based null
  vector fails for the leaky reduced system; SVD matches it to ~1e-13 on
  conservative systems)
- qmatlib: factor Cxx out of Zxx to expose the bare survivor C-matrices

Validated against CHME97: P(R>=1) = 0.96553 (ideal) and 0.83472 (apparent)
for the 50 ms / 1 mM example reproduce the paper exactly, and tres->0 reduces
to the ideal pulse pdf. Adds tests and a pytest 'slow' marker for the two
regime-3 quadrature tests. docs updated (firstlatency, qmatlib, index).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
- CHME97-paper-reproduction: add Section 7 reproducing Figure 6 (50 ms pulse
  FL/AFL distributions). Fix the Figure 5b initial vector: a jump *from* zero
  uses phi=[0,0,0,1] (all in R; C absorbing at c=0), not the eq-5.4 jump-*to*-
  zero vector that was used before, which lowered the peak by ~0.005/ms. Mean
  first latency now 122.2 ms, matching Table 5. Also correct the phi_tozero
  state mapping in the Section 6 comparison.
- FirstLatency-CHME97: tres 0.7 -> 1 ms (now stable) and re-execute.
- Concentration_Jumps: minor re-execution.
- Add Flip_a1GlyRWT_Burz2004.yaml sample mechanism (used by the Flip Popen
  notebook).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Add mec_load_json(path) and mechanism_from_dict(d) to build a
scalcs.mechanism.Mechanism from the JSON written by
dcio.formats.modern.write_mechanism. Supports both source_format='mec'
(per-rate 'value', cycles, conductances, conc-dependence) and
'hjcfit_prt' (falls back to the 'final' fitted value).

Round-trip test reproduces a .mec-loaded mechanism exactly: states,
types, conductances, rates, cycles, and Q matrix (rtol 1e-9).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
(cherry picked from commit 2765fc5779f408966d564f8d887dc8163431f443)
master and modernise are two independent modernisations of the same 2021 code,
not successive versions of one. modernise branched in 2021 and was developed in
June 2026; refactoring2, merged into master as #3, was developed separately and
landed in May. Neither contains the other. This makes modernise the basis, on
the evidence below.

  test suite                     74 passed        348 passed, 1 skipped
  popen.maxPopen(tres > 0)       AttributeError   works
  popen.EC50(tres > 0)           AttributeError   works
  stdout noise from one call     558 KB           none
  packaging                      fixed in #4      already correct
  declared dependencies          also Deprecated  numpy, scipy - and it
                                 and tabulate     imports no others
  documentation                  35-file Sphinx   11 Markdown files
                                 tree

The missed-events failures are the substantive point. refactoring2 moved
functions into classes but left module-level callers reaching for qml.GXY,
qml.eGs, qml.phiHJC and qml.dARSdS, none of which exist at module level any
more, so every resolution-corrected Popen calculation on master raises. The
class QMatrix also carries two live debug prints and computes five matrix
inversions eagerly on construction. A 74-test suite did not catch any of it.

Mechanics: this is a merge with modernise's tree taken wholesale, not a
force-push. Both histories are preserved and refactoring2 remains reachable
through the second parent.

Two things are carried over from master rather than taken from modernise:

  * .gitignore, which modernise had deleted
  * the eight tracked scalcs/__pycache__/*.pyc files are dropped again, as in #6

hjclib.py does not survive into this tree. It is the most finished part of
refactoring2 - HJCMatrix, AsymptoticPDF, ExactPDFCalculator, DerivativeCalculator
in 1488 lines - and it is the model to work from when the class API is redone on
top of this base, with 348 tests underneath it to catch the kind of half-migration
that broke master. It is kept on the branch classapi/refactoring2.

Verified on the merged tree: 348 passed, 1 skipped; maxPopen and EC50 at
tres = 30 us return 0.95345 and 2.3561e-06 with no stray output; and the
Colquhoun & Lape (2012) reproduction notebook runs against it unchanged, all
twelve published values within 0.129 %, ALL CHECKS PASSED.

The v0.5.1 and v0.5.1-cl2012 tags are unaffected: they are immutable and point
at cf0df11, and the corrigendum notebook is pinned to the tag.

Co-Authored-By: Claude Opus 5 <[email protected]>
@remislp
remislp merged commit 0b93e67 into DCPROGS:master Aug 6, 2026
@remislp
remislp deleted the base/modernise branch August 6, 2026 09:30
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