input: 
  import numpy as np
  class LinearRegressionModel:
      def __init__(self, input_dim: int, output_dim: int):
          """ Initialises the weights and bias of the linear regression model
          Arguments:
              input_dim {int} -- Number of input features
              output_dim {int} -- Number of outputs
          >>> model = LinearRegressionModel(2, 1)
          >>> model.weights
          array([[0.],
                 [0.]])
          >>> model.bias
          array([[0.]])
          """
          self.weights = np.zeros((input_dim, output_dim))
          self.bias = np.zeros((1, output_dim))
  
      def forward(self, x: np.ndarray) -> np.ndarray:
          """ Calculates the forward pass of the linear regression model
          Arguments:
              x {numpy array} -- Input data of size (N, input_dim)
          Returns:
              out {numpy array} -- Output of the linear regression model of size (N, output_dim)
          >>> model = LinearRegressionModel(2, 1)
          >>> model.weights = np.array([[1.], [2.]])
          >>> model.bias = np.array([[3.]])
          >>> model.forward(np.array([[1, 2], [2, 3]]))
          array([[ 8.],
                 [13.]])
          """
          out = np.dot(x, self.weights) + self.bias
          return out
  
output:     
      def fit(self, x: np.ndarray, y: np.ndarray, learning_rate: float = 0.01, epochs: int = 1000):
          """ Trains the linear regression model using gradient descent
          Arguments:
              x {numpy array} -- Input data of size (N, input_dim)
              y {numpy array} -- Target data of size (N, output_dim)
              learning_rate {float} -- Learning rate for gradient descent (default: 0.01)
              epochs {int} -- Number of epochs to train (default: 1000)
          >>> model = LinearRegressionModel(2, 1)
          >>> x_train = np.array([[1, 2], [2, 3], [3, 4]])
          >>> y_train = np.array([[3], [5], [7]])
          >>> model.fit(x_train, y_train, learning_rate=0.01, epochs=1000)
          >>> np.allclose(model.weights, np.array([[1.], [1.]]), atol=0.1)
          True
          >>> np.allclose(model.bias, np.array([[1.]]), atol=0.1)
          True
          """
          for _ in range(epochs):
              y_pred = self.forward(x)
              error = y_pred - y
              gradient_weights = np.dot(x.T, error) / len(x)
              gradient_bias = np.mean(error, axis=0, keepdims=True)
              self.weights -= learning_rate * gradient_weights
              self.bias -= learning_rate * gradient_bias
  