class_docstr: Class for NOTEARS MLP (Multi-layer Perceptron) model.
  The model weights consist of dag_layer and loc_lin_layer weights respectively.
  dag_layer weight is the weight of the first fully connected layer which determines the causal structure.
  loc_lin_layer weights are the weight of hidden layers after the first fully connected layer
class_docstr_tok: ["Class","for","NOTEARS","MLP","(","Multi-layer","Perceptron",")","model",".","The","model","weights","consist","of","dag_layer","and","loc_lin_layer","weights","respectively",".","dag_layer","weight","is","the","weight","of","the","first","fully","connected","layer","which","determines","the","causal","structure",".","loc_lin_layer","weights","are","the","weight","of","hidden","layers","after","the","first","fully","connected","layer"]
docstr: Calculate the adjacency matrix.
  
  For the linear case, this is just dag_layer_weight.
  For the nonlinear case, approximate the relationship using the gradient of X_hat wrt X.
docstr_tok: ["Calculate","the","adjacency","matrix",".","For","the","linear","case",",","this","is","just","dag_layer_weight",".","For","the","nonlinear","case",",","approximate","the","relationship","using","the","gradient","of","X_hat","wrt","X","."]
code: def _calculate_adj(
      self, X: torch.Tensor, mean_effect: bool
  ) -> torch.Tensor:
      """
      Calculate the adjacency matrix.
  
      For the linear case, this is just dag_layer_weight.
      For the nonlinear case, approximate the relationship using the gradient of X_hat wrt X.
      """
      # for the linear case, save compute by just returning the dag_layer weights
      if len(self.dims) <= 2:
          adj = (
              self.dag_layer_weight.T
              if mean_effect
              else torch.abs(self.dag_layer_weight.T)
          )
          return adj
  
      _, n_features = X.shape
  
      # get the data X and reconstruction X_hat
      X = X.clone().requires_grad_()
      X_hat = self(X).sum(dim=0)  # shape = (n_features,)
  
      # iterate over sums of reconstructed features
      adj = []
  
      for j in range(n_features):
          # calculate the gradient of X_hat wrt X
          ddx = torch.autograd.grad(X_hat[j], X, create_graph=True)[0]
  
          if mean_effect:
              # get the average effect
              adj.append(ddx.mean(axis=0).unsqueeze(0))
          else:
              # otherwise, use the average L1 of the gradient as the W
              adj.append(torch.abs(ddx).mean(dim=0).unsqueeze(0))
  
      adj = torch.cat(adj, dim=0)
  
      # transpose to get the adjacency matrix
      return adj.T
  
imports: import logging
  from typing import Iterable, List, Tuple, Union
  import numpy
  import scipy.optimize
  import torch
  from sklearn.base import BaseEstimator
  from torch import nn
  from causalnex.structure.pytorch.dist_type._base import DistTypeBase
  from causalnex.structure.pytorch.nonlinear import LocallyConnected
label_desc: Use image node_id 14 for calling the NotearsMLP obj's underlying member method code with example usage: obj._calculate_adj(X, mean_effect) and returns: adj, adj