docs: public void init(INDArray parameters, boolean cloneParametersArray) {
          if (layerWiseConfigurations == null || layers == null)
              intializeConfigurations();
          if (initCalled)
              return;
  
          DataType netDtype = getLayerWiseConfigurations().getDataType();
          if(parameters != null && parameters.dataType() != netDtype){
              if(cloneParametersArray){
                  try(MemoryWorkspace ws = Nd4j.getWorkspaceManager().scopeOutOfWorkspaces()) {
                      parameters = parameters.castTo(netDtype);
                  }
              } else {
                  throw new IllegalStateException("Error initializing network: Network datatype is set to " + netDtype
                          + " but provided array has datatype " + parameters.dataType() + " with cloneParametersArray argument" +
                          " set to false. Cannot initialize net with specified datatype array if that array does not match network datatype");
              }
          }
  
          if (layerMap == null)
              layerMap = new LinkedHashMap<>();
  
          if (layerWiseConfigurations.getTrainingWorkspaceMode() == null)
              layerWiseConfigurations.setTrainingWorkspaceMode(WorkspaceMode.NONE);
  
          if (layerWiseConfigurations.getInferenceWorkspaceMode() == null)
              layerWiseConfigurations.setInferenceWorkspaceMode(WorkspaceMode.NONE);
  
          if (layerWiseConfigurations.getCacheMode() == null)
              layerWiseConfigurations.setCacheMode(CacheMode.NONE);
  
          OneTimeLogger.info(log, "Starting MultiLayerNetwork with WorkspaceModes set to [training: {}; inference: {}], cacheMode set to [{}]",
                  layerWiseConfigurations.getTrainingWorkspaceMode(),
                  layerWiseConfigurations.getInferenceWorkspaceMode(),
                  layerWiseConfigurations.getCacheMode());
  
          int nLayers = getnLayers();
  
          if (nLayers < 1)
              throw new IllegalStateException("Unable to create network: number of layers is less than 1");
  
          if (this.layers == null || this.layers[0] == null) {
              if (this.layers == null)
                  this.layers = new Layer[nLayers];
  
              //First: Work out total length of params
              long paramLength = 0;
              val nParamsPerLayer = new long[nLayers];
              for (int i = 0; i < nLayers; i++) {
                  NeuralNetConfiguration conf = layerWiseConfigurations.getConf(i);
                  nParamsPerLayer[i] = conf.getLayer().initializer().numParams(conf);
                  paramLength += nParamsPerLayer[i];
              }
  
              //Create parameters array, if required
              boolean initializeParams;
              if (parameters != null) {
                  if (!parameters.isRowVectorOrScalar())
                      throw new IllegalArgumentException("Invalid parameters: should be a row vector");
                  if (parameters.length() != paramLength)
                      throw new IllegalArgumentException("Invalid parameters: expected length " + paramLength
                              + ", got length " + parameters.length());
  
                  if (cloneParametersArray)
                      flattenedParams = parameters.dup();
                  else
                      flattenedParams = parameters;
  
                  initializeParams = false;
              } else if(paramLength > 0){
                  flattenedParams = Nd4j.create(netDtype, 1, paramLength);
                  initializeParams = true;
              } else {
                  //Edge case: 0 params in network
                  flattenedParams = null;
                  initializeParams = false;
              }
  
              //Set RNG seed, for repeatability between initializations when set
              if (initializeParams) {
                  Nd4j.getRandom().setSeed(getDefaultConfiguration().getSeed());
              }
  
              // construct multi-layer
              long paramCountSoFar = 0;
              for (int i = 0; i < nLayers; i++) {
                  INDArray paramsView;
                  if (nParamsPerLayer[i] > 0) {
                      paramsView = flattenedParams.get(NDArrayIndex.interval(0,0,true),
                              NDArrayIndex.interval(paramCountSoFar, paramCountSoFar + nParamsPerLayer[i]));
                  } else {
                      paramsView = null;
                  }
                  paramCountSoFar += nParamsPerLayer[i];
  
                  NeuralNetConfiguration conf = layerWiseConfigurations.getConf(i);
                  layers[i] = conf.getLayer().instantiate(conf, trainingListeners, i, paramsView, initializeParams, netDtype);
                  layerMap.put(conf.getLayer().getLayerName(), layers[i]);
              }
              initCalled = true;
          }
  
          //Set parameters in MultiLayerNetwork.defaultConfiguration for later use in BaseOptimizer.setupSearchState() etc
          defaultConfiguration.clearVariables();
          List<String> variables = defaultConfiguration.variables(false);
          for (int i = 0; i < layers.length; i++) {
              if(layers[i] == null){
                  throw new IllegalStateException("Encountered null layer during initialization for layer " + i +
                          ": " + layerWiseConfigurations.getConf(i).getLayer().getClass().getSimpleName() + " initialization " +
                          "returned null layer?");
              }
  
              for (String s : layers[i].conf().variables()) {
                  variables.add(i + "_" + s);
              }
          }
  
          // now we init solver & optimizer
          if (solver == null) {
              try (MemoryWorkspace wsO = Nd4j.getMemoryManager().scopeOutOfWorkspaces()) {
                  solver = new Solver.Builder().configure(conf()).listeners(getListeners()).model(this).build();
                  solver.initOptimizer();
              }
          }
  
          //Mark that input modification is allowed.
          //TODO When is it safe to NOT skip the very first layer? It's not always safe...
          // For example dropout + iterating over List<DataSet> that is used for multiple epochs...
          for( int i=1; i<layers.length; i++ ){
              layers[i].allowInputModification(true);
          }
  
          synchronizeIterEpochCounts();
      }