docs: public void setDerivatives(double[] parameters, double[][] derivatives) throws SolverException {
  		// Calculate new derivatives. Note that this method is called only with
  		// parameters = parameterCurrent, so we may use valueCurrent.
  
  		Vector<Future<double[]>> valueFutures = new Vector<Future<double[]>>(parameterCurrent.length);
  		for (int parameterIndex = 0; parameterIndex < parameterCurrent.length; parameterIndex++) {
  			final double[] parametersNew	= parameters.clone();
  			final double[] derivative		= derivatives[parameterIndex];
  
  			final int workerParameterIndex = parameterIndex;
  			Callable<double[]> worker = new  Callable<double[]>() {
  				public double[] call() {
  					double parameterFiniteDifference;
  					if(parameterSteps != null) {
  						parameterFiniteDifference = parameterSteps[workerParameterIndex];
  					}
  					else {
  						/*
  						 * Try to adaptively set a parameter shift. Note that in some
  						 * applications it may be important to set parameterSteps.
  						 * appropriately.
  						 */
  						parameterFiniteDifference = (Math.abs(parametersNew[workerParameterIndex]) + 1) * 1E-8;
  					}
  
  					// Shift parameter value
  					parametersNew[workerParameterIndex] += parameterFiniteDifference;
  
  					// Calculate derivative as (valueUpShift - valueCurrent) / parameterFiniteDifference
  					try {
  						setValues(parametersNew, derivative);
  					} catch (Exception e) {
  						// We signal an exception to calculate the derivative as NaN
  						Arrays.fill(derivative, Double.NaN);
  					}
  					for (int valueIndex = 0; valueIndex < valueCurrent.length; valueIndex++) {
  						derivative[valueIndex] -= valueCurrent[valueIndex];
  						derivative[valueIndex] /= parameterFiniteDifference;
  						if(Double.isNaN(derivative[valueIndex])) {
  							derivative[valueIndex] = 0.0;
  						}
  					}
  					return derivative;
  				}
  			};
  			if(executor != null) {
  				Future<double[]> valueFuture = executor.submit(worker);
  				valueFutures.add(parameterIndex, valueFuture);
  			}
  			else {
  				FutureTask<double[]> valueFutureTask = new FutureTask<double[]>(worker);
  				valueFutureTask.run();
  				valueFutures.add(parameterIndex, valueFutureTask);
  			}
  		}
  
  		for (int parameterIndex = 0; parameterIndex < parameterCurrent.length; parameterIndex++) {
  			try {
  				derivatives[parameterIndex] = valueFutures.get(parameterIndex).get();
  			}
  			catch (InterruptedException e) {
  				throw new SolverException(e);
  			} catch (ExecutionException e) {
  				throw new SolverException(e);
  			}
  		}
  	}