Skip to content

260 Update QualityControlPipeline to consume the QualityMetric.compute() dictionary-based API - #261

Open
jeipollack wants to merge 7 commits into
developfrom
260-bug-update-qc-pipe-to-use-compute-dict
Open

jeipollack wants to merge 7 commits into
developfrom
260-bug-update-qc-pipe-to-use-compute-dict

Conversation

@jeipollack

@jeipollack jeipollack commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR updates the pipeline and configuration handling to support the dictionary-based metric API, allowing a rejection policy to select a specific diagnostic produced by a quality metric.

Context: There was an incompatibility between the QualityMetric.compute() API introduced in PR #258 and the quality-control pipeline, which previously assumed that a metric returned a single array rather than multiple diagnostics in a dictionary.

closes #260

Several files were modified in this PR to resolve the bug, but all are within the scope of the changes. I recommend taking a look at the referenced issue and the Notes section at the bottom, where I tried to provide some additional context on the changes and where to start the review. If you have questions about the processing flow, I'm happy to set up a meeting to go through it together.

What’s changed

  • Update QualityControlPipeline to consume the QualityMetric.compute() dictionary-based API.
  • Allow quality metrics to expose multiple diagnostics through the QualityMetric.diagnostics class attribute.
  • Add a diagnostic field to RejectionPolicyConfig to select which metric diagnostic is used by a rejection policy.
  • Add configuration validation for metric resource requirements and rejection-policy/metric relationships, including validation of configured diagnostics.
  • Separate configuration parsing from cross-section quality-control validation.
  • Update resource handling and pipeline execution to validate required resources after they have been resolved.
  • Update the pixel-mask and goodness-of-fit metrics to provide multiple diagnostics.
  • Move cross-section validation tests from config_test.py to pipeline_test.py.
  • Extend pipeline and configuration test coverage for the updated processing flow and rejection-policy behaviour.
  • Removed deprecated YAML fixtures and tests

How to test / verify

  • Review the updated configuration tests in config_test.py, particularly parsing and validation of rejection-policy configuration.
  • Review the pipeline tests in pipeline_test.py for configuration validation and the updated metric → diagnostic → rejection-policy processing flow.
  • Check the tests covering disabled, single, and multiple rejection policies.
  • Confirm that the CI test suite passes.

Scope

  • Feature
  • Bug fix
  • Hotfix
  • Documentation / process change
  • Internal / refactor
  • Release

This PR is part of the quality-control/outlier-rejection work.

Changelog

The changes are internal and do not introduce a user-visible change to the public product behaviour.

  • Changelog fragment added (if applicable)

Reviewer Checklist

  • The PR targets the correct base branch (develop, or main for release PRs)
  • The PR is assigned to the developer
  • Appropriate labels are applied
  • The PR is included in relevant projects and/or milestones
  • Description clearly explains what has changed
  • Issue references included, if applicable
  • Code and documentation adhere to current standards (ruff)
  • Documentation updates included, if relevant
  • CI tests are passing
  • All reviewer comments have been addressed

Next Steps / Notes (if applicable)

The recommended review path is to start with quality_control/pipeline.py to understand the end-to-end processing flow, then trace the corresponding changes into config.py, metrics/base.py, the concrete metrics, and the tests.

The main processing flow is:

configuration → pipeline → resource resolution → metric computation → diagnostics → rejection policy → validity masks → QualityControlResult

The associated issue contains the more detailed architectural discussion and validation results.

Jennifer Pollack added 3 commits September 30, 2026 18:54
- Add diagnostic attribute to RejectionPolicyConfig
- Update rejection config section parser
- Remove validation methods added to pipeline.py
- Update config_test.py with new and improved test cases
- Update fixtures and remove deprecated YAML fixtures
- Add validation for metric resource requirements and rejection policy configuration
- Call configuration validation from the pipeline constructor
- Update pipeline execution to consume the `QualityMetric.compute` diagnostics API
- Apply rejection policies using their configured diagnostic
- Move cross-section validation tests from config_test.py to pipeline_test.py
- Update and extend pipeline test coverage for the revised validation and updated processing flow
@jeipollack jeipollack added this to the Outlier Removal milestone Sep 30, 2026
@jeipollack jeipollack self-assigned this Sep 30, 2026
@jeipollack jeipollack added bug Something isn't working enhancement New feature or request needed labels Sep 30, 2026

@roryclaydon1994 roryclaydon1994 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Feedback

There were a lot of changes, so I might have missed something, but overall design makes sense.

I couldn't see an example of applying multiple rejection policies per quality metric. Is that an expected use case?

Comment thread src/wf_psf/quality_control/metrics/goodness_of_fit.py
Comment thread src/wf_psf/quality_control/config.py Outdated
"""

enabled: bool = False
diagnostic: str | None = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it ever be valid to allow a RejectionPolicyConfig to be created with a diagnostic=None? I'm wondering if this should be a required parameter

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it's None, then just quality metrics computation part of the pipeline runs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should have waited to reply when I could really concentrate. I agree with your suggestion. Metric evaluation without implementing a rejection policy is already covered by the enabled flag. I'll make diagnostic required so that each configured rejection policy explicitly identifies the metric diagnostic it evaluates.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated.

Comment thread src/wf_psf/quality_control/pipeline.py Outdated
validity_masks[name] = policy.apply(diagnostic)

if validity_masks:
if validity_masks != {}:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious what you found that required the comparison to an empty dict?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if diagnostic is None, then rejection policy application part of the pipeline will not run only the quality metrics. I think Quality Control can also include just computing some metrics without rejecting samples. does that make sense?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it to allow an explicit None to be allowed through the guard?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My previous comment is no longer relevant. The loop that generates validity masks only iterates over enabled rejection policies. If no policies are enabled, _instantiate_rejection_policies() returns an empty dictionary, so validity_masks remains empty.

The integration test test_pipeline_run_rejection_policy_disabled confirms that the metrics are still computed, but rejection policies are not applied when disabled, even when their diagnostic parameters are valid. In this case, the pipeline returns an all-True validity mask.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this fine for you?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say if
if validity_masks:
works then we go for that as it's more pythonic and it would prevent a None value being passed on.

@jeipollack

Copy link
Copy Markdown
Contributor Author

Thanks @roryclaydon1994 for your careful review! I believe I have addressed each comment and query. I am ready for you to take a second look.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request needed

Projects

Development

Successfully merging this pull request may close these issues.

2 participants