def _obj_func_midp()

in causalml/optimize/unit_selection.py [0:0]


    def _obj_func_midp(self, data, treatment, outcome):
        """
        Calculates bounds for the objective function. Returns the midpoint
        between bounds.

        Parameters
        ----------
        pr_y1_w1 : float
            The probability of conversion given treatment assignment.

        pr_y1_w0 : float
            The probability of conversion given control assignment.

        pr_y0_w1 : float
            The probability of no conversion given treatment assignment
            (1 - pr_y1_w1).

        pr_y0_w0 : float
            The probability of no conversion given control assignment
            (1 - pr_1y_w0)

        pr_y1w1_x : float
            Probability of complier or always-taker given X.

        pr_y0w0_x : float
            Probability of complier or never-taker given X.

        pr_y1w0_x : float
            Probability of defier or always-taker given X.

        pr_y0w1_x : float
            Probability of never-taker or defier given X.

        pr_y_x : float
            Organic probability of conversion.
        """

        X = data.drop([treatment, outcome], axis=1)

        beta = self.complier_payoff
        gamma = self.alwaystaker_payoff
        theta = self.nevertaker_payoff
        delta = self.defier_payoff

        pr_y0_w1, pr_y1_w1 = np.split(
            self.trt_model.predict_proba(X), indices_or_sections=2, axis=1
        )
        pr_y0_w0, pr_y1_w0 = np.split(
            self.ctr_model.predict_proba(X), indices_or_sections=2, axis=1
        )

        segment_prob = self.segment_model.predict_proba(X)
        segment_name = self.segment_model.classes_

        pr_y1w1_x = segment_prob[:, segment_name == "AC"]
        pr_y0w0_x = segment_prob[:, segment_name == "NC"]
        pr_y1w0_x = segment_prob[:, segment_name == "AD"]
        pr_y0w1_x = segment_prob[:, segment_name == "ND"]

        if self.organic_conversion is not None:
            pr_y_x = self.organic_conversion

        else:
            pr_y_x = pr_y1_w0
            warnings.warn(
                "Probability of organic conversion estimated from control observations."
            )

        p1 = (beta - theta) * pr_y1_w1 + delta * pr_y1_w0 + theta * pr_y0_w0
        p2 = gamma * pr_y1_w1 + delta * pr_y0_w1 + (beta - gamma) * pr_y0_w0
        p3 = (
            (gamma - delta) * pr_y1_w1
            + delta * pr_y1_w0
            + theta * pr_y0_w0
            + (beta - gamma - theta + delta) * (pr_y1w1_x + pr_y0w0_x)
        )
        p4 = (
            (beta - theta) * pr_y1_w1
            - (beta - gamma - theta) * pr_y1_w0
            + theta * pr_y0_w0
            + (beta - gamma - theta + delta) * (pr_y1w0_x + pr_y0w1_x)
        )
        p5 = (gamma - delta) * pr_y1_w1 + delta * pr_y1_w0 + theta * pr_y0_w0
        p6 = (
            (beta - theta) * pr_y1_w1
            - (beta - gamma - theta) * pr_y1_w0
            + theta * pr_y0_w0
        )
        p7 = (
            (gamma - delta) * pr_y1_w1
            - (beta - gamma - theta) * pr_y1_w0
            + theta * pr_y0_w0
            + (beta - gamma - theta + delta) * pr_y_x
        )
        p8 = (
            (beta - theta) * pr_y1_w1
            + delta * pr_y1_w0
            + theta * pr_y0_w0
            - (beta - gamma - theta + delta) * pr_y_x
        )

        params_1 = np.concatenate((p1, p2, p3, p4), axis=1)
        params_2 = np.concatenate((p5, p6, p7, p8), axis=1)

        sigma = beta - gamma - theta + delta

        if sigma < 0:
            lower_bound = np.max(params_1, axis=1)
            upper_bound = np.min(params_2, axis=1)

        elif sigma > 0:
            lower_bound = np.max(params_2, axis=1)
            upper_bound = np.min(params_1, axis=1)

        return (lower_bound + upper_bound) / 2