def __deriv_probs_sharp_threshold(self, rho1, rho2, rho12):
        """Compute and return derivative of probability of phi(infty) with
        respect to rho1, rho2 and rho12 using the sharp threshold model.

        Returns
        -------
        dprobs_drhos : 2D float numpy array, shape = (4, 3)
        """

        # 3 elements in array are derivative wrt to rho1, rho2 and rho12.
        deriv_prob_noD1 = np.zeros(3, dtype=np.float)
        deriv_prob_noD2 = np.zeros(3, dtype=np.float)
        deriv_prob_noD1_and_noD2 = np.zeros(3, dtype=np.float)

        tau1 = self.tau1
        tau2 = self.tau2
        tau12 = max(tau1, tau2)

        if self.overlap_type == 0:  # coorperative model

            indices = [(k1, k2, k12) for k1 in xrange(tau1)
                                     for k2 in xrange(tau2)
                                     for k12 in xrange(tau12)]

            deriv_prob_noD1[0] = poisson.dcdf_dmu(tau1 - 1, rho1 + rho12)
            deriv_prob_noD1[1] = 0.0
            deriv_prob_noD1[2] = poisson.dcdf_dmu(tau1 - 1, rho1 + rho12)

            deriv_prob_noD2[0] = 0.0
            deriv_prob_noD2[1] = poisson.dcdf_dmu(tau2 - 1, rho2 + rho12)
            deriv_prob_noD2[2] = poisson.dcdf_dmu(tau2 - 1, rho2 + rho12)

            for (k1, k2, k12) in indices:

                no_penetrance = k1 + k12 < tau1 and k2 + k12 < tau2

                if no_penetrance:

                    deriv_prob_noD1_and_noD2[0] = (
                        poisson.dpmf_dmu(k1, rho1) *
                        poisson.pmf(k2, rho2) *
                        poisson.pmf(k12, rho12))

                    deriv_prob_noD1_and_noD2[1] = (
                        poisson.pmf(k1, rho1) *
                        poisson.dpmf_dmu(k2, rho2) *
                        poisson.pmf(k12, rho12))

                    deriv_prob_noD1_and_noD2[2] = (
                        poisson.pmf(k1, rho1)*
                        poisson.pmf(k2, rho2) *
                        poisson.dpmf_dmu(k12, rho12))

        elif self.overlap_type == 1 and rho12 > 0.0:  # competition model
            raise NotImplementedError("competition model not implemented.")

        else:  # independent model, rho12 does not contribute!

            deriv_prob_noD1[0] = poisson.dcdf_dmu(tau1 - 1, rho1)
            deriv_prob_noD1[1] = 0.0
            deriv_prob_noD1[2] = 0.0

            deriv_prob_noD2[0] = 0.0
            deriv_prob_noD2[1] = poisson.dcdf_dmu(tau2 - 1, rho2)
            deriv_prob_noD2[2] = 0.0

            indices = [(k1, k2) for k1 in xrange(tau1)
                                for k2 in xrange(tau2)]

            for (k1, k2) in indices:
                no_penetrance = k1 < tau1 and k2 < tau2

                if no_penetrance:

                    deriv_prob_noD1_and_noD2[0] = (
                        poisson.dpmf_dmu(k1, rho1) *
                        poisson.pmf(k2, rho2))

                    deriv_prob_noD1_and_noD2[1] = (
                        poisson.pmf(k1, rho1) *
                        poisson.dpmf_dmu(k2, rho2))

                    deriv_prob_noD1_and_noD2[2] = 0.0

        # Derivative of phi_infty_probs wrt to rho1, rho2, and rho12
        deriv_probs = np.zeros([4, 3], dtype=np.float)

        # deriv of prob(noD1 and noD2)
        deriv_probs[0] = deriv_prob_noD1_and_noD2

        # deriv of prob(yesD1 and noD2)
        deriv_probs[1] = deriv_prob_noD2 - deriv_prob_noD1_and_noD2

        # deriv of prob(noD1 and yesD2)
        deriv_probs[2] = deriv_prob_noD1 - deriv_prob_noD1_and_noD2

        # deriv of prob(yesD1 and yesD2)
        deriv_probs[3] = -np.sum(deriv_probs[0:3])

        return deriv_probs
    def __deriv_probs_sharp_threshold(self, rho1, rho2, rho12):
        """Compute and return derivative of probability of phi(infty) with
        respect to rho1, rho2 and rho12 using the sharp threshold model.

        Returns
        -------
        dprobs_drhos : 2D float numpy array, shape = (4, 3)
        """

        # 3 elements in array are derivative wrt to rho1, rho2 and rho12.
        deriv_prob_noD1 = np.zeros(3, dtype=np.float)
        deriv_prob_noD2 = np.zeros(3, dtype=np.float)
        deriv_prob_noD1_and_noD2 = np.zeros(3, dtype=np.float)

        tau1 = self.tau1
        tau2 = self.tau2
        tau12 = max(tau1, tau2)

        if self.overlap_type == 0:  # coorperative model

            indices = [(k1, k2, k12) for k1 in xrange(tau1)
                       for k2 in xrange(tau2) for k12 in xrange(tau12)]

            deriv_prob_noD1[0] = poisson.dcdf_dmu(tau1 - 1, rho1 + rho12)
            deriv_prob_noD1[1] = 0.0
            deriv_prob_noD1[2] = poisson.dcdf_dmu(tau1 - 1, rho1 + rho12)

            deriv_prob_noD2[0] = 0.0
            deriv_prob_noD2[1] = poisson.dcdf_dmu(tau2 - 1, rho2 + rho12)
            deriv_prob_noD2[2] = poisson.dcdf_dmu(tau2 - 1, rho2 + rho12)

            for (k1, k2, k12) in indices:

                no_penetrance = k1 + k12 < tau1 and k2 + k12 < tau2

                if no_penetrance:

                    deriv_prob_noD1_and_noD2[0] = (poisson.dpmf_dmu(k1, rho1) *
                                                   poisson.pmf(k2, rho2) *
                                                   poisson.pmf(k12, rho12))

                    deriv_prob_noD1_and_noD2[1] = (poisson.pmf(k1, rho1) *
                                                   poisson.dpmf_dmu(k2, rho2) *
                                                   poisson.pmf(k12, rho12))

                    deriv_prob_noD1_and_noD2[2] = (
                        poisson.pmf(k1, rho1) * poisson.pmf(k2, rho2) *
                        poisson.dpmf_dmu(k12, rho12))

        elif self.overlap_type == 1 and rho12 > 0.0:  # competition model
            raise NotImplementedError("competition model not implemented.")

        else:  # independent model, rho12 does not contribute!

            deriv_prob_noD1[0] = poisson.dcdf_dmu(tau1 - 1, rho1)
            deriv_prob_noD1[1] = 0.0
            deriv_prob_noD1[2] = 0.0

            deriv_prob_noD2[0] = 0.0
            deriv_prob_noD2[1] = poisson.dcdf_dmu(tau2 - 1, rho2)
            deriv_prob_noD2[2] = 0.0

            indices = [(k1, k2) for k1 in xrange(tau1) for k2 in xrange(tau2)]

            for (k1, k2) in indices:
                no_penetrance = k1 < tau1 and k2 < tau2

                if no_penetrance:

                    deriv_prob_noD1_and_noD2[0] = (poisson.dpmf_dmu(k1, rho1) *
                                                   poisson.pmf(k2, rho2))

                    deriv_prob_noD1_and_noD2[1] = (poisson.pmf(k1, rho1) *
                                                   poisson.dpmf_dmu(k2, rho2))

                    deriv_prob_noD1_and_noD2[2] = 0.0

        # Derivative of phi_infty_probs wrt to rho1, rho2, and rho12
        deriv_probs = np.zeros([4, 3], dtype=np.float)

        # deriv of prob(noD1 and noD2)
        deriv_probs[0] = deriv_prob_noD1_and_noD2

        # deriv of prob(yesD1 and noD2)
        deriv_probs[1] = deriv_prob_noD2 - deriv_prob_noD1_and_noD2

        # deriv of prob(noD1 and yesD2)
        deriv_probs[2] = deriv_prob_noD1 - deriv_prob_noD1_and_noD2

        # deriv of prob(yesD1 and yesD2)
        deriv_probs[3] = -np.sum(deriv_probs[0:3])

        return deriv_probs
    def __probs_sharp_threshold(self, rho1, rho2, rho12):
        """Compute and return probability of phi(infty), the age-integrated
        phenotypes using the sharp threshold model.

        Please refer to section 6.2 in the SI Appendix 2 of Rzhetsky et al.
        2007 for details.

        Returns
        -------
        phi_infty_probs : 1D float numpy array (size = 4)

        Notes
        -----
        poisson.pmf(k, mu) is probability mass function of the Poisson
        distribution with parameter mu evualated at k.

        poisson.cdf(kmax, mu) is cumulative of Poisson probability mass
        function from k = 0, 1, 2 to kmax.
        """

        prob_noD1 = 0.0
        prob_noD2 = 0.0
        prob_noD1_and_noD2 = 0.0

        tau1 = self.tau1
        tau2 = self.tau2
        tau12 = max(tau1, tau2)

        if self.overlap_type == 0:  # coorperative model

            indices = [(k1, k2, k12) for k1 in xrange(tau1)
                                     for k2 in xrange(tau2)
                                     for k12 in xrange(tau12)]

            prob_noD1 = poisson.cdf(tau1 - 1, rho1 + rho12)
            prob_noD2 = poisson.cdf(tau2 - 1, rho2 + rho12)

            for (k1, k2, k12) in indices:
                no_penetrance = k1 + k12 < tau1 and k2 + k12 < tau2

                if no_penetrance:
                    prob_noD1_and_noD2 += (poisson.pmf(k1, rho1) *
                                           poisson.pmf(k2, rho2) *
                                           poisson.pmf(k12, rho12))

        elif self.overlap_type == 1 and rho12 > 0.0:  # competition model
            raise NotImplementedError("competition model not implemented.")

        else:  # independent model, rho12 does not contribute!

            prob_noD1 = poisson.cdf(tau1 - 1, rho1)
            prob_noD2 = poisson.cdf(tau2 - 1, rho2)

            indices = [(k1, k2) for k1 in xrange(tau1)
                                for k2 in xrange(tau2)]

            for (k1, k2) in indices:
                no_penetrance = k1 < tau1 and k2 < tau2

                if no_penetrance:
                    prob_noD1_and_noD2 += (poisson.pmf(k1, rho1) *
                                           poisson.pmf(k2, rho2))

         # Instantiate phi_infinity_probs
        phi_infty_probs = np.zeros(4, dtype=np.float)

        # prob(noD1 and noD2)
        phi_infty_probs[0] = prob_noD1_and_noD2

        # prob(yesD1 and noD2)
        phi_infty_probs[1] = prob_noD2 - prob_noD1_and_noD2

        # prob(noD1 and yesD2)
        phi_infty_probs[2] = prob_noD1 - prob_noD1_and_noD2

        # prob(yesD1 and yesD2)
        # use the fact that probability must add up to 1.
        phi_infty_probs[3] = 1.0 - np.sum(phi_infty_probs[0:3])

        return phi_infty_probs
    def __probs_sharp_threshold(self, rho1, rho2, rho12):
        """Compute and return probability of phi(infty), the age-integrated
        phenotypes using the sharp threshold model.

        Please refer to section 6.2 in the SI Appendix 2 of Rzhetsky et al.
        2007 for details.

        Returns
        -------
        phi_infty_probs : 1D float numpy array (size = 4)

        Notes
        -----
        poisson.pmf(k, mu) is probability mass function of the Poisson
        distribution with parameter mu evualated at k.

        poisson.cdf(kmax, mu) is cumulative of Poisson probability mass
        function from k = 0, 1, 2 to kmax.
        """

        prob_noD1 = 0.0
        prob_noD2 = 0.0
        prob_noD1_and_noD2 = 0.0

        tau1 = self.tau1
        tau2 = self.tau2
        tau12 = max(tau1, tau2)

        if self.overlap_type == 0:  # coorperative model

            indices = [(k1, k2, k12) for k1 in xrange(tau1)
                       for k2 in xrange(tau2) for k12 in xrange(tau12)]

            prob_noD1 = poisson.cdf(tau1 - 1, rho1 + rho12)
            prob_noD2 = poisson.cdf(tau2 - 1, rho2 + rho12)

            for (k1, k2, k12) in indices:
                no_penetrance = k1 + k12 < tau1 and k2 + k12 < tau2

                if no_penetrance:
                    prob_noD1_and_noD2 += (poisson.pmf(k1, rho1) *
                                           poisson.pmf(k2, rho2) *
                                           poisson.pmf(k12, rho12))

        elif self.overlap_type == 1 and rho12 > 0.0:  # competition model
            raise NotImplementedError("competition model not implemented.")

        else:  # independent model, rho12 does not contribute!

            prob_noD1 = poisson.cdf(tau1 - 1, rho1)
            prob_noD2 = poisson.cdf(tau2 - 1, rho2)

            indices = [(k1, k2) for k1 in xrange(tau1) for k2 in xrange(tau2)]

            for (k1, k2) in indices:
                no_penetrance = k1 < tau1 and k2 < tau2

                if no_penetrance:
                    prob_noD1_and_noD2 += (poisson.pmf(k1, rho1) *
                                           poisson.pmf(k2, rho2))

        # Instantiate phi_infinity_probs
        phi_infty_probs = np.zeros(4, dtype=np.float)

        # prob(noD1 and noD2)
        phi_infty_probs[0] = prob_noD1_and_noD2

        # prob(yesD1 and noD2)
        phi_infty_probs[1] = prob_noD2 - prob_noD1_and_noD2

        # prob(noD1 and yesD2)
        phi_infty_probs[2] = prob_noD1 - prob_noD1_and_noD2

        # prob(yesD1 and yesD2)
        # use the fact that probability must add up to 1.
        phi_infty_probs[3] = 1.0 - np.sum(phi_infty_probs[0:3])

        return phi_infty_probs