Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions autofit/non_linear/samples/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ def __init__(self, model: AbstractPriorModel):
self._names = None
self._instance = None

def _rebind_model(self, model: AbstractPriorModel) -> None:
"""Rebind a copied samples object without retaining model-derived caches."""
self.model = model
self._paths = None
self._names = None
self._instance = None

@property
def instance(self):
if self._instance is None:
Expand Down
8 changes: 3 additions & 5 deletions autofit/non_linear/samples/samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ def with_paths(self, paths: Union[List[Tuple[str, ...]], List[str]]) -> "Samples
A set of samples with a reduced set of attributes
"""
with_paths = copy(self)
with_paths.model = self.model.with_paths(paths)
with_paths._rebind_model(model=self.model.with_paths(paths))
with_paths.sample_list = [
sample.with_paths(paths) for sample in self.sample_list
]
Expand All @@ -526,7 +526,7 @@ def without_paths(
A set of samples with a reduced set of attributes
"""
with_paths = copy(self)
with_paths.model = self.model.without_paths(paths)
with_paths._rebind_model(model=self.model.without_paths(paths))
with_paths.sample_list = [
sample.without_paths(paths) for sample in self.sample_list
]
Expand All @@ -538,9 +538,7 @@ def subsamples(self, model):

path_map = self.path_map_for_model(model)
copied = copy(self)
copied._paths = None
copied._names = None
copied.model = model
copied._rebind_model(model=model)

copied.sample_list = [sample.subsample(path_map) for sample in self.sample_list]
return copied
4 changes: 1 addition & 3 deletions autofit/non_linear/samples/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,7 @@ def subsamples(self, model):
return None

copied = copy(self)
copied._paths = None
copied._names = None
copied.model = model
copied._rebind_model(model=model)

copied._max_log_likelihood_sample = self.max_log_likelihood_sample.subsample(
self.path_map_for_model(model)
Expand Down
2 changes: 1 addition & 1 deletion autofit/non_linear/search/abstract_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@ def start_resume_fit(self, analysis: Analysis, model: AbstractPriorModel) -> Res

if mode == 1:
try:
samples_summary.instance
samples_summary.max_log_likelihood()
except exc.FitException as error:
samples = self._test_mode_samples_after_rejected_fit(
samples=samples,
Expand Down
56 changes: 56 additions & 0 deletions test_autofit/non_linear/samples/test_samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,62 @@ def _guarded_samples(parameter_lists, log_likelihood_list, weight_list):
)


def _factor_graph_samples():
shared_value = af.UniformPrior(lower_limit=0.0, upper_limit=1.0)
factor_models = [
af.Collection(galaxies=af.Model(_RejectsLowStoredValue, value=shared_value))
for _ in range(2)
]
factor_graph = af.FactorGraphModel(
*[
af.AnalysisFactor(
prior_model=factor_model,
analysis=af.m.MockAnalysis(),
)
for factor_model in factor_models
]
)
model = factor_graph.global_prior_model
samples = af.SamplesPDF(
model=model,
sample_list=af.Sample.from_lists(
model=model,
parameter_lists=[[0.9]],
log_likelihood_list=[1.0],
log_prior_list=[0.0],
weight_list=[1.0],
),
)
return samples, factor_models[0]


@pytest.mark.parametrize("use_summary", [False, True])
def test__subsamples__rebuilds_instance_after_model_rebind(use_summary):
samples, factor_model = _factor_graph_samples()
source = samples.summary() if use_summary else samples

global_instance = source.instance
child = source.subsamples(model=factor_model)

assert child._instance is None
assert child.instance is not global_instance
assert child.instance.galaxies.value == pytest.approx(0.9)


@pytest.mark.parametrize("method_name", ["with_paths", "without_paths"])
def test__path_filter__clears_model_derived_caches(samples_x5, method_name):
samples_x5.instance
samples_x5.paths
samples_x5.names

paths = [("mock_class_1", "one")]
filtered = getattr(samples_x5, method_name)(paths)

assert filtered._instance is None
assert filtered._paths is None
assert filtered._names is None


def test__table__headers(samples_x5):
assert samples_x5._headers == [
"mock_class_1.one",
Expand Down
44 changes: 44 additions & 0 deletions test_autofit/non_linear/search/test_abstract_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,50 @@ def test__test_mode_1__factor_graph_children_and_sample_type_are_preserved(
assert len(result) == 2
assert all(child.instance.galaxies.value >= 0.75 for child in result)

def test__test_mode_1__valid_factor_graph_children_keep_per_analysis_model(
self, monkeypatch
):
"""Validation must not leave child results bound to a cached global instance."""
monkeypatch.setenv("PYAUTO_TEST_MODE", "1")

shared_value = af.UniformPrior(lower_limit=0.0, upper_limit=1.0)
factor_models = [
af.Collection(galaxies=af.Model(_RejectsLowValue, value=shared_value))
for _ in range(2)
]
factor_graph = af.FactorGraphModel(
*[
af.AnalysisFactor(
prior_model=factor_model,
analysis=af.m.MockAnalysis(),
)
for factor_model in factor_models
]
)
model = factor_graph.global_prior_model
valid_samples = _TaggedSamplesPDF(
model=model,
sample_list=af.Sample.from_lists(
model=model,
parameter_lists=[[0.9]],
log_likelihood_list=[1.0],
log_prior_list=[0.0],
weight_list=[1.0],
),
samples_info={"log_evidence": 1.0, "sampler_marker": "retained"},
)

result = _RejectedFinalSampleSearch(samples=valid_samples).fit(
model=model,
analysis=factor_graph,
)

assert type(result.samples) is _TaggedSamplesPDF
assert len(result) == 2
assert all(
child.instance.galaxies.value == pytest.approx(0.9) for child in result
)

def test__normal_mode__fitexception_still_propagates(self, monkeypatch):
monkeypatch.delenv("PYAUTO_TEST_MODE", raising=False)
model, rejected_samples = _model_and_rejected_samples(_RejectsLowValue)
Expand Down
Loading