docs: @Override
      public void postStep(INDArray gradient) {
          INDArray previousParameters = (INDArray) searchState.get("oldparams");
          INDArray parameters = model.params();
          INDArray previousGradient = (INDArray) searchState.get(GRADIENT_KEY);
  
          LinkedList<Double> rho = (LinkedList<Double>) searchState.get("rho");
          LinkedList<INDArray> s = (LinkedList<INDArray>) searchState.get("s");
          LinkedList<INDArray> y = (LinkedList<INDArray>) searchState.get("y");
  
          double sy = Nd4j.getBlasWrapper().dot(previousParameters, previousGradient) + Nd4j.EPS_THRESHOLD;
          double yy = Nd4j.getBlasWrapper().dot(previousGradient, previousGradient) + Nd4j.EPS_THRESHOLD;
  
          INDArray sCurrent;
          INDArray yCurrent;
          if (s.size() >= m) {
              //Optimization: Remove old (no longer needed) INDArrays, and use assign for re-use.
              //Better to do this: fewer objects created -> less memory overall + less garbage collection
              sCurrent = s.removeLast();
              yCurrent = y.removeLast();
              rho.removeLast();
              sCurrent.assign(parameters).subi(previousParameters);
              yCurrent.assign(gradient).subi(previousGradient);
          } else {
              //First few iterations. Need to allocate new INDArrays for storage (via copy operation sub)
              sCurrent = parameters.sub(previousParameters);
              yCurrent = gradient.sub(previousGradient);
          }
  
          rho.addFirst(1.0 / sy); //Most recent first
          s.addFirst(sCurrent); //Most recent first. si = currParams - oldParams
          y.addFirst(yCurrent); //Most recent first. yi = currGradient - oldGradient
  
          //assert (s.size()==y.size()) : "Gradient and parameter sizes are not equal";
          if (s.size() != y.size())
              throw new IllegalStateException("Gradient and parameter sizes are not equal");
  
          //In general: have m elements in s,y,rho.
          //But for first few iterations, have less.
          int numVectors = Math.min(m, s.size());
  
          double[] alpha = new double[numVectors];
  
          // First work backwards, from the most recent difference vectors
          Iterator<INDArray> sIter = s.iterator();
          Iterator<INDArray> yIter = y.iterator();
          Iterator<Double> rhoIter = rho.iterator();
  
          //searchDir: first used as equivalent to q as per N&W, then later used as r as per N&W.
          //Re-using existing array for performance reasons
          INDArray searchDir = (INDArray) searchState.get(SEARCH_DIR);
          searchDir.assign(gradient);
  
          for (int i = 0; i < numVectors; i++) {
              INDArray si = sIter.next();
              INDArray yi = yIter.next();
              double rhoi = rhoIter.next();
  
              if (si.length() != searchDir.length())
                  throw new IllegalStateException("Gradients and parameters length not equal");
  
              alpha[i] = rhoi * Nd4j.getBlasWrapper().dot(si, searchDir);
              Nd4j.getBlasWrapper().level1().axpy(searchDir.length(), -alpha[i], yi, searchDir); //q = q-alpha[i]*yi
          }
  
          //Use Hessian approximation initialization scheme
          //searchDir = H0*q = (gamma*IdentityMatrix)*q = gamma*q
          double gamma = sy / yy;
          searchDir.muli(gamma);
  
          //Reverse iterators: end to start. Java LinkedLists are doubly-linked,
          // so still O(1) for reverse iteration operations.
          sIter = s.descendingIterator();
          yIter = y.descendingIterator();
          rhoIter = rho.descendingIterator();
          for (int i = 0; i < numVectors; i++) {
              INDArray si = sIter.next();
              INDArray yi = yIter.next();
              double rhoi = rhoIter.next();
  
              double beta = rhoi * Nd4j.getBlasWrapper().dot(yi, searchDir); //beta = rho_i * y_i^T * r
              //r = r + s_i * (alpha_i - beta)
              Nd4j.getBlasWrapper().level1().axpy(gradient.length(), alpha[i] - beta, si, searchDir);
          }
  
          previousParameters.assign(parameters);
          previousGradient.assign(gradient); //Update gradient. Still in searchState map keyed by GRADIENT_KEY
      }