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

@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
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!

throw std::runtime_error("Switch node currently only supports numeric comparisons");
}

sel.op = comp.getRequired<std::string_view>("op");
Copy link
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.

else {
throw std::runtime_error("Unknown operator in Switch node: " + sel.op);
}

Copy link
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 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
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.

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