Пример #1
0
    def test_invalid_PDCA_axis(logging_mixin, test_root_hists, PDCA_axis):
        """ Test catching a PDCA on the same axis as the projection axis. """
        import ROOT  # noqa: F401

        # Setup projector
        output_observable = {}
        observable_to_project_from = {"hist3D": test_root_hists.hist3D}
        projection_name_format = "hist"
        obj = projectors.HistProjector(
            output_observable=output_observable,
            observable_to_project_from=observable_to_project_from,
            projection_name_format=projection_name_format,
            projection_information={})

        # Set the projection axes.
        # It is invalid even if the ranges are different
        obj.projection_dependent_cut_axes.append([PDCA_axis])
        obj.projection_axes.append(hist_axis_ranges.x_axis)

        # Perform the projection.
        with pytest.raises(ValueError) as exception_info:
            obj.project()

        assert "This configuration is not allowed" in exception_info.value.args[
            0]
Пример #2
0
    def test_projectors(self, logging_mixin, single_observable,
                        test_root_hists):
        """ Test creation and basic methods of the projection class. """
        import ROOT  # noqa: F401

        # Args
        projection_name_format = "{test} world"
        kwdargs, observable, output_observable = determine_projector_input_args(
            single_observable=single_observable,
            hist=None,
            hist_label="histogram",
        )
        kwdargs["projection_name_format"] = projection_name_format
        kwdargs["projection_information"] = {"test": "Hello"}
        # Create object
        obj = projectors.HistProjector(**kwdargs)

        assert obj.output_attribute_name is ("hist"
                                             if single_observable else None)

        # These objects should be overridden so they aren't super meaningful, but we can still
        # test to ensure that they provide the basic functionality that is expected.
        assert obj.projection_name(
            test="Hello") == projection_name_format.format(test="Hello")
        assert obj.get_hist(
            observable=test_root_hists.hist2D) == test_root_hists.hist2D
        assert obj.output_key_name(
            input_key="input_key",
            output_hist=test_root_hists.hist2D,
            projection_name=projection_name_format.format(
                test="Hello")) == projection_name_format.format(test="Hello")
        assert obj.output_hist(
            output_hist=test_root_hists.hist1D,
            input_observable=test_root_hists.hist2D) == test_root_hists.hist1D

        # Checking printing of the projector settings.
        # Add one additional per selection entry so we have something to print.
        obj.additional_axis_cuts.append("my_axis")
        obj.projection_dependent_cut_axes.append(
            [hist_axis_ranges_without_entries.x_axis])
        obj.projection_axes.append("projection_axis")
        # It is rather minimal, but that is fine since it is only printed information.
        expected_str = "HistProjector: Projection Information:\n"
        expected_str += f"\tprojection_name_format: \"{projection_name_format}\"\n"
        expected_str += "\tprojection_information:\n"
        expected_str += "\n".join([
            "\t\t- " + str("Arg: ") + str(val) for arg, val in {
                "test": "Hello"
            }.items()
        ])
        expected_str += "\n\tadditional_axis_cuts:\n"
        expected_str += "\t\t- my_axis"
        expected_str += "\n\tprojection_dependent_cut_axes:\n"
        expected_str += "\t\t- ['an_x_axis_no_entries']"
        expected_str += "\n\tprojection_axes:\n"
        expected_str += "\t\t- projection_axis"

        assert str(obj) == expected_str
Пример #3
0
def _project_to_part_level(hist: Hist,
                           outliers_removal_axis: OutliersRemovalAxis) -> Hist:
    """Project the input histogram to the particle level axis.

    Args:
        hist: Histogram to check for outliers.
        outliers_removal_axis: Axis along which outliers removal will be performed. Usually
            the particle level aixs.
    Returns:
        The histogram to check for outliers.
    """
    # Setup the projector
    import ROOT

    if isinstance(hist, (ROOT.TH2, ROOT.TH3)):
        projection_information: Dict[str, Any] = {}
        output_object = _OutputObject(None)
        projector = projectors.HistProjector(
            observable_to_project_from=hist,
            output_observable=output_object,
            output_attribute_name="output",
            projection_name_format="outliers_removal_hist",
            projection_information=projection_information,
        )
        # No additional_axis_cuts or projection_dependent_cut_axes
        # Projection axis
        projector.projection_axes.append(
            projectors.HistAxisRange(
                axis_type=outliers_removal_axis,
                axis_range_name="outliers_removal_axis",
                min_val=projectors.HistAxisRange.apply_func_to_find_bin(
                    None, 1),
                max_val=projectors.HistAxisRange.apply_func_to_find_bin(
                    ROOT.TAxis.GetNbins),
            ))

        # Perform the actual projection and return the output.
        projector.project()
        return output_object.output

    # If we already have a 1D hist, just return that existing hist.
    return hist
Пример #4
0
    def test_THn_projection(logging_mixin, test_sparse, single_observable,
                            additional_axis_cuts,
                            expected_additional_axis_cuts_counts,
                            projection_dependent_cut_axes,
                            expected_projection_dependent_cut_axes_counts,
                            projection_axes, expected_projection_axes_counts):
        """ Test projection of a THnSparse into a TH1. """
        import ROOT  # noqa: F401

        # Setup hist ranges
        if additional_axis_cuts:
            additional_axis_cuts = setup_hist_axis_range(additional_axis_cuts)
        if projection_dependent_cut_axes:
            projection_dependent_cut_axes = [
                setup_hist_axis_range(cut)
                for cut in projection_dependent_cut_axes
            ]
        projection_axes = setup_hist_axis_range(projection_axes)
        # Setup objects
        sparse, _ = test_sparse
        # Setup projector
        kwdargs, observable, output_observable = determine_projector_input_args(
            single_observable=single_observable,
            hist=sparse,
            hist_label="hist_sparse",
        )
        kwdargs["projection_name_format"] = "hist"
        kwdargs["projection_information"] = {}
        obj = projectors.HistProjector(**kwdargs)

        # Set the projection axes.
        if additional_axis_cuts is not None:
            obj.additional_axis_cuts.append(additional_axis_cuts)
        if projection_dependent_cut_axes is not None:
            # We need to iterate here separately so that we can separate out the cuts
            # for the disconnected PDCAs.
            for axis_set in projection_dependent_cut_axes:
                obj.projection_dependent_cut_axes.append([axis_set])
        obj.projection_axes.append(projection_axes)

        # Perform the projection.
        obj.project()

        # Check the output and get the projection.
        proj = check_and_get_projection(
            single_observable=single_observable,
            observable=observable,
            output_observable=output_observable,
        )
        assert proj.GetName() == "hist"

        logger.debug(
            f"output_observable: {output_observable}, proj.GetEntries(): {proj.GetEntries()}"
        )

        # Find the non-zero bin content so that it can be checked below.
        non_zero_bins = []
        for x in range(1, proj.GetNcells()):
            if proj.GetBinContent(x) != 0 and not proj.IsBinUnderflow(
                    x) and not proj.IsBinOverflow(x):
                logger.debug(f"non-zero bin at {x}")
                non_zero_bins.append(x)

        # The expected value can be more than one. We find it by multiply the expected values. We can get away with
        # this because the largest value will be a single 2.
        expected_count = expected_additional_axis_cuts_counts * expected_projection_dependent_cut_axes_counts * expected_projection_axes_counts
        # However, we will only find one non-zero bin regardless of the value, so we just check for the one bin if
        # we have a non-zero value.
        assert len(non_zero_bins) == (1 if expected_count else 0)
        # Check the precise bin which was found and the bin value.
        if expected_count != 0:
            # Only check if we actually expected a count
            non_zero_bin_location = next(iter(non_zero_bins))
            # I determined the expected value empirically by looking at the projection.
            assert non_zero_bin_location == 9
            assert proj.GetBinContent(non_zero_bin_location) == expected_count
Пример #5
0
    def test_TH3_to_TH2_projection(self, logging_mixin, test_root_hists,
                                   single_observable, use_PDCA,
                                   additional_cuts, expected_additional_cuts,
                                   projection_axes, expected_projection_axes):
        """ Test projection of a TH3 into a TH2. """
        import ROOT  # noqa: F401

        # Setup hist ranges
        if additional_cuts:
            if use_PDCA:
                additional_cuts = [
                    setup_hist_axis_range(cut) for cut in additional_cuts
                ]
            else:
                additional_cuts = setup_hist_axis_range(additional_cuts)
        projection_axes = [
            setup_hist_axis_range(cut) for cut in projection_axes
        ]
        # Setup projector
        kwdargs, observable, output_observable = determine_projector_input_args(
            single_observable=single_observable,
            hist=test_root_hists.hist3D,
            hist_label="hist3D",
        )
        kwdargs["projection_name_format"] = "hist"
        kwdargs["projection_information"] = {}
        obj = projectors.HistProjector(**kwdargs)

        # Set the projection axes.
        # Using additional cut axes or PDCA is mutually exclusive because we only have one
        # non-projection axis to work with.
        if use_PDCA:
            if additional_cuts is not None:
                # We need to iterate here separately so that we can separate out the cuts
                # for the disconnected PDCAs.
                for axis_set in additional_cuts:
                    obj.projection_dependent_cut_axes.append([axis_set])
        else:
            if additional_cuts is not None:
                obj.additional_axis_cuts.append(additional_cuts)
        for ax in projection_axes:
            obj.projection_axes.append(ax)

        # Perform the projection.
        obj.project()

        # Check the output and get the projection.
        proj = check_and_get_projection(
            single_observable=single_observable,
            observable=observable,
            output_observable=output_observable,
        )
        assert proj.GetName() == "hist"

        logger.debug(
            f"output_observable: {output_observable}, proj.GetEntries(): {proj.GetEntries()}"
        )

        # Check the axes (they should be in the same order that they are defined above).
        # Use the axis max as a proxy (this function name sux).
        assert proj.GetXaxis().GetXmax() == 60.0
        assert proj.GetYaxis().GetXmax() == 0.8
        logger.debug(
            f"x axis min: {proj.GetXaxis().GetXmin()}, y axis min: {proj.GetYaxis().GetXmin()}"
        )

        # Find the non-zero bin content so that it can be checked below.
        non_zero_bins = find_non_zero_bins(hist=proj)

        expected_count = 0
        # It will only be non-zero if all of the expected values are true.
        expected_non_zero_counts = all(
            [expected_additional_cuts, expected_projection_axes])
        if expected_non_zero_counts:
            expected_count = 1
        assert len(non_zero_bins) == expected_count
        # Check the precise bin which was found and the bin value.
        if expected_count != 0:
            # Only check if we actually expected a count
            non_zero_bin_location = next(iter(non_zero_bins))
            # I determined the expected value empirically by looking at the projection.
            assert non_zero_bin_location == 8
            assert proj.GetBinContent(non_zero_bin_location) == 1
Пример #6
0
    def test_TH3_to_TH1_projection(self, logging_mixin, test_root_hists,
                                   single_observable, additional_axis_cuts,
                                   expected_additional_axis_cuts,
                                   projection_dependent_cut_axes,
                                   expected_projection_dependent_cut_axes,
                                   projection_axes, expected_projection_axes):
        """ Test projection from a TH3 to a TH1 derived class. """
        import ROOT  # noqa: F401

        # Setup hist ranges
        if additional_axis_cuts:
            additional_axis_cuts = setup_hist_axis_range(additional_axis_cuts)
        if projection_dependent_cut_axes:
            projection_dependent_cut_axes = [
                setup_hist_axis_range(cut)
                for cut in projection_dependent_cut_axes
            ]
        projection_axes = setup_hist_axis_range(projection_axes)
        # Setup projector
        kwdargs, observable, output_observable = determine_projector_input_args(
            single_observable=single_observable,
            hist=test_root_hists.hist3D,
            hist_label="hist3D",
        )
        kwdargs["projection_name_format"] = "hist"
        kwdargs["projection_information"] = {}
        obj = projectors.HistProjector(**kwdargs)

        # Set the projection axes.
        if additional_axis_cuts is not None:
            obj.additional_axis_cuts.append(additional_axis_cuts)
        if projection_dependent_cut_axes is not None:
            # We need to iterate here separately so that we can separate out the cuts
            # for the disconnected PDCAs.
            for axis_set in projection_dependent_cut_axes:
                obj.projection_dependent_cut_axes.append([axis_set])
        obj.projection_axes.append(projection_axes)

        # Perform the projection.
        obj.project()

        # Check the output and get the projection.
        proj = check_and_get_projection(
            single_observable=single_observable,
            observable=observable,
            output_observable=output_observable,
        )
        assert proj.GetName() == "hist"

        logger.debug(
            f"output_observable: {output_observable}, proj.GetEntries(): {proj.GetEntries()}"
        )

        expected_bins = 5
        # If we don't expect a count, we've restricted the range further, so we need to reflect this in our check.
        if expected_projection_axes is False:
            expected_bins = 4
        assert proj.GetXaxis().GetNbins() == expected_bins

        # Find the non-zero bin content so that it can be checked below.
        non_zero_bins = find_non_zero_bins(hist=proj)

        expected_count = 0
        # It will only be non-zero if all of the expected values are true.
        expected_non_zero_counts = all([
            expected_additional_axis_cuts,
            expected_projection_dependent_cut_axes, expected_projection_axes
        ])
        if expected_non_zero_counts:
            expected_count = 1
        assert len(non_zero_bins) == expected_count
        # Check the precise bin which was found and the bin value.
        if expected_count != 0:
            # Only check if we actually expected a count
            non_zero_bin_location = next(iter(non_zero_bins))
            # I determined the expected value empirically by looking at the projection.
            assert non_zero_bin_location == 1
            assert proj.GetBinContent(non_zero_bin_location) == 1