<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def update_grads(self, X, dL_dW):
 """Update the gradients of marginal log likelihood with respect to the parameters of warping function Parameters X : array_like, shape = (n_samples, n_features) The input BEFORE warping dL_dW : array_like, shape = (n_samples, n_features) The gradient of marginal log likelihood with respect to the Warped input Math ---- let w = f(x), the input after warping, then dW_da = b * (1 - x^a)^(b - 1) * x^a * ln(x) dW_db = - (1 - x^a)^b * ln(1 - x^a) dL_da = dL_dW * dW_da dL_db = dL_dW * dW_db """

        for i_seq, i_fea in enumerate(self.warping_indices):
            ai, bi = self.params[i_seq][0], self.params[i_seq][1]

            # cache some value for save some computation
            x_pow_a = np.power(self.X_normalized[:, i_fea], ai)

            # compute gradient for ai, bi on all X
            dz_dai = bi * np.power(1 - x_pow_a, bi-1) * x_pow_a * np.log(self.X_normalized[:, i_fea])
            dz_dbi = - np.power(1 - x_pow_a, bi) * np.log(1 - x_pow_a)

            # sum gradients on all the data
            dL_dai = np.sum(dL_dW[:, i_fea] * dz_dai)
            dL_dbi = np.sum(dL_dW[:, i_fea] * dz_dbi)
            self.params[i_seq][0].gradient[:] = dL_dai
            self.params[i_seq][1].gradient[:] = dL_dbi