From f18d2b038474c629698f0aa3ec266673960b6f15 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Wed, 12 Aug 2026 14:35:02 +0300 Subject: [PATCH 1/5] Add temporary calibration patch workflow --- .../workflows/patch-calibration-adjusted.yml | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 .github/workflows/patch-calibration-adjusted.yml diff --git a/.github/workflows/patch-calibration-adjusted.yml b/.github/workflows/patch-calibration-adjusted.yml new file mode 100644 index 00000000..60dc160f --- /dev/null +++ b/.github/workflows/patch-calibration-adjusted.yml @@ -0,0 +1,193 @@ +name: Patch calibration adjusted no censoring + +on: + push: + branches: ["agent/calibration-adjusted-no-censoring"] + +permissions: + contents: write + +jobs: + patch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: agent/calibration-adjusted-no-censoring + - uses: astral-sh/setup-uv@v5 + with: + python-version: "3.10" + - name: Apply focused patch + run: | + python - <<'PY' + from pathlib import Path + + path = Path("src/rtichoke/calibration/calibration.py") + text = path.read_text() + old = ''' unsupported = [ + heuristics + for heuristics in heuristics_sets + if heuristics.get("censoring_heuristic") == "adjusted" + or heuristics.get("competing_heuristic") == "adjusted_as_censored" + ] + if unsupported: + raise ValueError( + "Unsupported calibration heuristics: " + "create_calibration_curve_times() does not support " + "censoring_heuristic='adjusted' or " + "competing_heuristic='adjusted_as_censored'. " + "Use a supported heuristic combination such as " + "censoring_heuristic='excluded' with " + "competing_heuristic='adjusted_as_negative'." + ) +''' + new = ''' real_values = reals.values() if isinstance(reals, dict) else [reals] + has_censoring = any(np.any(np.asarray(values) == 0) for values in real_values) + + unsupported_adjusted_censoring = has_censoring and any( + heuristics.get("censoring_heuristic") == "adjusted" + for heuristics in heuristics_sets + ) + unsupported_competing_as_censored = any( + heuristics.get("competing_heuristic") == "adjusted_as_censored" + for heuristics in heuristics_sets + ) + + if unsupported_adjusted_censoring or unsupported_competing_as_censored: + raise ValueError( + "Unsupported calibration heuristics: " + "create_calibration_curve_times() does not support " + "censoring_heuristic='adjusted' when censored observations are present, " + "or competing_heuristic='adjusted_as_censored'. " + "When censoring is present, use a supported heuristic combination such as " + "censoring_heuristic='excluded' with " + "competing_heuristic='adjusted_as_negative'." + ) +''' + if old not in text: + raise SystemExit("Expected calibration validation block not found") + path.write_text(text.replace(old, new)) + + test_path = Path("tests/test_calibration_times.py") + tests = test_path.read_text() + old_test = '''@pytest.mark.parametrize( + "entry_point", + [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], +) +@pytest.mark.parametrize( + "heuristics", + [ + { + "censoring_heuristic": "adjusted", + "competing_heuristic": "adjusted_as_negative", + }, + { + "censoring_heuristic": "excluded", + "competing_heuristic": "adjusted_as_censored", + }, + ], +) +def test_create_calibration_curve_times_rejects_unsupported_heuristics( + entry_point, heuristics +): + probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} + reals = np.array([0, 1, 0, 1]) + times = np.array([1.0, 2.0, 3.0, 4.0]) + + with pytest.raises(ValueError, match="Unsupported calibration heuristics"): + entry_point( + probs, + reals, + times, + fixed_time_horizons=[2.0], + heuristics_sets=[heuristics], + ) +''' + new_test = '''@pytest.mark.parametrize( + "entry_point", + [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], +) +def test_create_calibration_curve_times_allows_adjusted_without_censoring(entry_point): + probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} + reals = np.array([1, 1, 1, 1]) + times = np.array([1.0, 2.0, 3.0, 4.0]) + + fig = entry_point( + probs, + reals, + times, + fixed_time_horizons=[2.0], + heuristics_sets=[ + { + "censoring_heuristic": "adjusted", + "competing_heuristic": "adjusted_as_negative", + } + ], + ) + + assert fig is not None + + +@pytest.mark.parametrize( + "entry_point", + [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], +) +def test_create_calibration_curve_times_rejects_adjusted_with_censoring(entry_point): + probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} + reals = np.array([0, 1, 1, 1]) + times = np.array([1.0, 2.0, 3.0, 4.0]) + + with pytest.raises(ValueError, match="Unsupported calibration heuristics"): + entry_point( + probs, + reals, + times, + fixed_time_horizons=[2.0], + heuristics_sets=[ + { + "censoring_heuristic": "adjusted", + "competing_heuristic": "adjusted_as_negative", + } + ], + ) + + +@pytest.mark.parametrize( + "entry_point", + [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], +) +def test_create_calibration_curve_times_rejects_competing_as_censored(entry_point): + probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} + reals = np.array([1, 1, 1, 1]) + times = np.array([1.0, 2.0, 3.0, 4.0]) + + with pytest.raises(ValueError, match="Unsupported calibration heuristics"): + entry_point( + probs, + reals, + times, + fixed_time_horizons=[2.0], + heuristics_sets=[ + { + "censoring_heuristic": "excluded", + "competing_heuristic": "adjusted_as_censored", + } + ], + ) +''' + if old_test not in tests: + raise SystemExit("Expected calibration heuristic test block not found") + test_path.write_text(tests.replace(old_test, new_test)) + PY + - name: Run focused tests + run: uv run pytest tests/test_calibration_times.py + - name: Commit patch + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add src/rtichoke/calibration/calibration.py tests/test_calibration_times.py + if git diff --cached --quiet; then + exit 0 + fi + git commit -m "Allow adjusted calibration when censoring is absent" + git push origin HEAD:agent/calibration-adjusted-no-censoring From a9e7400e1d02df1b42a308e6494b891f1ac0e9c7 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Wed, 12 Aug 2026 14:37:36 +0300 Subject: [PATCH 2/5] Fix temporary calibration patch workflow --- .../workflows/patch-calibration-adjusted.yml | 162 +----------------- 1 file changed, 2 insertions(+), 160 deletions(-) diff --git a/.github/workflows/patch-calibration-adjusted.yml b/.github/workflows/patch-calibration-adjusted.yml index 60dc160f..f5c3c041 100644 --- a/.github/workflows/patch-calibration-adjusted.yml +++ b/.github/workflows/patch-calibration-adjusted.yml @@ -19,166 +19,8 @@ jobs: python-version: "3.10" - name: Apply focused patch run: | - python - <<'PY' - from pathlib import Path - - path = Path("src/rtichoke/calibration/calibration.py") - text = path.read_text() - old = ''' unsupported = [ - heuristics - for heuristics in heuristics_sets - if heuristics.get("censoring_heuristic") == "adjusted" - or heuristics.get("competing_heuristic") == "adjusted_as_censored" - ] - if unsupported: - raise ValueError( - "Unsupported calibration heuristics: " - "create_calibration_curve_times() does not support " - "censoring_heuristic='adjusted' or " - "competing_heuristic='adjusted_as_censored'. " - "Use a supported heuristic combination such as " - "censoring_heuristic='excluded' with " - "competing_heuristic='adjusted_as_negative'." - ) -''' - new = ''' real_values = reals.values() if isinstance(reals, dict) else [reals] - has_censoring = any(np.any(np.asarray(values) == 0) for values in real_values) - - unsupported_adjusted_censoring = has_censoring and any( - heuristics.get("censoring_heuristic") == "adjusted" - for heuristics in heuristics_sets - ) - unsupported_competing_as_censored = any( - heuristics.get("competing_heuristic") == "adjusted_as_censored" - for heuristics in heuristics_sets - ) - - if unsupported_adjusted_censoring or unsupported_competing_as_censored: - raise ValueError( - "Unsupported calibration heuristics: " - "create_calibration_curve_times() does not support " - "censoring_heuristic='adjusted' when censored observations are present, " - "or competing_heuristic='adjusted_as_censored'. " - "When censoring is present, use a supported heuristic combination such as " - "censoring_heuristic='excluded' with " - "competing_heuristic='adjusted_as_negative'." - ) -''' - if old not in text: - raise SystemExit("Expected calibration validation block not found") - path.write_text(text.replace(old, new)) - - test_path = Path("tests/test_calibration_times.py") - tests = test_path.read_text() - old_test = '''@pytest.mark.parametrize( - "entry_point", - [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], -) -@pytest.mark.parametrize( - "heuristics", - [ - { - "censoring_heuristic": "adjusted", - "competing_heuristic": "adjusted_as_negative", - }, - { - "censoring_heuristic": "excluded", - "competing_heuristic": "adjusted_as_censored", - }, - ], -) -def test_create_calibration_curve_times_rejects_unsupported_heuristics( - entry_point, heuristics -): - probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} - reals = np.array([0, 1, 0, 1]) - times = np.array([1.0, 2.0, 3.0, 4.0]) - - with pytest.raises(ValueError, match="Unsupported calibration heuristics"): - entry_point( - probs, - reals, - times, - fixed_time_horizons=[2.0], - heuristics_sets=[heuristics], - ) -''' - new_test = '''@pytest.mark.parametrize( - "entry_point", - [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], -) -def test_create_calibration_curve_times_allows_adjusted_without_censoring(entry_point): - probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} - reals = np.array([1, 1, 1, 1]) - times = np.array([1.0, 2.0, 3.0, 4.0]) - - fig = entry_point( - probs, - reals, - times, - fixed_time_horizons=[2.0], - heuristics_sets=[ - { - "censoring_heuristic": "adjusted", - "competing_heuristic": "adjusted_as_negative", - } - ], - ) - - assert fig is not None - - -@pytest.mark.parametrize( - "entry_point", - [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], -) -def test_create_calibration_curve_times_rejects_adjusted_with_censoring(entry_point): - probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} - reals = np.array([0, 1, 1, 1]) - times = np.array([1.0, 2.0, 3.0, 4.0]) - - with pytest.raises(ValueError, match="Unsupported calibration heuristics"): - entry_point( - probs, - reals, - times, - fixed_time_horizons=[2.0], - heuristics_sets=[ - { - "censoring_heuristic": "adjusted", - "competing_heuristic": "adjusted_as_negative", - } - ], - ) - - -@pytest.mark.parametrize( - "entry_point", - [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], -) -def test_create_calibration_curve_times_rejects_competing_as_censored(entry_point): - probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} - reals = np.array([1, 1, 1, 1]) - times = np.array([1.0, 2.0, 3.0, 4.0]) - - with pytest.raises(ValueError, match="Unsupported calibration heuristics"): - entry_point( - probs, - reals, - times, - fixed_time_horizons=[2.0], - heuristics_sets=[ - { - "censoring_heuristic": "excluded", - "competing_heuristic": "adjusted_as_censored", - } - ], - ) -''' - if old_test not in tests: - raise SystemExit("Expected calibration heuristic test block not found") - test_path.write_text(tests.replace(old_test, new_test)) - PY + echo 'ZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCgpwYXRoID0gUGF0aCgic3JjL3J0aWNob2tlL2NhbGlicmF0aW9uL2NhbGlicmF0aW9uLnB5IikKdGV4dCA9IHBhdGgucmVhZF90ZXh0KCkKb2xkID0gIiIiICAgIHVuc3VwcG9ydGVkID0gWwogICAgICAgIGhldXJpc3RpY3MKICAgICAgICBmb3IgaGV1cmlzdGljcyBpbiBoZXVyaXN0aWNzX3NldHMKICAgICAgICBpZiBoZXVyaXN0aWNzLmdldCgiY2Vuc29yaW5nX2hldXJpc3RpYyIpID09ICJhZGp1c3RlZCIKICAgICAgICBvciBoZXVyaXN0aWNzLmdldCgiY29tcGV0aW5nX2hldXJpc3RpYyIpID09ICJhZGp1c3RlZF9hc19jZW5zb3JlZCIKICAgIF0KICAgIGlmIHVuc3VwcG9ydGVkOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoCiAgICAgICAgICAgICJVbnN1cHBvcnRlZCBjYWxpYnJhdGlvbiBoZXVyaXN0aWNzOiAiCiAgICAgICAgICAgICJjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXMoKSBkb2VzIG5vdCBzdXBwb3J0ICIKICAgICAgICAgICAgImNlbnNvcmluZ19oZXVyaXN0aWM9J2FkanVzdGVkJyBvciAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19jZW5zb3JlZCcuICIKICAgICAgICAgICAgIlVzZSBhIHN1cHBvcnRlZCBoZXVyaXN0aWMgY29tYmluYXRpb24gc3VjaCBhcyAiCiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljPSdleGNsdWRlZCcgd2l0aCAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19uZWdhdGl2ZScuIgogICAgICAgICkKIiIiCm5ldyA9ICIiIiAgICByZWFsX3ZhbHVlcyA9IHJlYWxzLnZhbHVlcygpIGlmIGlzaW5zdGFuY2UocmVhbHMsIGRpY3QpIGVsc2UgW3JlYWxzXQogICAgaGFzX2NlbnNvcmluZyA9IGFueShucC5hbnkobnAuYXNhcnJheSh2YWx1ZXMpID09IDApIGZvciB2YWx1ZXMgaW4gcmVhbF92YWx1ZXMpCgogICAgdW5zdXBwb3J0ZWRfYWRqdXN0ZWRfY2Vuc29yaW5nID0gaGFzX2NlbnNvcmluZyBhbmQgYW55KAogICAgICAgIGhldXJpc3RpY3MuZ2V0KCJjZW5zb3JpbmdfaGV1cmlzdGljIikgPT0gImFkanVzdGVkIgogICAgICAgIGZvciBoZXVyaXN0aWNzIGluIGhldXJpc3RpY3Nfc2V0cwogICAgKQogICAgdW5zdXBwb3J0ZWRfY29tcGV0aW5nX2FzX2NlbnNvcmVkID0gYW55KAogICAgICAgIGhldXJpc3RpY3MuZ2V0KCJjb21wZXRpbmdfaGV1cmlzdGljIikgPT0gImFkanVzdGVkX2FzX2NlbnNvcmVkIgogICAgICAgIGZvciBoZXVyaXN0aWNzIGluIGhldXJpc3RpY3Nfc2V0cwogICAgKQoKICAgIGlmIHVuc3VwcG9ydGVkX2FkanVzdGVkX2NlbnNvcmluZyBvciB1bnN1cHBvcnRlZF9jb21wZXRpbmdfYXNfY2Vuc29yZWQ6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcigKICAgICAgICAgICAgIlVuc3VwcG9ydGVkIGNhbGlicmF0aW9uIGhldXJpc3RpY3M6ICIKICAgICAgICAgICAgImNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lcygpIGRvZXMgbm90IHN1cHBvcnQgIgogICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYz0nYWRqdXN0ZWQnIHdoZW4gY2Vuc29yZWQgb2JzZXJ2YXRpb25zIGFyZSBwcmVzZW50LCAiCiAgICAgICAgICAgICJvciBjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19jZW5zb3JlZCcuICIKICAgICAgICAgICAgIldoZW4gY2Vuc29yaW5nIGlzIHByZXNlbnQsIHVzZSBhIHN1cHBvcnRlZCBoZXVyaXN0aWMgY29tYmluYXRpb24gc3VjaCBhcyAiCiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljPSdleGNsdWRlZCcgd2l0aCAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19uZWdhdGl2ZScuIgogICAgICAgICkKIiIiCmlmIG9sZCBub3QgaW4gdGV4dDoKICAgIHJhaXNlIFN5c3RlbUV4aXQoIkV4cGVjdGVkIGNhbGlicmF0aW9uIHZhbGlkYXRpb24gYmxvY2sgbm90IGZvdW5kIikKcGF0aC53cml0ZV90ZXh0KHRleHQucmVwbGFjZShvbGQsIG5ldykpCgp0ZXN0X3BhdGggPSBQYXRoKCJ0ZXN0cy90ZXN0X2NhbGlicmF0aW9uX3RpbWVzLnB5IikKdGVzdHMgPSB0ZXN0X3BhdGgucmVhZF90ZXh0KCkKb2xkX3Rlc3QgPSAiIiJAcHl0ZXN0Lm1hcmsucGFyYW1ldHJpemUoCiAgICAiZW50cnlfcG9pbnQiLAogICAgW2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc190b3BfbGV2ZWwsIGNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19kaXJlY3RdLAopCkBweXRlc3QubWFyay5wYXJhbWV0cml6ZSgKICAgICJoZXVyaXN0aWNzIiwKICAgIFsKICAgICAgICB7CiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljIjogImFkanVzdGVkIiwKICAgICAgICAgICAgImNvbXBldGluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWRfYXNfbmVnYXRpdmUiLAogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYyI6ICJleGNsdWRlZCIsCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljIjogImFkanVzdGVkX2FzX2NlbnNvcmVkIiwKICAgICAgICB9LAogICAgXSwKKQpkZWYgdGVzdF9jcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfcmVqZWN0c191bnN1cHBvcnRlZF9oZXVyaXN0aWNzKAogICAgZW50cnlfcG9pbnQsIGhldXJpc3RpY3MKKToKICAgIHByb2JzID0geyJtb2RlbF8xIjogbnAuYXJyYXkoWzAuMSwgMC4yLCAwLjMsIDAuNF0pfQogICAgcmVhbHMgPSBucC5hcnJheShbMCwgMSwgMCwgMV0pCiAgICB0aW1lcyA9IG5wLmFycmF5KFsxLjAsIDIuMCwgMy4wLCA0LjBdKQoKICAgIHdpdGggcHl0ZXN0LnJhaXNlcyhWYWx1ZUVycm9yLCBtYXRjaD0iVW5zdXBwb3J0ZWQgY2FsaWJyYXRpb24gaGV1cmlzdGljcyIpOgogICAgICAgIGVudHJ5X3BvaW50KAogICAgICAgICAgICBwcm9icywKICAgICAgICAgICAgcmVhbHMsCiAgICAgICAgICAgIHRpbWVzLAogICAgICAgICAgICBmaXhlZF90aW1lX2hvcml6b25zPVsyLjBdLAogICAgICAgICAgICBoZXVyaXN0aWNzX3NldHM9W2hldXJpc3RpY3NdLAogICAgICAgICkKIiIiCm5ld190ZXN0ID0gIiIiQHB5dGVzdC5tYXJrLnBhcmFtZXRyaXplKAogICAgImVudHJ5X3BvaW50IiwKICAgIFtjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfdG9wX2xldmVsLCBjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfZGlyZWN0XSwKKQpkZWYgdGVzdF9jcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfYWxsb3dzX2FkanVzdGVkX3dpdGhvdXRfY2Vuc29yaW5nKGVudHJ5X3BvaW50KToKICAgIHByb2JzID0geyJtb2RlbF8xIjogbnAuYXJyYXkoWzAuMSwgMC4yLCAwLjMsIDAuNF0pfQogICAgcmVhbHMgPSBucC5hcnJheShbMSwgMSwgMSwgMV0pCiAgICB0aW1lcyA9IG5wLmFycmF5KFsxLjAsIDIuMCwgMy4wLCA0LjBdKQoKICAgIGZpZyA9IGVudHJ5X3BvaW50KAogICAgICAgIHByb2JzLAogICAgICAgIHJlYWxzLAogICAgICAgIHRpbWVzLAogICAgICAgIGZpeGVkX3RpbWVfaG9yaXpvbnM9WzIuMF0sCiAgICAgICAgaGV1cmlzdGljc19zZXRzPVsKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgImNlbnNvcmluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWQiLAogICAgICAgICAgICAgICAgImNvbXBldGluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWRfYXNfbmVnYXRpdmUiLAogICAgICAgICAgICB9CiAgICAgICAgXSwKICAgICkKCiAgICBhc3NlcnQgZmlnIGlzIG5vdCBOb25lCgoKQHB5dGVzdC5tYXJrLnBhcmFtZXRyaXplKAogICAgImVudHJ5X3BvaW50IiwKICAgIFtjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfdG9wX2xldmVsLCBjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfZGlyZWN0XSwKKQpkZWYgdGVzdF9jcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfcmVqZWN0c19hZGp1c3RlZF93aXRoX2NlbnNvcmluZyhlbnRyeV9wb2ludCk6CiAgICBwcm9icyA9IHsibW9kZWxfMSI6IG5wLmFycmF5KFswLjEsIDAuMiwgMC4zLCAwLjRdKX0KICAgIHJlYWxzID0gbnAuYXJyYXkoWzAsIDEsIDEsIDFdKQogICAgdGltZXMgPSBucC5hcnJheShbMS4wLCAyLjAsIDMuMCwgNC4wXSkKCiAgICB3aXRoIHB5dGVzdC5yYWlzZXMoVmFsdWVFcnJvciwgbWF0Y2g9IlVuc3VwcG9ydGVkIGNhbGlicmF0aW9uIGhldXJpc3RpY3MiKToKICAgICAgICBlbnRyeV9wb2ludCgKICAgICAgICAgICAgcHJvYnMsCiAgICAgICAgICAgIHJlYWxzLAogICAgICAgICAgICB0aW1lcywKICAgICAgICAgICAgZml4ZWRfdGltZV9ob3Jpem9ucz1bMi4wXSwKICAgICAgICAgICAgaGV1cmlzdGljc19zZXRzPVsKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZCIsCiAgICAgICAgICAgICAgICAgICAgImNvbXBldGluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWRfYXNfbmVnYXRpdmUiLAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICBdLAogICAgICAgICkKCgpAcHl0ZXN0Lm1hcmsucGFyYW1ldHJpemUoCiAgICAiZW50cnlfcG9pbnQiLAogICAgW2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc190b3BfbGV2ZWwsIGNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19kaXJlY3RdLAopCmRlZiB0ZXN0X2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19yZWplY3RzX2NvbXBldGluZ19hc19jZW5zb3JlZChlbnRyeV9wb2ludCk6CiAgICBwcm9icyA9IHsibW9kZWxfMSI6IG5wLmFycmF5KFswLjEsIDAuMiwgMC4zLCAwLjRdKX0KICAgIHJlYWxzID0gbnAuYXJyYXkoWzEsIDEsIDEsIDFdKQogICAgdGltZXMgPSBucC5hcnJheShbMS4wLCAyLjAsIDMuMCwgNC4wXSkKCiAgICB3aXRoIHB5dGVzdC5yYWlzZXMoVmFsdWVFcnJvciwgbWF0Y2g9IlVuc3VwcG9ydGVkIGNhbGlicmF0aW9uIGhldXJpc3RpY3MiKToKICAgICAgICBlbnRyeV9wb2ludCgKICAgICAgICAgICAgcHJvYnMsCiAgICAgICAgICAgIHJlYWxzLAogICAgICAgICAgICB0aW1lcywKICAgICAgICAgICAgZml4ZWRfdGltZV9ob3Jpem9ucz1bMi4wXSwKICAgICAgICAgICAgaGV1cmlzdGljc19zZXRzPVsKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYyI6ICJleGNsdWRlZCIsCiAgICAgICAgICAgICAgICAgICAgImNvbXBldGluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWRfYXNfY2Vuc29yZWQiLAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICBdLAogICAgICAgICkKIiIiCmlmIG9sZF90ZXN0IG5vdCBpbiB0ZXN0czoKICAgIHJhaXNlIFN5c3RlbUV4aXQoIkV4cGVjdGVkIGNhbGlicmF0aW9uIGhldXJpc3RpYyB0ZXN0IGJsb2NrIG5vdCBmb3VuZCIpCnRlc3RfcGF0aC53cml0ZV90ZXh0KHRlc3RzLnJlcGxhY2Uob2xkX3Rlc3QsIG5ld190ZXN0KSkK' | base64 -d > /tmp/patch.py + python /tmp/patch.py - name: Run focused tests run: uv run pytest tests/test_calibration_times.py - name: Commit patch From 628a4f8bf712af48b853e6925abbbc4c291ef006 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Wed, 12 Aug 2026 14:39:41 +0300 Subject: [PATCH 3/5] Handle adjusted censoring in calibration internals --- .github/workflows/patch-calibration-adjusted.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/patch-calibration-adjusted.yml b/.github/workflows/patch-calibration-adjusted.yml index f5c3c041..f0e8c5f7 100644 --- a/.github/workflows/patch-calibration-adjusted.yml +++ b/.github/workflows/patch-calibration-adjusted.yml @@ -19,7 +19,7 @@ jobs: python-version: "3.10" - name: Apply focused patch run: | - echo 'ZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCgpwYXRoID0gUGF0aCgic3JjL3J0aWNob2tlL2NhbGlicmF0aW9uL2NhbGlicmF0aW9uLnB5IikKdGV4dCA9IHBhdGgucmVhZF90ZXh0KCkKb2xkID0gIiIiICAgIHVuc3VwcG9ydGVkID0gWwogICAgICAgIGhldXJpc3RpY3MKICAgICAgICBmb3IgaGV1cmlzdGljcyBpbiBoZXVyaXN0aWNzX3NldHMKICAgICAgICBpZiBoZXVyaXN0aWNzLmdldCgiY2Vuc29yaW5nX2hldXJpc3RpYyIpID09ICJhZGp1c3RlZCIKICAgICAgICBvciBoZXVyaXN0aWNzLmdldCgiY29tcGV0aW5nX2hldXJpc3RpYyIpID09ICJhZGp1c3RlZF9hc19jZW5zb3JlZCIKICAgIF0KICAgIGlmIHVuc3VwcG9ydGVkOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoCiAgICAgICAgICAgICJVbnN1cHBvcnRlZCBjYWxpYnJhdGlvbiBoZXVyaXN0aWNzOiAiCiAgICAgICAgICAgICJjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXMoKSBkb2VzIG5vdCBzdXBwb3J0ICIKICAgICAgICAgICAgImNlbnNvcmluZ19oZXVyaXN0aWM9J2FkanVzdGVkJyBvciAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19jZW5zb3JlZCcuICIKICAgICAgICAgICAgIlVzZSBhIHN1cHBvcnRlZCBoZXVyaXN0aWMgY29tYmluYXRpb24gc3VjaCBhcyAiCiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljPSdleGNsdWRlZCcgd2l0aCAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19uZWdhdGl2ZScuIgogICAgICAgICkKIiIiCm5ldyA9ICIiIiAgICByZWFsX3ZhbHVlcyA9IHJlYWxzLnZhbHVlcygpIGlmIGlzaW5zdGFuY2UocmVhbHMsIGRpY3QpIGVsc2UgW3JlYWxzXQogICAgaGFzX2NlbnNvcmluZyA9IGFueShucC5hbnkobnAuYXNhcnJheSh2YWx1ZXMpID09IDApIGZvciB2YWx1ZXMgaW4gcmVhbF92YWx1ZXMpCgogICAgdW5zdXBwb3J0ZWRfYWRqdXN0ZWRfY2Vuc29yaW5nID0gaGFzX2NlbnNvcmluZyBhbmQgYW55KAogICAgICAgIGhldXJpc3RpY3MuZ2V0KCJjZW5zb3JpbmdfaGV1cmlzdGljIikgPT0gImFkanVzdGVkIgogICAgICAgIGZvciBoZXVyaXN0aWNzIGluIGhldXJpc3RpY3Nfc2V0cwogICAgKQogICAgdW5zdXBwb3J0ZWRfY29tcGV0aW5nX2FzX2NlbnNvcmVkID0gYW55KAogICAgICAgIGhldXJpc3RpY3MuZ2V0KCJjb21wZXRpbmdfaGV1cmlzdGljIikgPT0gImFkanVzdGVkX2FzX2NlbnNvcmVkIgogICAgICAgIGZvciBoZXVyaXN0aWNzIGluIGhldXJpc3RpY3Nfc2V0cwogICAgKQoKICAgIGlmIHVuc3VwcG9ydGVkX2FkanVzdGVkX2NlbnNvcmluZyBvciB1bnN1cHBvcnRlZF9jb21wZXRpbmdfYXNfY2Vuc29yZWQ6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcigKICAgICAgICAgICAgIlVuc3VwcG9ydGVkIGNhbGlicmF0aW9uIGhldXJpc3RpY3M6ICIKICAgICAgICAgICAgImNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lcygpIGRvZXMgbm90IHN1cHBvcnQgIgogICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYz0nYWRqdXN0ZWQnIHdoZW4gY2Vuc29yZWQgb2JzZXJ2YXRpb25zIGFyZSBwcmVzZW50LCAiCiAgICAgICAgICAgICJvciBjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19jZW5zb3JlZCcuICIKICAgICAgICAgICAgIldoZW4gY2Vuc29yaW5nIGlzIHByZXNlbnQsIHVzZSBhIHN1cHBvcnRlZCBoZXVyaXN0aWMgY29tYmluYXRpb24gc3VjaCBhcyAiCiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljPSdleGNsdWRlZCcgd2l0aCAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19uZWdhdGl2ZScuIgogICAgICAgICkKIiIiCmlmIG9sZCBub3QgaW4gdGV4dDoKICAgIHJhaXNlIFN5c3RlbUV4aXQoIkV4cGVjdGVkIGNhbGlicmF0aW9uIHZhbGlkYXRpb24gYmxvY2sgbm90IGZvdW5kIikKcGF0aC53cml0ZV90ZXh0KHRleHQucmVwbGFjZShvbGQsIG5ldykpCgp0ZXN0X3BhdGggPSBQYXRoKCJ0ZXN0cy90ZXN0X2NhbGlicmF0aW9uX3RpbWVzLnB5IikKdGVzdHMgPSB0ZXN0X3BhdGgucmVhZF90ZXh0KCkKb2xkX3Rlc3QgPSAiIiJAcHl0ZXN0Lm1hcmsucGFyYW1ldHJpemUoCiAgICAiZW50cnlfcG9pbnQiLAogICAgW2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc190b3BfbGV2ZWwsIGNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19kaXJlY3RdLAopCkBweXRlc3QubWFyay5wYXJhbWV0cml6ZSgKICAgICJoZXVyaXN0aWNzIiwKICAgIFsKICAgICAgICB7CiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljIjogImFkanVzdGVkIiwKICAgICAgICAgICAgImNvbXBldGluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWRfYXNfbmVnYXRpdmUiLAogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYyI6ICJleGNsdWRlZCIsCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljIjogImFkanVzdGVkX2FzX2NlbnNvcmVkIiwKICAgICAgICB9LAogICAgXSwKKQpkZWYgdGVzdF9jcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfcmVqZWN0c191bnN1cHBvcnRlZF9oZXVyaXN0aWNzKAogICAgZW50cnlfcG9pbnQsIGhldXJpc3RpY3MKKToKICAgIHByb2JzID0geyJtb2RlbF8xIjogbnAuYXJyYXkoWzAuMSwgMC4yLCAwLjMsIDAuNF0pfQogICAgcmVhbHMgPSBucC5hcnJheShbMCwgMSwgMCwgMV0pCiAgICB0aW1lcyA9IG5wLmFycmF5KFsxLjAsIDIuMCwgMy4wLCA0LjBdKQoKICAgIHdpdGggcHl0ZXN0LnJhaXNlcyhWYWx1ZUVycm9yLCBtYXRjaD0iVW5zdXBwb3J0ZWQgY2FsaWJyYXRpb24gaGV1cmlzdGljcyIpOgogICAgICAgIGVudHJ5X3BvaW50KAogICAgICAgICAgICBwcm9icywKICAgICAgICAgICAgcmVhbHMsCiAgICAgICAgICAgIHRpbWVzLAogICAgICAgICAgICBmaXhlZF90aW1lX2hvcml6b25zPVsyLjBdLAogICAgICAgICAgICBoZXVyaXN0aWNzX3NldHM9W2hldXJpc3RpY3NdLAogICAgICAgICkKIiIiCm5ld190ZXN0ID0gIiIiQHB5dGVzdC5tYXJrLnBhcmFtZXRyaXplKAogICAgImVudHJ5X3BvaW50IiwKICAgIFtjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfdG9wX2xldmVsLCBjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfZGlyZWN0XSwKKQpkZWYgdGVzdF9jcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfYWxsb3dzX2FkanVzdGVkX3dpdGhvdXRfY2Vuc29yaW5nKGVudHJ5X3BvaW50KToKICAgIHByb2JzID0geyJtb2RlbF8xIjogbnAuYXJyYXkoWzAuMSwgMC4yLCAwLjMsIDAuNF0pfQogICAgcmVhbHMgPSBucC5hcnJheShbMSwgMSwgMSwgMV0pCiAgICB0aW1lcyA9IG5wLmFycmF5KFsxLjAsIDIuMCwgMy4wLCA0LjBdKQoKICAgIGZpZyA9IGVudHJ5X3BvaW50KAogICAgICAgIHByb2JzLAogICAgICAgIHJlYWxzLAogICAgICAgIHRpbWVzLAogICAgICAgIGZpeGVkX3RpbWVfaG9yaXpvbnM9WzIuMF0sCiAgICAgICAgaGV1cmlzdGljc19zZXRzPVsKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgImNlbnNvcmluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWQiLAogICAgICAgICAgICAgICAgImNvbXBldGluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWRfYXNfbmVnYXRpdmUiLAogICAgICAgICAgICB9CiAgICAgICAgXSwKICAgICkKCiAgICBhc3NlcnQgZmlnIGlzIG5vdCBOb25lCgoKQHB5dGVzdC5tYXJrLnBhcmFtZXRyaXplKAogICAgImVudHJ5X3BvaW50IiwKICAgIFtjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfdG9wX2xldmVsLCBjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfZGlyZWN0XSwKKQpkZWYgdGVzdF9jcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXNfcmVqZWN0c19hZGp1c3RlZF93aXRoX2NlbnNvcmluZyhlbnRyeV9wb2ludCk6CiAgICBwcm9icyA9IHsibW9kZWxfMSI6IG5wLmFycmF5KFswLjEsIDAuMiwgMC4zLCAwLjRdKX0KICAgIHJlYWxzID0gbnAuYXJyYXkoWzAsIDEsIDEsIDFdKQogICAgdGltZXMgPSBucC5hcnJheShbMS4wLCAyLjAsIDMuMCwgNC4wXSkKCiAgICB3aXRoIHB5dGVzdC5yYWlzZXMoVmFsdWVFcnJvciwgbWF0Y2g9IlVuc3VwcG9ydGVkIGNhbGlicmF0aW9uIGhldXJpc3RpY3MiKToKICAgICAgICBlbnRyeV9wb2ludCgKICAgICAgICAgICAgcHJvYnMsCiAgICAgICAgICAgIHJlYWxzLAogICAgICAgICAgICB0aW1lcywKICAgICAgICAgICAgZml4ZWRfdGltZV9ob3Jpem9ucz1bMi4wXSwKICAgICAgICAgICAgaGV1cmlzdGljc19zZXRzPVsKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZCIsCiAgICAgICAgICAgICAgICAgICAgImNvbXBldGluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWRfYXNfbmVnYXRpdmUiLAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICBdLAogICAgICAgICkKCgpAcHl0ZXN0Lm1hcmsucGFyYW1ldHJpemUoCiAgICAiZW50cnlfcG9pbnQiLAogICAgW2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc190b3BfbGV2ZWwsIGNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19kaXJlY3RdLAopCmRlZiB0ZXN0X2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19yZWplY3RzX2NvbXBldGluZ19hc19jZW5zb3JlZChlbnRyeV9wb2ludCk6CiAgICBwcm9icyA9IHsibW9kZWxfMSI6IG5wLmFycmF5KFswLjEsIDAuMiwgMC4zLCAwLjRdKX0KICAgIHJlYWxzID0gbnAuYXJyYXkoWzEsIDEsIDEsIDFdKQogICAgdGltZXMgPSBucC5hcnJheShbMS4wLCAyLjAsIDMuMCwgNC4wXSkKCiAgICB3aXRoIHB5dGVzdC5yYWlzZXMoVmFsdWVFcnJvciwgbWF0Y2g9IlVuc3VwcG9ydGVkIGNhbGlicmF0aW9uIGhldXJpc3RpY3MiKToKICAgICAgICBlbnRyeV9wb2ludCgKICAgICAgICAgICAgcHJvYnMsCiAgICAgICAgICAgIHJlYWxzLAogICAgICAgICAgICB0aW1lcywKICAgICAgICAgICAgZml4ZWRfdGltZV9ob3Jpem9ucz1bMi4wXSwKICAgICAgICAgICAgaGV1cmlzdGljc19zZXRzPVsKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYyI6ICJleGNsdWRlZCIsCiAgICAgICAgICAgICAgICAgICAgImNvbXBldGluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWRfYXNfY2Vuc29yZWQiLAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICBdLAogICAgICAgICkKIiIiCmlmIG9sZF90ZXN0IG5vdCBpbiB0ZXN0czoKICAgIHJhaXNlIFN5c3RlbUV4aXQoIkV4cGVjdGVkIGNhbGlicmF0aW9uIGhldXJpc3RpYyB0ZXN0IGJsb2NrIG5vdCBmb3VuZCIpCnRlc3RfcGF0aC53cml0ZV90ZXh0KHRlc3RzLnJlcGxhY2Uob2xkX3Rlc3QsIG5ld190ZXN0KSkK' | base64 -d > /tmp/patch.py + echo 'ZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCgpwYXRoID0gUGF0aCgic3JjL3J0aWNob2tlL2NhbGlicmF0aW9uL2NhbGlicmF0aW9uLnB5IikKdGV4dCA9IHBhdGgucmVhZF90ZXh0KCkKb2xkID0gIiIiICAgIHVuc3VwcG9ydGVkID0gWwogICAgICAgIGhldXJpc3RpY3MKICAgICAgICBmb3IgaGV1cmlzdGljcyBpbiBoZXVyaXN0aWNzX3NldHMKICAgICAgICBpZiBoZXVyaXN0aWNzLmdldCgiY2Vuc29yaW5nX2hldXJpc3RpYyIpID09ICJhZGp1c3RlZCIKICAgICAgICBvciBoZXVyaXN0aWNzLmdldCgiY29tcGV0aW5nX2hldXJpc3RpYyIpID09ICJhZGp1c3RlZF9hc19jZW5zb3JlZCIKICAgIF0KICAgIGlmIHVuc3VwcG9ydGVkOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoCiAgICAgICAgICAgICJVbnN1cHBvcnRlZCBjYWxpYnJhdGlvbiBoZXVyaXN0aWNzOiAiCiAgICAgICAgICAgICJjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXMoKSBkb2VzIG5vdCBzdXBwb3J0ICIKICAgICAgICAgICAgImNlbnNvcmluZ19oZXVyaXN0aWM9J2FkanVzdGVkJyBvciAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19jZW5zb3JlZCcuICIKICAgICAgICAgICAgIlVzZSBhIHN1cHBvcnRlZCBoZXVyaXN0aWMgY29tYmluYXRpb24gc3VjaCBhcyAiCiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljPSdleGNsdWRlZCcgd2l0aCAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19uZWdhdGl2ZScuIgogICAgICAgICkKIiIiCm5ldyA9ICIiIiAgICByZWFsX3ZhbHVlcyA9IHJlYWxzLnZhbHVlcygpIGlmIGlzaW5zdGFuY2UocmVhbHMsIGRpY3QpIGVsc2UgW3JlYWxzXQogICAgaGFzX2NlbnNvcmluZyA9IGFueShucC5hbnkobnAuYXNhcnJheSh2YWx1ZXMpID09IDApIGZvciB2YWx1ZXMgaW4gcmVhbF92YWx1ZXMpCgogICAgdW5zdXBwb3J0ZWRfYWRqdXN0ZWRfY2Vuc29yaW5nID0gaGFzX2NlbnNvcmluZyBhbmQgYW55KAogICAgICAgIGhldXJpc3RpY3MuZ2V0KCJjZW5zb3JpbmdfaGV1cmlzdGljIikgPT0gImFkanVzdGVkIgogICAgICAgIGZvciBoZXVyaXN0aWNzIGluIGhldXJpc3RpY3Nfc2V0cwogICAgKQogICAgdW5zdXBwb3J0ZWRfY29tcGV0aW5nX2FzX2NlbnNvcmVkID0gYW55KAogICAgICAgIGhldXJpc3RpY3MuZ2V0KCJjb21wZXRpbmdfaGV1cmlzdGljIikgPT0gImFkanVzdGVkX2FzX2NlbnNvcmVkIgogICAgICAgIGZvciBoZXVyaXN0aWNzIGluIGhldXJpc3RpY3Nfc2V0cwogICAgKQoKICAgIGlmIHVuc3VwcG9ydGVkX2FkanVzdGVkX2NlbnNvcmluZyBvciB1bnN1cHBvcnRlZF9jb21wZXRpbmdfYXNfY2Vuc29yZWQ6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcigKICAgICAgICAgICAgIlVuc3VwcG9ydGVkIGNhbGlicmF0aW9uIGhldXJpc3RpY3M6ICIKICAgICAgICAgICAgImNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lcygpIGRvZXMgbm90IHN1cHBvcnQgIgogICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYz0nYWRqdXN0ZWQnIHdoZW4gY2Vuc29yZWQgb2JzZXJ2YXRpb25zIGFyZSBwcmVzZW50LCAiCiAgICAgICAgICAgICJvciBjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19jZW5zb3JlZCcuICIKICAgICAgICAgICAgIldoZW4gY2Vuc29yaW5nIGlzIHByZXNlbnQsIHVzZSBhIHN1cHBvcnRlZCBoZXVyaXN0aWMgY29tYmluYXRpb24gc3VjaCBhcyAiCiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljPSdleGNsdWRlZCcgd2l0aCAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19uZWdhdGl2ZScuIgogICAgICAgICkKIiIiCmlmIG9sZCBub3QgaW4gdGV4dDoKICAgIHJhaXNlIFN5c3RlbUV4aXQoIkV4cGVjdGVkIGNhbGlicmF0aW9uIHZhbGlkYXRpb24gYmxvY2sgbm90IGZvdW5kIikKdGV4dCA9IHRleHQucmVwbGFjZShvbGQsIG5ldykKCm9sZF9pbnRlcm5hbCA9ICIiIiAgICBpbml0aWFsX2RmID0gX2J1aWxkX2luaXRpYWxfZGZfZm9yX3RpbWVzKHByb2JzLCByZWFscywgdGltZXMpCgogICAgIyBQYXJ0IDI6IEl0ZXJhdGUgYW5kIGdlbmVyYXRlIGNhbGlicmF0aW9uIGRhdGEgZm9yIGVhY2ggaG9yaXpvbi9oZXVyaXN0aWMKIiIiCm5ld19pbnRlcm5hbCA9ICIiIiAgICBpbml0aWFsX2RmID0gX2J1aWxkX2luaXRpYWxfZGZfZm9yX3RpbWVzKHByb2JzLCByZWFscywgdGltZXMpCiAgICBoYXNfY2Vuc29yaW5nID0gaW5pdGlhbF9kZi5maWx0ZXIocGwuY29sKCJyZWFsIikgPT0gMCkuaGVpZ2h0ID4gMAoKICAgICMgUGFydCAyOiBJdGVyYXRlIGFuZCBnZW5lcmF0ZSBjYWxpYnJhdGlvbiBkYXRhIGZvciBlYWNoIGhvcml6b24vaGV1cmlzdGljCiIiIgppZiBvbGRfaW50ZXJuYWwgbm90IGluIHRleHQ6CiAgICByYWlzZSBTeXN0ZW1FeGl0KCJFeHBlY3RlZCBjYWxpYnJhdGlvbiBpbml0aWFsLWRhdGEgYmxvY2sgbm90IGZvdW5kIikKdGV4dCA9IHRleHQucmVwbGFjZShvbGRfaW50ZXJuYWwsIG5ld19pbnRlcm5hbCkKCm9sZF9za2lwID0gIiIiICAgICAgICAgICAgaWYgKAogICAgICAgICAgICAgICAgY2Vuc29yaW5nX2hldXJpc3RpYyA9PSAiYWRqdXN0ZWQiCiAgICAgICAgICAgICAgICBvciBjb21wZXRpbmdfaGV1cmlzdGljID09ICJhZGp1c3RlZF9hc19jZW5zb3JlZCIKICAgICAgICAgICAgKToKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiIiIgpuZXdfc2tpcCA9ICIiIiAgICAgICAgICAgIGlmICgKICAgICAgICAgICAgICAgIChjZW5zb3JpbmdfaGV1cmlzdGljID09ICJhZGp1c3RlZCIgYW5kIGhhc19jZW5zb3JpbmcpCiAgICAgICAgICAgICAgICBvciBjb21wZXRpbmdfaGV1cmlzdGljID09ICJhZGp1c3RlZF9hc19jZW5zb3JlZCIKICAgICAgICAgICAgKToKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiIiIgppZiBvbGRfc2tpcCBub3QgaW4gdGV4dDoKICAgIHJhaXNlIFN5c3RlbUV4aXQoIkV4cGVjdGVkIGNhbGlicmF0aW9uIGhldXJpc3RpYyBza2lwIGJsb2NrIG5vdCBmb3VuZCIpCnBhdGgud3JpdGVfdGV4dCh0ZXh0LnJlcGxhY2Uob2xkX3NraXAsIG5ld19za2lwKSkKCnRlc3RfcGF0aCA9IFBhdGgoInRlc3RzL3Rlc3RfY2FsaWJyYXRpb25fdGltZXMucHkiKQp0ZXN0cyA9IHRlc3RfcGF0aC5yZWFkX3RleHQoKQpvbGRfdGVzdCA9ICIiIkBweXRlc3QubWFyay5wYXJhbWV0cml6ZSgKICAgICJlbnRyeV9wb2ludCIsCiAgICBbY3JlYXRlX2NhbGlicmF0aW9uX2N1cnZlX3RpbWVzX3RvcF9sZXZlbCwgY3JlYXRlX2NhbGlicmF0aW9uX2N1cnZlX3RpbWVzX2RpcmVjdF0sCikKQHB5dGVzdC5tYXJrLnBhcmFtZXRyaXplKAogICAgImhldXJpc3RpY3MiLAogICAgWwogICAgICAgIHsKICAgICAgICAgICAgImNlbnNvcmluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWQiLAogICAgICAgICAgICAiY29tcGV0aW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZF9hc19uZWdhdGl2ZSIsCiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljIjogImV4Y2x1ZGVkIiwKICAgICAgICAgICAgImNvbXBldGluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWRfYXNfY2Vuc29yZWQiLAogICAgICAgIH0sCiAgICBdLAopCmRlZiB0ZXN0X2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19yZWplY3RzX3Vuc3VwcG9ydGVkX2hldXJpc3RpY3MoCiAgICBlbnRyeV9wb2ludCwgaGV1cmlzdGljcwopOgogICAgcHJvYnMgPSB7Im1vZGVsXzEiOiBucC5hcnJheShbMC4xLCAwLjIsIDAuMywgMC40XSl9CiAgICByZWFscyA9IG5wLmFycmF5KFswLCAxLCAwLCAxXSkKICAgIHRpbWVzID0gbnAuYXJyYXkoWzEuMCwgMi4wLCAzLjAsIDQuMF0pCgogICAgd2l0aCBweXRlc3QucmFpc2VzKFZhbHVlRXJyb3IsIG1hdGNoPSJVbnN1cHBvcnRlZCBjYWxpYnJhdGlvbiBoZXVyaXN0aWNzIik6CiAgICAgICAgZW50cnlfcG9pbnQoCiAgICAgICAgICAgIHByb2JzLAogICAgICAgICAgICByZWFscywKICAgICAgICAgICAgdGltZXMsCiAgICAgICAgICAgIGZpeGVkX3RpbWVfaG9yaXpvbnM9WzIuMF0sCiAgICAgICAgICAgIGhldXJpc3RpY3Nfc2V0cz1baGV1cmlzdGljc10sCiAgICAgICAgKQoiIiIKbmV3X3Rlc3QgPSAiIiJAcHl0ZXN0Lm1hcmsucGFyYW1ldHJpemUoCiAgICAiZW50cnlfcG9pbnQiLAogICAgW2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc190b3BfbGV2ZWwsIGNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19kaXJlY3RdLAopCmRlZiB0ZXN0X2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19hbGxvd3NfYWRqdXN0ZWRfd2l0aG91dF9jZW5zb3JpbmcoZW50cnlfcG9pbnQpOgogICAgcHJvYnMgPSB7Im1vZGVsXzEiOiBucC5hcnJheShbMC4xLCAwLjIsIDAuMywgMC40XSl9CiAgICByZWFscyA9IG5wLmFycmF5KFsxLCAxLCAxLCAxXSkKICAgIHRpbWVzID0gbnAuYXJyYXkoWzEuMCwgMi4wLCAzLjAsIDQuMF0pCgogICAgZmlnID0gZW50cnlfcG9pbnQoCiAgICAgICAgcHJvYnMsCiAgICAgICAgcmVhbHMsCiAgICAgICAgdGltZXMsCiAgICAgICAgZml4ZWRfdGltZV9ob3Jpem9ucz1bMi4wXSwKICAgICAgICBoZXVyaXN0aWNzX3NldHM9WwogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZCIsCiAgICAgICAgICAgICAgICAiY29tcGV0aW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZF9hc19uZWdhdGl2ZSIsCiAgICAgICAgICAgIH0KICAgICAgICBdLAogICAgKQoKICAgIGFzc2VydCBmaWcgaXMgbm90IE5vbmUKCgpAcHl0ZXN0Lm1hcmsucGFyYW1ldHJpemUoCiAgICAiZW50cnlfcG9pbnQiLAogICAgW2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc190b3BfbGV2ZWwsIGNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19kaXJlY3RdLAopCmRlZiB0ZXN0X2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19yZWplY3RzX2FkanVzdGVkX3dpdGhfY2Vuc29yaW5nKGVudHJ5X3BvaW50KToKICAgIHByb2JzID0geyJtb2RlbF8xIjogbnAuYXJyYXkoWzAuMSwgMC4yLCAwLjMsIDAuNF0pfQogICAgcmVhbHMgPSBucC5hcnJheShbMCwgMSwgMSwgMV0pCiAgICB0aW1lcyA9IG5wLmFycmF5KFsxLjAsIDIuMCwgMy4wLCA0LjBdKQoKICAgIHdpdGggcHl0ZXN0LnJhaXNlcyhWYWx1ZUVycm9yLCBtYXRjaD0iVW5zdXBwb3J0ZWQgY2FsaWJyYXRpb24gaGV1cmlzdGljcyIpOgogICAgICAgIGVudHJ5X3BvaW50KAogICAgICAgICAgICBwcm9icywKICAgICAgICAgICAgcmVhbHMsCiAgICAgICAgICAgIHRpbWVzLAogICAgICAgICAgICBmaXhlZF90aW1lX2hvcml6b25zPVsyLjBdLAogICAgICAgICAgICBoZXVyaXN0aWNzX3NldHM9WwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljIjogImFkanVzdGVkIiwKICAgICAgICAgICAgICAgICAgICAiY29tcGV0aW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZF9hc19uZWdhdGl2ZSIsCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIF0sCiAgICAgICAgKQoKCkBweXRlc3QubWFyay5wYXJhbWV0cml6ZSgKICAgICJlbnRyeV9wb2ludCIsCiAgICBbY3JlYXRlX2NhbGlicmF0aW9uX2N1cnZlX3RpbWVzX3RvcF9sZXZlbCwgY3JlYXRlX2NhbGlicmF0aW9uX2N1cnZlX3RpbWVzX2RpcmVjdF0sCikKZGVmIHRlc3RfY3JlYXRlX2NhbGlicmF0aW9uX2N1cnZlX3RpbWVzX3JlamVjdHNfY29tcGV0aW5nX2FzX2NlbnNvcmVkKGVudHJ5X3BvaW50KToKICAgIHByb2JzID0geyJtb2RlbF8xIjogbnAuYXJyYXkoWzAuMSwgMC4yLCAwLjMsIDAuNF0pfQogICAgcmVhbHMgPSBucC5hcnJheShbMSwgMSwgMSwgMV0pCiAgICB0aW1lcyA9IG5wLmFycmF5KFsxLjAsIDIuMCwgMy4wLCA0LjBdKQoKICAgIHdpdGggcHl0ZXN0LnJhaXNlcyhWYWx1ZUVycm9yLCBtYXRjaD0iVW5zdXBwb3J0ZWQgY2FsaWJyYXRpb24gaGV1cmlzdGljcyIpOgogICAgICAgIGVudHJ5X3BvaW50KAogICAgICAgICAgICBwcm9icywKICAgICAgICAgICAgcmVhbHMsCiAgICAgICAgICAgIHRpbWVzLAogICAgICAgICAgICBmaXhlZF90aW1lX2hvcml6b25zPVsyLjBdLAogICAgICAgICAgICBoZXVyaXN0aWNzX3NldHM9WwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljIjogImV4Y2x1ZGVkIiwKICAgICAgICAgICAgICAgICAgICAiY29tcGV0aW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZF9hc19jZW5zb3JlZCIsCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIF0sCiAgICAgICAgKQoiIiIKaWYgb2xkX3Rlc3Qgbm90IGluIHRlc3RzOgogICAgcmFpc2UgU3lzdGVtRXhpdCgiRXhwZWN0ZWQgY2FsaWJyYXRpb24gaGV1cmlzdGljIHRlc3QgYmxvY2sgbm90IGZvdW5kIikKdGVzdF9wYXRoLndyaXRlX3RleHQodGVzdHMucmVwbGFjZShvbGRfdGVzdCwgbmV3X3Rlc3QpKQo=' | base64 -d > /tmp/patch.py python /tmp/patch.py - name: Run focused tests run: uv run pytest tests/test_calibration_times.py From 4d99a5e3613fa3a37e28914b72b5eb99e9e97439 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:40:08 +0000 Subject: [PATCH 4/5] Allow adjusted calibration when censoring is absent --- src/rtichoke/calibration/calibration.py | 27 ++++++---- tests/test_calibration_times.py | 71 +++++++++++++++++++------ 2 files changed, 72 insertions(+), 26 deletions(-) diff --git a/src/rtichoke/calibration/calibration.py b/src/rtichoke/calibration/calibration.py index c809ed6d..0d102d45 100644 --- a/src/rtichoke/calibration/calibration.py +++ b/src/rtichoke/calibration/calibration.py @@ -106,19 +106,25 @@ def create_calibration_curve_times( competing events as censored, which calibration does not support. """ - unsupported = [ - heuristics + real_values = reals.values() if isinstance(reals, dict) else [reals] + has_censoring = any(np.any(np.asarray(values) == 0) for values in real_values) + + unsupported_adjusted_censoring = has_censoring and any( + heuristics.get("censoring_heuristic") == "adjusted" for heuristics in heuristics_sets - if heuristics.get("censoring_heuristic") == "adjusted" - or heuristics.get("competing_heuristic") == "adjusted_as_censored" - ] - if unsupported: + ) + unsupported_competing_as_censored = any( + heuristics.get("competing_heuristic") == "adjusted_as_censored" + for heuristics in heuristics_sets + ) + + if unsupported_adjusted_censoring or unsupported_competing_as_censored: raise ValueError( "Unsupported calibration heuristics: " "create_calibration_curve_times() does not support " - "censoring_heuristic='adjusted' or " - "competing_heuristic='adjusted_as_censored'. " - "Use a supported heuristic combination such as " + "censoring_heuristic='adjusted' when censored observations are present, " + "or competing_heuristic='adjusted_as_censored'. " + "When censoring is present, use a supported heuristic combination such as " "censoring_heuristic='excluded' with " "competing_heuristic='adjusted_as_negative'." ) @@ -1091,6 +1097,7 @@ def _create_calibration_curve_list_times( """ # Part 1: Prepare initial dataframe from inputs initial_df = _build_initial_df_for_times(probs, reals, times) + has_censoring = initial_df.filter(pl.col("real") == 0).height > 0 # Part 2: Iterate and generate calibration data for each horizon/heuristic all_deciles = [] @@ -1105,7 +1112,7 @@ def _create_calibration_curve_list_times( competing_heuristic = heuristics["competing_heuristic"] if ( - censoring_heuristic == "adjusted" + (censoring_heuristic == "adjusted" and has_censoring) or competing_heuristic == "adjusted_as_censored" ): continue diff --git a/tests/test_calibration_times.py b/tests/test_calibration_times.py index 6fbc86ed..5be7d7e3 100644 --- a/tests/test_calibration_times.py +++ b/tests/test_calibration_times.py @@ -64,24 +64,58 @@ def test_create_calibration_curve_times_unequal_size_populations(): "entry_point", [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], ) +def test_create_calibration_curve_times_allows_adjusted_without_censoring(entry_point): + probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} + reals = np.array([1, 1, 1, 1]) + times = np.array([1.0, 2.0, 3.0, 4.0]) + + fig = entry_point( + probs, + reals, + times, + fixed_time_horizons=[2.0], + heuristics_sets=[ + { + "censoring_heuristic": "adjusted", + "competing_heuristic": "adjusted_as_negative", + } + ], + ) + + assert fig is not None + + @pytest.mark.parametrize( - "heuristics", - [ - { - "censoring_heuristic": "adjusted", - "competing_heuristic": "adjusted_as_negative", - }, - { - "censoring_heuristic": "excluded", - "competing_heuristic": "adjusted_as_censored", - }, - ], + "entry_point", + [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], +) +def test_create_calibration_curve_times_rejects_adjusted_with_censoring(entry_point): + probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} + reals = np.array([0, 1, 1, 1]) + times = np.array([1.0, 2.0, 3.0, 4.0]) + + with pytest.raises(ValueError, match="Unsupported calibration heuristics"): + entry_point( + probs, + reals, + times, + fixed_time_horizons=[2.0], + heuristics_sets=[ + { + "censoring_heuristic": "adjusted", + "competing_heuristic": "adjusted_as_negative", + } + ], + ) + + +@pytest.mark.parametrize( + "entry_point", + [create_calibration_curve_times_top_level, create_calibration_curve_times_direct], ) -def test_create_calibration_curve_times_rejects_unsupported_heuristics( - entry_point, heuristics -): +def test_create_calibration_curve_times_rejects_competing_as_censored(entry_point): probs = {"model_1": np.array([0.1, 0.2, 0.3, 0.4])} - reals = np.array([0, 1, 0, 1]) + reals = np.array([1, 1, 1, 1]) times = np.array([1.0, 2.0, 3.0, 4.0]) with pytest.raises(ValueError, match="Unsupported calibration heuristics"): @@ -90,5 +124,10 @@ def test_create_calibration_curve_times_rejects_unsupported_heuristics( reals, times, fixed_time_horizons=[2.0], - heuristics_sets=[heuristics], + heuristics_sets=[ + { + "censoring_heuristic": "excluded", + "competing_heuristic": "adjusted_as_censored", + } + ], ) From a735731e43473be19ab12f70a47e063ccf3de16f Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Wed, 12 Aug 2026 14:41:14 +0300 Subject: [PATCH 5/5] Remove temporary calibration patch workflow --- .../workflows/patch-calibration-adjusted.yml | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 .github/workflows/patch-calibration-adjusted.yml diff --git a/.github/workflows/patch-calibration-adjusted.yml b/.github/workflows/patch-calibration-adjusted.yml deleted file mode 100644 index f0e8c5f7..00000000 --- a/.github/workflows/patch-calibration-adjusted.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Patch calibration adjusted no censoring - -on: - push: - branches: ["agent/calibration-adjusted-no-censoring"] - -permissions: - contents: write - -jobs: - patch: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: agent/calibration-adjusted-no-censoring - - uses: astral-sh/setup-uv@v5 - with: - python-version: "3.10" - - name: Apply focused patch - run: | - echo 'ZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCgpwYXRoID0gUGF0aCgic3JjL3J0aWNob2tlL2NhbGlicmF0aW9uL2NhbGlicmF0aW9uLnB5IikKdGV4dCA9IHBhdGgucmVhZF90ZXh0KCkKb2xkID0gIiIiICAgIHVuc3VwcG9ydGVkID0gWwogICAgICAgIGhldXJpc3RpY3MKICAgICAgICBmb3IgaGV1cmlzdGljcyBpbiBoZXVyaXN0aWNzX3NldHMKICAgICAgICBpZiBoZXVyaXN0aWNzLmdldCgiY2Vuc29yaW5nX2hldXJpc3RpYyIpID09ICJhZGp1c3RlZCIKICAgICAgICBvciBoZXVyaXN0aWNzLmdldCgiY29tcGV0aW5nX2hldXJpc3RpYyIpID09ICJhZGp1c3RlZF9hc19jZW5zb3JlZCIKICAgIF0KICAgIGlmIHVuc3VwcG9ydGVkOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoCiAgICAgICAgICAgICJVbnN1cHBvcnRlZCBjYWxpYnJhdGlvbiBoZXVyaXN0aWNzOiAiCiAgICAgICAgICAgICJjcmVhdGVfY2FsaWJyYXRpb25fY3VydmVfdGltZXMoKSBkb2VzIG5vdCBzdXBwb3J0ICIKICAgICAgICAgICAgImNlbnNvcmluZ19oZXVyaXN0aWM9J2FkanVzdGVkJyBvciAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19jZW5zb3JlZCcuICIKICAgICAgICAgICAgIlVzZSBhIHN1cHBvcnRlZCBoZXVyaXN0aWMgY29tYmluYXRpb24gc3VjaCBhcyAiCiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljPSdleGNsdWRlZCcgd2l0aCAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19uZWdhdGl2ZScuIgogICAgICAgICkKIiIiCm5ldyA9ICIiIiAgICByZWFsX3ZhbHVlcyA9IHJlYWxzLnZhbHVlcygpIGlmIGlzaW5zdGFuY2UocmVhbHMsIGRpY3QpIGVsc2UgW3JlYWxzXQogICAgaGFzX2NlbnNvcmluZyA9IGFueShucC5hbnkobnAuYXNhcnJheSh2YWx1ZXMpID09IDApIGZvciB2YWx1ZXMgaW4gcmVhbF92YWx1ZXMpCgogICAgdW5zdXBwb3J0ZWRfYWRqdXN0ZWRfY2Vuc29yaW5nID0gaGFzX2NlbnNvcmluZyBhbmQgYW55KAogICAgICAgIGhldXJpc3RpY3MuZ2V0KCJjZW5zb3JpbmdfaGV1cmlzdGljIikgPT0gImFkanVzdGVkIgogICAgICAgIGZvciBoZXVyaXN0aWNzIGluIGhldXJpc3RpY3Nfc2V0cwogICAgKQogICAgdW5zdXBwb3J0ZWRfY29tcGV0aW5nX2FzX2NlbnNvcmVkID0gYW55KAogICAgICAgIGhldXJpc3RpY3MuZ2V0KCJjb21wZXRpbmdfaGV1cmlzdGljIikgPT0gImFkanVzdGVkX2FzX2NlbnNvcmVkIgogICAgICAgIGZvciBoZXVyaXN0aWNzIGluIGhldXJpc3RpY3Nfc2V0cwogICAgKQoKICAgIGlmIHVuc3VwcG9ydGVkX2FkanVzdGVkX2NlbnNvcmluZyBvciB1bnN1cHBvcnRlZF9jb21wZXRpbmdfYXNfY2Vuc29yZWQ6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcigKICAgICAgICAgICAgIlVuc3VwcG9ydGVkIGNhbGlicmF0aW9uIGhldXJpc3RpY3M6ICIKICAgICAgICAgICAgImNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lcygpIGRvZXMgbm90IHN1cHBvcnQgIgogICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYz0nYWRqdXN0ZWQnIHdoZW4gY2Vuc29yZWQgb2JzZXJ2YXRpb25zIGFyZSBwcmVzZW50LCAiCiAgICAgICAgICAgICJvciBjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19jZW5zb3JlZCcuICIKICAgICAgICAgICAgIldoZW4gY2Vuc29yaW5nIGlzIHByZXNlbnQsIHVzZSBhIHN1cHBvcnRlZCBoZXVyaXN0aWMgY29tYmluYXRpb24gc3VjaCBhcyAiCiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljPSdleGNsdWRlZCcgd2l0aCAiCiAgICAgICAgICAgICJjb21wZXRpbmdfaGV1cmlzdGljPSdhZGp1c3RlZF9hc19uZWdhdGl2ZScuIgogICAgICAgICkKIiIiCmlmIG9sZCBub3QgaW4gdGV4dDoKICAgIHJhaXNlIFN5c3RlbUV4aXQoIkV4cGVjdGVkIGNhbGlicmF0aW9uIHZhbGlkYXRpb24gYmxvY2sgbm90IGZvdW5kIikKdGV4dCA9IHRleHQucmVwbGFjZShvbGQsIG5ldykKCm9sZF9pbnRlcm5hbCA9ICIiIiAgICBpbml0aWFsX2RmID0gX2J1aWxkX2luaXRpYWxfZGZfZm9yX3RpbWVzKHByb2JzLCByZWFscywgdGltZXMpCgogICAgIyBQYXJ0IDI6IEl0ZXJhdGUgYW5kIGdlbmVyYXRlIGNhbGlicmF0aW9uIGRhdGEgZm9yIGVhY2ggaG9yaXpvbi9oZXVyaXN0aWMKIiIiCm5ld19pbnRlcm5hbCA9ICIiIiAgICBpbml0aWFsX2RmID0gX2J1aWxkX2luaXRpYWxfZGZfZm9yX3RpbWVzKHByb2JzLCByZWFscywgdGltZXMpCiAgICBoYXNfY2Vuc29yaW5nID0gaW5pdGlhbF9kZi5maWx0ZXIocGwuY29sKCJyZWFsIikgPT0gMCkuaGVpZ2h0ID4gMAoKICAgICMgUGFydCAyOiBJdGVyYXRlIGFuZCBnZW5lcmF0ZSBjYWxpYnJhdGlvbiBkYXRhIGZvciBlYWNoIGhvcml6b24vaGV1cmlzdGljCiIiIgppZiBvbGRfaW50ZXJuYWwgbm90IGluIHRleHQ6CiAgICByYWlzZSBTeXN0ZW1FeGl0KCJFeHBlY3RlZCBjYWxpYnJhdGlvbiBpbml0aWFsLWRhdGEgYmxvY2sgbm90IGZvdW5kIikKdGV4dCA9IHRleHQucmVwbGFjZShvbGRfaW50ZXJuYWwsIG5ld19pbnRlcm5hbCkKCm9sZF9za2lwID0gIiIiICAgICAgICAgICAgaWYgKAogICAgICAgICAgICAgICAgY2Vuc29yaW5nX2hldXJpc3RpYyA9PSAiYWRqdXN0ZWQiCiAgICAgICAgICAgICAgICBvciBjb21wZXRpbmdfaGV1cmlzdGljID09ICJhZGp1c3RlZF9hc19jZW5zb3JlZCIKICAgICAgICAgICAgKToKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiIiIgpuZXdfc2tpcCA9ICIiIiAgICAgICAgICAgIGlmICgKICAgICAgICAgICAgICAgIChjZW5zb3JpbmdfaGV1cmlzdGljID09ICJhZGp1c3RlZCIgYW5kIGhhc19jZW5zb3JpbmcpCiAgICAgICAgICAgICAgICBvciBjb21wZXRpbmdfaGV1cmlzdGljID09ICJhZGp1c3RlZF9hc19jZW5zb3JlZCIKICAgICAgICAgICAgKToKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiIiIgppZiBvbGRfc2tpcCBub3QgaW4gdGV4dDoKICAgIHJhaXNlIFN5c3RlbUV4aXQoIkV4cGVjdGVkIGNhbGlicmF0aW9uIGhldXJpc3RpYyBza2lwIGJsb2NrIG5vdCBmb3VuZCIpCnBhdGgud3JpdGVfdGV4dCh0ZXh0LnJlcGxhY2Uob2xkX3NraXAsIG5ld19za2lwKSkKCnRlc3RfcGF0aCA9IFBhdGgoInRlc3RzL3Rlc3RfY2FsaWJyYXRpb25fdGltZXMucHkiKQp0ZXN0cyA9IHRlc3RfcGF0aC5yZWFkX3RleHQoKQpvbGRfdGVzdCA9ICIiIkBweXRlc3QubWFyay5wYXJhbWV0cml6ZSgKICAgICJlbnRyeV9wb2ludCIsCiAgICBbY3JlYXRlX2NhbGlicmF0aW9uX2N1cnZlX3RpbWVzX3RvcF9sZXZlbCwgY3JlYXRlX2NhbGlicmF0aW9uX2N1cnZlX3RpbWVzX2RpcmVjdF0sCikKQHB5dGVzdC5tYXJrLnBhcmFtZXRyaXplKAogICAgImhldXJpc3RpY3MiLAogICAgWwogICAgICAgIHsKICAgICAgICAgICAgImNlbnNvcmluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWQiLAogICAgICAgICAgICAiY29tcGV0aW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZF9hc19uZWdhdGl2ZSIsCiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljIjogImV4Y2x1ZGVkIiwKICAgICAgICAgICAgImNvbXBldGluZ19oZXVyaXN0aWMiOiAiYWRqdXN0ZWRfYXNfY2Vuc29yZWQiLAogICAgICAgIH0sCiAgICBdLAopCmRlZiB0ZXN0X2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19yZWplY3RzX3Vuc3VwcG9ydGVkX2hldXJpc3RpY3MoCiAgICBlbnRyeV9wb2ludCwgaGV1cmlzdGljcwopOgogICAgcHJvYnMgPSB7Im1vZGVsXzEiOiBucC5hcnJheShbMC4xLCAwLjIsIDAuMywgMC40XSl9CiAgICByZWFscyA9IG5wLmFycmF5KFswLCAxLCAwLCAxXSkKICAgIHRpbWVzID0gbnAuYXJyYXkoWzEuMCwgMi4wLCAzLjAsIDQuMF0pCgogICAgd2l0aCBweXRlc3QucmFpc2VzKFZhbHVlRXJyb3IsIG1hdGNoPSJVbnN1cHBvcnRlZCBjYWxpYnJhdGlvbiBoZXVyaXN0aWNzIik6CiAgICAgICAgZW50cnlfcG9pbnQoCiAgICAgICAgICAgIHByb2JzLAogICAgICAgICAgICByZWFscywKICAgICAgICAgICAgdGltZXMsCiAgICAgICAgICAgIGZpeGVkX3RpbWVfaG9yaXpvbnM9WzIuMF0sCiAgICAgICAgICAgIGhldXJpc3RpY3Nfc2V0cz1baGV1cmlzdGljc10sCiAgICAgICAgKQoiIiIKbmV3X3Rlc3QgPSAiIiJAcHl0ZXN0Lm1hcmsucGFyYW1ldHJpemUoCiAgICAiZW50cnlfcG9pbnQiLAogICAgW2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc190b3BfbGV2ZWwsIGNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19kaXJlY3RdLAopCmRlZiB0ZXN0X2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19hbGxvd3NfYWRqdXN0ZWRfd2l0aG91dF9jZW5zb3JpbmcoZW50cnlfcG9pbnQpOgogICAgcHJvYnMgPSB7Im1vZGVsXzEiOiBucC5hcnJheShbMC4xLCAwLjIsIDAuMywgMC40XSl9CiAgICByZWFscyA9IG5wLmFycmF5KFsxLCAxLCAxLCAxXSkKICAgIHRpbWVzID0gbnAuYXJyYXkoWzEuMCwgMi4wLCAzLjAsIDQuMF0pCgogICAgZmlnID0gZW50cnlfcG9pbnQoCiAgICAgICAgcHJvYnMsCiAgICAgICAgcmVhbHMsCiAgICAgICAgdGltZXMsCiAgICAgICAgZml4ZWRfdGltZV9ob3Jpem9ucz1bMi4wXSwKICAgICAgICBoZXVyaXN0aWNzX3NldHM9WwogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAiY2Vuc29yaW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZCIsCiAgICAgICAgICAgICAgICAiY29tcGV0aW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZF9hc19uZWdhdGl2ZSIsCiAgICAgICAgICAgIH0KICAgICAgICBdLAogICAgKQoKICAgIGFzc2VydCBmaWcgaXMgbm90IE5vbmUKCgpAcHl0ZXN0Lm1hcmsucGFyYW1ldHJpemUoCiAgICAiZW50cnlfcG9pbnQiLAogICAgW2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc190b3BfbGV2ZWwsIGNyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19kaXJlY3RdLAopCmRlZiB0ZXN0X2NyZWF0ZV9jYWxpYnJhdGlvbl9jdXJ2ZV90aW1lc19yZWplY3RzX2FkanVzdGVkX3dpdGhfY2Vuc29yaW5nKGVudHJ5X3BvaW50KToKICAgIHByb2JzID0geyJtb2RlbF8xIjogbnAuYXJyYXkoWzAuMSwgMC4yLCAwLjMsIDAuNF0pfQogICAgcmVhbHMgPSBucC5hcnJheShbMCwgMSwgMSwgMV0pCiAgICB0aW1lcyA9IG5wLmFycmF5KFsxLjAsIDIuMCwgMy4wLCA0LjBdKQoKICAgIHdpdGggcHl0ZXN0LnJhaXNlcyhWYWx1ZUVycm9yLCBtYXRjaD0iVW5zdXBwb3J0ZWQgY2FsaWJyYXRpb24gaGV1cmlzdGljcyIpOgogICAgICAgIGVudHJ5X3BvaW50KAogICAgICAgICAgICBwcm9icywKICAgICAgICAgICAgcmVhbHMsCiAgICAgICAgICAgIHRpbWVzLAogICAgICAgICAgICBmaXhlZF90aW1lX2hvcml6b25zPVsyLjBdLAogICAgICAgICAgICBoZXVyaXN0aWNzX3NldHM9WwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljIjogImFkanVzdGVkIiwKICAgICAgICAgICAgICAgICAgICAiY29tcGV0aW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZF9hc19uZWdhdGl2ZSIsCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIF0sCiAgICAgICAgKQoKCkBweXRlc3QubWFyay5wYXJhbWV0cml6ZSgKICAgICJlbnRyeV9wb2ludCIsCiAgICBbY3JlYXRlX2NhbGlicmF0aW9uX2N1cnZlX3RpbWVzX3RvcF9sZXZlbCwgY3JlYXRlX2NhbGlicmF0aW9uX2N1cnZlX3RpbWVzX2RpcmVjdF0sCikKZGVmIHRlc3RfY3JlYXRlX2NhbGlicmF0aW9uX2N1cnZlX3RpbWVzX3JlamVjdHNfY29tcGV0aW5nX2FzX2NlbnNvcmVkKGVudHJ5X3BvaW50KToKICAgIHByb2JzID0geyJtb2RlbF8xIjogbnAuYXJyYXkoWzAuMSwgMC4yLCAwLjMsIDAuNF0pfQogICAgcmVhbHMgPSBucC5hcnJheShbMSwgMSwgMSwgMV0pCiAgICB0aW1lcyA9IG5wLmFycmF5KFsxLjAsIDIuMCwgMy4wLCA0LjBdKQoKICAgIHdpdGggcHl0ZXN0LnJhaXNlcyhWYWx1ZUVycm9yLCBtYXRjaD0iVW5zdXBwb3J0ZWQgY2FsaWJyYXRpb24gaGV1cmlzdGljcyIpOgogICAgICAgIGVudHJ5X3BvaW50KAogICAgICAgICAgICBwcm9icywKICAgICAgICAgICAgcmVhbHMsCiAgICAgICAgICAgIHRpbWVzLAogICAgICAgICAgICBmaXhlZF90aW1lX2hvcml6b25zPVsyLjBdLAogICAgICAgICAgICBoZXVyaXN0aWNzX3NldHM9WwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICJjZW5zb3JpbmdfaGV1cmlzdGljIjogImV4Y2x1ZGVkIiwKICAgICAgICAgICAgICAgICAgICAiY29tcGV0aW5nX2hldXJpc3RpYyI6ICJhZGp1c3RlZF9hc19jZW5zb3JlZCIsCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIF0sCiAgICAgICAgKQoiIiIKaWYgb2xkX3Rlc3Qgbm90IGluIHRlc3RzOgogICAgcmFpc2UgU3lzdGVtRXhpdCgiRXhwZWN0ZWQgY2FsaWJyYXRpb24gaGV1cmlzdGljIHRlc3QgYmxvY2sgbm90IGZvdW5kIikKdGVzdF9wYXRoLndyaXRlX3RleHQodGVzdHMucmVwbGFjZShvbGRfdGVzdCwgbmV3X3Rlc3QpKQo=' | base64 -d > /tmp/patch.py - python /tmp/patch.py - - name: Run focused tests - run: uv run pytest tests/test_calibration_times.py - - name: Commit patch - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add src/rtichoke/calibration/calibration.py tests/test_calibration_times.py - if git diff --cached --quiet; then - exit 0 - fi - git commit -m "Allow adjusted calibration when censoring is absent" - git push origin HEAD:agent/calibration-adjusted-no-censoring