Skip to content

Add Switch node to correctionlib for flexible logic#318

Open
piepb42 wants to merge 3 commits intocms-nanoAOD:masterfrom
piepb42:switch_node
Open

Add Switch node to correctionlib for flexible logic#318
piepb42 wants to merge 3 commits intocms-nanoAOD:masterfrom
piepb42:switch_node

Conversation

@piepb42
Copy link
Copy Markdown

@piepb42 piepb42 commented Jan 21, 2026

Addressing issue: #310

This PR implements a new Switch node (schema v2). The standard Binning node does not support inclusive upper boundaries (e.g., 2.7 < eta <= 3.0), which are necessary for some CMS Jet ID corrections. The Switch node allows users to define a list of arbitrary comparisons (conditions) that are evaluated in order, similar to a Category node but with boolean logic.

Changes:

  1. Added Switch class in C++ core.
  2. Updated schemav2.py bindings to support Switch inputs.
  3. Added tests/test_switch.py to verify behavior.

Documentation for using the JSON produced via switch node implementation and scripts for validation of this JSON vs Hard Coded Cuts: https://codimd.web.cern.ch/s/j8jmJ24HW#

Copy link
Copy Markdown
Collaborator

@nsmith- nsmith- left a comment

Choose a reason for hiding this comment

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

Thanks for implementing this!

Comment thread src/correction.cc
throw std::runtime_error("Switch node currently only supports numeric comparisons");
}

sel.op = comp.getRequired<std::string_view>("op");
Copy link
Copy Markdown
Collaborator

@nsmith- nsmith- Feb 9, 2026

Choose a reason for hiding this comment

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

Convert the string to an operation Enum here so that unknown operators raise on construction rather than evaluation. This also makes the comparison on evaluation much faster by not doing a string comparison for every call.

Comment thread src/correction.cc
else {
throw std::runtime_error("Unknown operator in Switch node: " + sel.op);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Once sel.op is an enum type, you can use a switch statement here

Comment thread tests/test_switch.py
Comment on lines +26 to +48
json_str = corr.model_dump_json(exclude_unset=True)
print(f"Generated JSON with Switch node:\n{json_str}\n")

cset_json = json.dumps({"schema_version": 2, "corrections": [json.loads(json_str)]})
cset = correctionlib.CorrectionSet.from_string(cset_json)
evaluator = cset["boundary_test"]

print("-" * 40)
print("TESTING C++ EVALUATOR")
print("-" * 40)

val_inclusive = 3.0
res_inclusive = evaluator.evaluate([val_inclusive])
print(f"Input: {val_inclusive:<10} | Expected: 1.0 | Got: {res_inclusive}")

val_exclusive = 3.00001
res_exclusive = evaluator.evaluate([val_exclusive])
print(f"Input: {val_exclusive:<10} | Expected: 0.0 | Got: {res_exclusive}")

if res_inclusive == 1.0 and res_exclusive == 0.0:
print("\n[SUCCESS] The Switch node correctly handles inclusive boundaries!")
else:
print("\n[FAIL] Logic error in Switch node implementation.")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

For the pytest framework to pick up this test you need to wrap this whole block into a test_switch() function.

@nsmith-
Copy link
Copy Markdown
Collaborator

nsmith- commented Mar 17, 2026

@piepb42 any estimate when you can address the review comments?

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants