def forward(self, X, mode='predict') -> np.ndarray:
    (n_in, c, h_in, w_in) = X.shape
    (n_f, c, h_f, w_f) = self.W.shape
    self.x_cols = im2col_cython(X, h_f, w_f, self.padding, self.stride)
    z = self.W.reshape((n_f, (- 1))).dot(self.x_cols)
    z += self.b.reshape((- 1), 1)
    z = z.reshape(n_f, self.h_out, self.w_out, n_in).transpose(3, 0, 1, 2)
    self.a_in = X
    if (self.activation is None):
        self.a_out = z
    else:
        self.a_out = self.activation.forward(z)
    if ((mode == 'train') and (self.dropout_rate > 0)):
        self.dropout_mask = (np.random.rand(*self.a_out.shape) > self.dropout_rate).astype(int)
        self.a_out *= self.dropout_mask
    return self.a_out
def forward(self, X, mode='predict') -> np.ndarray:
    (n_in, c, h_in, w_in) = X.shape
    (n_f, c, h_f, w_f) = self.W.shape
    self.x_cols = im2col_cython(X, h_f, w_f, self.padding, self.stride)
    z = self.W.reshape((n_f, (- 1))).dot(self.x_cols)
    z += self.b.reshape((- 1), 1)
    z = z.reshape(n_f, self.h_out, self.w_out, n_in).transpose(3, 0, 1, 2)
    self.a_in = X
    if (self.activation is None):
        self.a_out = z
    else:
        self.a_out = self.activation.forward(z)
    if ((mode == 'train') and (self.dropout_rate > 0)):
        self.dropout_mask = (np.random.rand(*self.a_out.shape) > self.dropout_rate).astype(int)
        self.a_out *= self.dropout_mask
    return self.a_out