Download tests/test_conditioning_support.py from ChatterjeeLab/ScooBDoob: direct link, hf CLI and curl.
- Browser
- Download file 5.16 kB
-
https://huggingface.co/ChatterjeeLab/ScooBDoob/resolve/main/tests/test_conditioning_support.py
- Command line
-
hf download hf://ChatterjeeLab/ScooBDoob/tests/test_conditioning_support.py
-
curl -L -o test_conditioning_support.py https://huggingface.co/ChatterjeeLab/ScooBDoob/resolve/main/tests/test_conditioning_support.py
5.16 kB
| from itertools import product | |
| import numpy as np | |
| import pytest | |
| from scoobdoob import _conditioning | |
| from scoobdoob.validation import bridge | |
| def test_conditioned_probabilities_equal_enumerated_weighted_path_law(): | |
| P = np.array([[0.6, 0.4, 0], [0.2, 0.4, 0.4], [0.3, 0.2, 0.5]]) | |
| weights = np.array( | |
| [[1.0, 2.0, 3.0], [2.0, 0.0, 1.0], [0.5, 2.0, 1.0], [1.0, 3.0, 0.8]] | |
| ) | |
| kernels, _ = _conditioning.build(P, weights, [1, 2], 4, starts=[0, 2]) | |
| for start in [0, 2]: | |
| raw, actual = [], [] | |
| for tail in product(range(3), repeat=4): | |
| path = (start,) + tail | |
| reference = float(tail[-1] in [1, 2]) | |
| probability = 1.0 | |
| for n in range(4): | |
| reference *= P[path[n], path[n + 1]] * weights[n, path[n + 1]] | |
| probability *= kernels[n][path[n], path[n + 1]] | |
| raw.append(reference) | |
| actual.append(probability) | |
| np.testing.assert_allclose( | |
| actual, np.array(raw) / sum(raw), atol=1e-14, rtol=1e-12 | |
| ) | |
| def test_infeasible_start_rejected_without_filtering_initial_distribution(): | |
| P = np.array([[1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]]) | |
| kernels = bridge.doob_kernels(P, [2], 2) | |
| assert not kernels[0][0].any() | |
| with pytest.raises(bridge.InfeasibleBridgeError, match="h_0=0"): | |
| bridge.sample_bridge_paths(kernels, [0, 1], 20) | |
| with pytest.raises(_conditioning.InfeasibleBridgeError): | |
| _conditioning.build(P, np.ones((2, 3)), [2], 2, starts=[0]) | |
| def test_exact_horizon_feasibility_not_just_reachability_by_horizon(): | |
| P = np.array([[0.0, 1.0], [1.0, 0.0]]) | |
| with pytest.raises(bridge.InfeasibleBridgeError): | |
| bridge.sample_bridge_paths(bridge.doob_kernels(P, [1], 2), [0]) | |
| paths = bridge.sample_bridge_paths(bridge.doob_kernels(P, [1], 3), [0], 3) | |
| np.testing.assert_array_equal(paths, [[0, 1, 0, 1]] * 3) | |
| def test_terminal_and_zero_weight_support_remain_exactly_zero(): | |
| P = np.array( | |
| [ | |
| [0.0, 0.5, 0.5, 0.0], | |
| [0.0, 0.0, 0.0, 1.0], | |
| [0.0, 0.0, 0.0, 1.0], | |
| [0.0, 0.0, 0.0, 1.0], | |
| ] | |
| ) | |
| weights = np.array([1.0, 0.0, 1.0, 1.0]) | |
| kernels, h = _conditioning.build(P, [weights] * 2, [3], 2, starts=[0]) | |
| assert np.array_equal(h[-1], [0.0, 0.0, 0.0, 1.0]) | |
| for matrix in kernels: | |
| assert not matrix[:, 1].any() | |
| assert not matrix[P == 0].any() | |
| assert not kernels[-1][:, :3].any() | |
| paths = bridge.sample_bridge_paths(kernels, [0], 10) | |
| np.testing.assert_array_equal(paths, [[0, 2, 3]] * 10) | |
| def test_zero_weights_can_make_every_start_infeasible(): | |
| P = np.ones((2, 2)) / 2 | |
| kernels = bridge.doob_kernels(P, [1], 4, w=np.zeros(2)) | |
| assert all(not k.any() for k in kernels) | |
| with pytest.raises(bridge.InfeasibleBridgeError): | |
| bridge.sample_bridge_paths(kernels, [0]) | |
| def test_small_positive_products_do_not_trigger_fallback_or_underflow(): | |
| P = np.array([[0.7, 0.3], [0.4, 0.6]]) | |
| w = np.full(2, 1e-250) | |
| h = np.array([2e-250, 1e-250]) | |
| expected = P * np.array([2.0, 1.0])[None, :] | |
| expected /= expected.sum(axis=1, keepdims=True) | |
| np.testing.assert_allclose(_conditioning.kernel(P, w, h), expected, rtol=1e-12) | |
| tiny = bridge.doob_kernels(P, [1], 1000, w=w) | |
| unit = bridge.doob_kernels(P, [1], 1000) | |
| np.testing.assert_allclose(tiny, unit, atol=1e-12, rtol=1e-12) | |
| def test_backwards_and_kernels_agree_across_public_interfaces(): | |
| P = np.array([[0.8, 0.2], [0.3, 0.7]]) | |
| weights = np.array([[1.0, 2.0], [0.2, 1.0], [3.0, 0.4]]) | |
| terminal = np.array([0.0, 1.0]) | |
| scaled = _conditioning.backward(P, weights, 3, terminal) | |
| unscaled = _conditioning.backward(P, weights, 3, terminal, rescale=False) | |
| np.testing.assert_allclose(scaled, unscaled / unscaled.sum(axis=1, keepdims=True)) | |
| reference = bridge.doob_kernels(P, [1], 3, weights) | |
| rebuilt = [_conditioning.kernel(P, weights[n], scaled[n + 1]) for n in range(3)] | |
| np.testing.assert_allclose(rebuilt, reference) | |
| kernels, backward = _conditioning.build(P, weights, [1], 3) | |
| np.testing.assert_allclose(kernels, reference) | |
| np.testing.assert_allclose(backward, scaled) | |
| def test_early_target_departures_and_residence_are_still_allowed(): | |
| P = np.array([[0.8, 0.2], [0.5, 0.5]]) | |
| kernels = bridge.doob_kernels(P, [1], 4) | |
| assert kernels[0][0, 0] > 0 | |
| assert kernels[1][1, 0] > 0 | |
| paths = bridge.sample_bridge_paths(kernels, [0], 100, seed=7) | |
| assert all(len(p) == 5 and p[-1] == 1 for p in paths) | |
| def test_invalid_inputs_are_not_silently_repaired(targets, horizon, weights): | |
| with pytest.raises(ValueError): | |
| bridge.doob_kernels(np.ones((2, 2)) / 2, targets, horizon, weights) | |
| def test_invalid_start_indices_are_not_replaced_with_arbitrary_states(): | |
| with pytest.raises(ValueError, match="out-of-range"): | |
| _conditioning.build(np.eye(2), np.ones((2, 2)), [1], 2, starts=[8]) | |