docs: public String summary() {
  
          Map<String, SDVariable> varMap = variableMap();
          DifferentialFunction[] functions = functions();
  
  
          int countVarsWithArrays = 0;
          for (String s : varMap.keySet()) {
              if (getArrForVarName(s) != null) {
                  countVarsWithArrays++;
              }
          }
  
          StringBuilder sb = new StringBuilder();
          String format = "%-25s%-20s";
          sb.append("--- Summary ---\n");
          sb.append(String.format(format, "Variables:", varMap.size())).append(" (").append(countVarsWithArrays).append(" with arrays)").append("\n")
                  .append(String.format(format, "Functions:", functions.length)).append("\n")
                  .append(String.format(format, "SameDiff Function Defs:", sameDiffFunctionInstances.size())).append("\n")
                  .append("Loss function variables: ").append(getLossVariables())
                  .append("\n\n");
  
          sb.append("--- Variables ---\n");
          //Work out which function - if any - this arg is an output of...
          Map<String, String> outputOfFn = new HashMap<>();
          int maxLengthOutputOf = 22;     //Length of "- Output Of Function -"
          int maxLengthOfName = 8;       //Length of "- Name -"
          for (String s : varMap.keySet()) {
              String outputOf = null;
              for(SameDiffOp op : ops.values()){
                  List<String> outputsOfOp = op.getOutputsOfOp();
                  if (outputsOfOp != null && outputsOfOp.contains(s)) {
                      outputOf = op.getName();
                      break;
                  }
              }
  
              if (outputOf == null) {
                  outputOf = "<none>";
              } else {
                  DifferentialFunction d = getFunctionById(outputOf);
                  outputOf = d.getOwnName() + "(" + d.opName() + ")";
              }
              outputOfFn.put(s, outputOf);
              maxLengthOutputOf = Math.max(maxLengthOutputOf, outputOf.length());
              maxLengthOfName = Math.max(maxLengthOfName, s.length());
          }
          maxLengthOutputOf += 2;
          maxLengthOfName += 2;
  
          //Create the output for values:
          format = "%-" + maxLengthOfName + "s%-20s%-20s%-20s%-" + maxLengthOutputOf + "s%-20s";
          sb.append(String.format(format, "- Name -", "- Array Shape -", "- Variable Type -", "- Data Type-", "- Output Of Function -", "- Inputs To Functions -")).append("\n");
          for (String s : varMap.keySet()) {
              INDArray arr = getArrForVarName(s);
              String arrayShape = "-";
              if (arr != null) {
                  arrayShape = Arrays.toString(arr.shape());
              }
              String varType = getVariable(s).getVariableType().toString();
              String dtype = getVariable(s).dataType().toString();
  
              List<String> argNames = variables.get(s).getInputsForOp();
              String dfArrStr = "";
              if (argNames != null) {
                  dfArrStr = argNames.toString();
              }
  
              String outputOfStr = outputOfFn.get(s);
  
              sb.append(String.format(format, s, arrayShape, varType, dtype, outputOfStr, dfArrStr)).append("\n");
          }
  
          sb.append("\n\n--- Functions ---\n");
  
          //First: work out the amount of space we need for inputs and outputs...
          List<String> dfInputStr = new ArrayList<>();
          List<String> dfOutputStr = new ArrayList<>();
          int maxInLength = 10;       //Length of "- Inputs -"
          int maxOutLength = 11;      //Length of "- Outputs -"
          int maxOpNameLength = 17;   //Default to min of 17 - length of "- Function Name -"
          int maxDfClassNameLength = 10;  //Default to min of 10
          for (DifferentialFunction df : functions) {
              String[] argNames = df.argNames();
              String[] outNames = df.outputVariablesNames();
  
              String argStr = Arrays.toString(argNames);
              String outStr = Arrays.toString(outNames);
  
              maxInLength = Math.max(maxInLength, argStr.length());
              maxOutLength = Math.max(maxOutLength, outStr.length());
  
              dfInputStr.add(argStr);
              dfOutputStr.add(outStr);
  
              String name = df.getOwnName() == null ? df.opName() : df.getOwnName();
              maxOpNameLength = Math.max(maxOpNameLength, name.length());
              maxDfClassNameLength = Math.max(maxDfClassNameLength, df.getClass().getSimpleName().length());
          }
          //Extra padding space
          maxInLength += 2;
          maxOutLength += 2;
          maxOpNameLength += 2;
          maxDfClassNameLength += 2;
  
  
          format = "%-5s%-" + maxOpNameLength + "s%-" + maxDfClassNameLength + "s%-" + maxInLength + "s%-" + maxOutLength + "s";
          sb.append(String.format(format, "", "- Function Name -", "- Op -", "- Inputs -", "- Outputs -")).append("\n");
          for (int i = 0; i < functions.length; i++) {
              DifferentialFunction df = functions[i];
              String fnName = df.getOwnName() == null ? df.opName() : df.getOwnName();
  
              sb.append(String.format(format, String.valueOf(i), fnName, df.getClass().getSimpleName(), dfInputStr.get(i), dfOutputStr.get(i))).append("\n");
          }
  
          if (sameDiffFunctionInstances.size() > 0) {
              sb.append("\n\n--- SameDiff Defined Functions ---\n");
              format = "%-20s%-15s%-15s%-15s";
              sb.append(String.format(format, "- Name -", "- Variables -", "- Functions -", "- Fn Defs -")).append("\n");
              for (Map.Entry<String, SameDiff> e : sameDiffFunctionInstances.entrySet()) {
                  SameDiff sd = e.getValue();
                  int vars = sd.variableMap().size();
                  int fns = (sd.functions() == null ? 0 : sd.functions().length);
                  int defFns = sd.definedFunctionNames().size();
  
                  sb.append(String.format(format, e.getKey(), String.valueOf(vars), String.valueOf(fns), String.valueOf(defFns))).append("\n");
              }
          }
  
          return sb.toString();
      }