<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def _estimate_GCV_UBRE(self, X=None, y=None, modelmat=None, gamma=1.4, add_scale=True, weights=None):
 """ Generalized Cross Validation and Un-Biased Risk Estimator. UBRE is used when the scale parameter is known, like Poisson and Binomial families. Parameters y : array-like of shape (n_samples,) output data vector modelmat : array-like, default: None contains the spline basis for each feature evaluated at the input gamma : float, default: 1.4 serves as a weighting to increase the impact of the influence matrix on the score add_scale : boolean, default: True UBRE score can be negative because the distribution scale is subtracted. to keep things positive we can add the scale back. weights : array-like shape (n_samples,) or None, default: None containing sample weights if None, defaults to array of ones Returns ------- score : float Either GCV or UBRE, depending on if the scale parameter is known. Notes ----- Sometimes the GCV or UBRE selected model is deemed to be too wiggly, and a smoother model is desired. One way to achieve this, in a systematic way, is to increase the amount that each model effective degree of freedom counts, in the GCV or UBRE score, by a factor γ ≥ 1 see Wood 2006 pg. 177-182, 220 for more details. """

        if gamma < 1:
            raise ValueError('gamma scaling should be greater than 1, '\
                             'but found gamma = {}',format(gamma))

        if modelmat is None:
            modelmat = self._modelmat(X)

        if weights is None:
            weights = np.ones_like(y).astype('float64')

        lp = self._linear_predictor(modelmat=modelmat)
        mu = self.link.mu(lp, self.distribution)
        n = y.shape[0]
        edof = self.statistics_['edof']

        GCV = None
        UBRE = None

        dev = self.distribution.deviance(mu=mu, y=y, scaled=False, weights=weights).sum()

        if self.distribution._known_scale:
            # scale is known, use UBRE
            scale = self.distribution.scale
            UBRE = 1./n *  dev - (~add_scale)*(scale) + 2.*gamma/n * edof * scale
        else:
            # scale unkown, use GCV
            GCV = (n * dev) / (n - gamma * edof)**2
        return (GCV, UBRE)