示例#1
0
  /**
   * Plot the results produced by the testing methods in this class.
   *
   * @param results The results of one or more tests. They must all have the same type {@link TYPE}.
   *     If more than one result is to be plotted then only {@link TYPE#STDP} or {@link
   *     TYPE#STDP_1D} are supported, and it is assumed that they all have the same pre- and
   *     post-synaptic spike patterns.
   * @param singlePlot If plotting more than one TestResult then whether to show the results on a
   *     single plot (true) or separate plots (false).
   * @param timeResolution The time resolution which the simulation was run at.
   * @param logSpikesAndStateVariables Whether to include pre- and post-synaptic spikes and any
   *     state variables exposed by the synapse model in the plot. This is ignored if more than one
   *     result is to be plotted.
   * @param showInFrame Whether to display the result in a frame.
   */
  public static JFreeChart createChart(
      TestResults[] results,
      boolean singlePlot,
      int timeResolution,
      boolean logSpikesAndStateVariables,
      boolean showInFrame,
      String title) {
    JFreeChart resultsPlot = null;
    int resultsCount = results.length;

    // Make sure they're all the same type.
    for (int ri = 0; ri < results.length; ri++) {
      if (ri < resultsCount - 1
          && results[ri].getProperty("type") != results[ri + 1].getProperty("type")) {
        throw new IllegalArgumentException("All results must have the same type.");
      }
      if (resultsCount > 1
          && results[ri].getProperty("type") != TYPE.STDP
          && results[ri].getProperty("type") != TYPE.STDP_1D) {
        throw new IllegalArgumentException(
            "Multiple results can only be plotted for types STDP or STDP_1D.");
      }
    }

    TYPE type = (TYPE) results[0].getProperty("type");

    XYLineAndShapeRenderer xyRenderer;
    XYToolTipGenerator tooltipGen = new StandardXYToolTipGenerator();

    if (type == TYPE.STDP) {
      CombinedDomainXYPlot combinedPlot = new CombinedDomainXYPlot(new NumberAxis("t (s)"));

      if (singlePlot) { // Plot all result sets together.
        DefaultXYDataset efficacyData = new DefaultXYDataset();
        for (TestResults result : results) {
          String efficacyLabel =
              (resultsCount == 1) ? "Efficacy" : "" + result.getProperty("label");
          efficacyData.addSeries(efficacyLabel, result.getResult("Time", "Efficacy"));
        }
        xyRenderer = new XYLineAndShapeRenderer(true, false);
        xyRenderer.setBaseToolTipGenerator(tooltipGen);
        combinedPlot.add(new XYPlot(efficacyData, null, new NumberAxis("Efficacy"), xyRenderer), 4);
      } else { // Plot each result set separately.
        for (TestResults result : results) {
          DefaultXYDataset efficacyData = new DefaultXYDataset();
          String efficacyLabel =
              (resultsCount == 1) ? "Efficacy" : "" + result.getProperty("label");
          efficacyData.addSeries(efficacyLabel, result.getResult("Time", "Efficacy"));
          xyRenderer = new XYLineAndShapeRenderer(true, false);
          xyRenderer.setBaseToolTipGenerator(tooltipGen);
          combinedPlot.add(
              new XYPlot(efficacyData, null, new NumberAxis("Efficacy"), xyRenderer), 4);
        }
      }

      // Don't plot trace data for multiple tests.
      if (resultsCount == 1 && logSpikesAndStateVariables) {
        DefaultXYDataset traceData = new DefaultXYDataset();
        for (String label : results[0].getResultLabels()) {
          if (!label.startsWith("Time")
              && !label.startsWith("Efficacy")
              && !label.equals("Pre-synaptic spikes")
              && !label.equals("Post-synaptic spikes")) {
            traceData.addSeries(label, results[0].getResult("Time", label));
          }
        }
        xyRenderer = new XYLineAndShapeRenderer(true, false);
        xyRenderer.setBaseToolTipGenerator(tooltipGen);
        combinedPlot.add(new XYPlot(traceData, null, new NumberAxis("State"), xyRenderer), 3);

        DefaultXYDataset spikeData = new DefaultXYDataset();
        spikeData.addSeries(
            "Pre-synaptic spikes", results[0].getResult("Time", "Pre-synaptic spikes"));
        spikeData.addSeries(
            "Post-synaptic spikes", results[0].getResult("Time", "Post-synaptic spikes"));
        xyRenderer = new XYLineAndShapeRenderer(true, false);
        xyRenderer.setBaseToolTipGenerator(tooltipGen);
        combinedPlot.add(
            new XYPlot(spikeData, null, new NumberAxis("Pre/post potential"), xyRenderer), 3);
      }

      resultsPlot = new JFreeChart(title, null, combinedPlot, true);
      resultsPlot.setBackgroundPaint(Color.WHITE);
      resultsPlot.getPlot().setBackgroundPaint(Color.WHITE);
      ((XYPlot) resultsPlot.getPlot()).setRangeGridlinePaint(Color.LIGHT_GRAY);
      ((XYPlot) resultsPlot.getPlot()).setDomainGridlinePaint(Color.LIGHT_GRAY);

    } else if (type == TYPE.STDP_1D) {
      DecimalFormat timeFormatter = new DecimalFormat();
      timeFormatter.setMultiplier(1000);
      NumberAxis domainAxis = new NumberAxis("\u0394t (ms)");
      domainAxis.setNumberFormatOverride(timeFormatter);
      CombinedDomainXYPlot combinedPlot = new CombinedDomainXYPlot(domainAxis);

      if (singlePlot) { // Plot all result sets together.
        DefaultXYDataset efficacyData = new DefaultXYDataset();
        for (TestResults result : results) {
          String efficacyLabel =
              (resultsCount == 1) ? "Efficacy" : "" + result.getProperty("label");
          efficacyData.addSeries(efficacyLabel, result.getResult("Time delta", "Efficacy"));
        }
        xyRenderer = new XYLineAndShapeRenderer(true, false);
        xyRenderer.setBaseToolTipGenerator(tooltipGen);
        combinedPlot.add(new XYPlot(efficacyData, null, new NumberAxis("Efficacy"), xyRenderer), 4);
      } else { // Plot each result set separately.
        for (TestResults result : results) {
          DefaultXYDataset efficacyData = new DefaultXYDataset();
          String efficacyLabel =
              (resultsCount == 1) ? "Efficacy" : "" + result.getProperty("label");
          efficacyData.addSeries(efficacyLabel, result.getResult("Time delta", "Efficacy"));
          xyRenderer = new XYLineAndShapeRenderer(true, false);
          xyRenderer.setBaseToolTipGenerator(tooltipGen);
          combinedPlot.add(
              new XYPlot(efficacyData, null, new NumberAxis("Efficacy"), xyRenderer), 4);
        }
      }

      resultsPlot = new JFreeChart(title, null, combinedPlot, true);
      resultsPlot.setBackgroundPaint(Color.WHITE);
      resultsPlot.getPlot().setBackgroundPaint(Color.WHITE);
      ((XYPlot) resultsPlot.getPlot()).setRangeGridlinePaint(Color.LIGHT_GRAY);
      ((XYPlot) resultsPlot.getPlot()).setDomainGridlinePaint(Color.LIGHT_GRAY);
    } else if (type == TYPE.STDP_2D) {
      double[][] data = results[0].getResult("Time delta 1", "Time delta 2", "Efficacy");

      DefaultXYZDataset plotData = new DefaultXYZDataset();
      plotData.addSeries("Efficacy", data);

      // Set up paint scale, and convert domain axes from seconds to
      // milliseconds (XYBlockRenderer won't deal with fractional values
      // in the domain axes)
      double min = Double.MAX_VALUE, max = -min;
      double[] efficacy = data[2];
      for (int i = 0; i < data[0].length; i++) {
        if (efficacy[i] < min) min = efficacy[i];
        if (efficacy[i] > max) max = efficacy[i];

        data[0][i] = Math.round(data[0][i] * 1000);
        data[1][i] = Math.round(data[1][i] * 1000);
      }

      XYBlockRenderer renderer = new XYBlockRenderer();

      double range = Math.max(Math.abs(min), Math.abs(max));
      double rangeBase = 0;
      if (min < 0) min = -range;
      if (max > 0) max = range;
      // If the value range does not cross the zero point, don't use a zero-based range.
      if ((min > 0) || (max < 0)) {
        range = Math.abs(max - min);
        rangeBase = Math.min(Math.abs(min), Math.abs(max));
      }
      if (min >= max) {
        max = min + Double.MIN_VALUE * 10;
      }

      LookupPaintScale scale = new LookupPaintScale(min, max, Color.WHITE);
      if (min < 0) {
        for (int ci = 0; ci <= 255; ci++) {
          double v = -(ci / 255.0) * range - rangeBase;
          scale.add(v, new Color(0, ci, ci));
        }
      }
      if (max > 0) {
        for (int ci = 0; ci <= 255; ci++) {
          double v = (ci / 255.0) * range + rangeBase;
          scale.add(v, new Color(ci, ci, 0));
        }
      }
      renderer.setPaintScale(scale);
      renderer.setSeriesToolTipGenerator(0, new StandardXYZToolTipGenerator());
      int displayResolution =
          ((Integer) results[0].getProperty("display time resolution")).intValue();
      renderer.setBlockWidth(1000.0 / displayResolution);
      renderer.setBlockHeight(1000.0 / displayResolution);

      NumberAxis xAxis = new NumberAxis("\u0394t1 (ms)");
      NumberAxis yAxis = new NumberAxis("\u0394t2 (ms)");

      XYPlot plot = new XYPlot(plotData, xAxis, yAxis, renderer);
      plot.setDomainGridlinesVisible(false);

      resultsPlot = new JFreeChart(title, plot);
      resultsPlot.removeLegend();
      NumberAxis valueAxis = new NumberAxis();
      valueAxis.setLowerBound(scale.getLowerBound());
      valueAxis.setUpperBound(scale.getUpperBound());
      PaintScaleLegend legend = new PaintScaleLegend(scale, valueAxis);
      legend.setPosition(RectangleEdge.RIGHT);
      legend.setMargin(5, 5, 5, 5);
      resultsPlot.addSubtitle(legend);
    }

    if (showInFrame) {
      JFrame plotFrame = new JFrame(title);
      plotFrame.add(new ChartPanel(resultsPlot));
      plotFrame.setExtendedState(plotFrame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
      plotFrame.pack();
      plotFrame.setVisible(true);
    }

    return resultsPlot;
  }
示例#2
0
  /**
   * Test the behaviour of a synapse model.
   *
   * @param network A neural network containing two neurons and a single synapse to be tested. The
   *     neurons at index 0 and 1 should be the pre- and post-synaptic neurons respectively, and are
   *     typically configured to produce a fixed firing pattern (though any neuron type and firing
   *     pattern are permitted).
   * @param simSteps The number of steps to run the simulation for.
   * @param logSpikesAndStateVariables Whether to record pre- and post-synaptic spikes and any state
   *     variables exposed by the synapse model in the test results.
   * @param simStepsNoSpikes The number of steps to run the simulation for with no spiking after the
   *     normal spike test. This is useful for testing models which change the efficacy in the
   *     absence of spikes.
   * @return For a single spike protocol, a TestResults object with type {@link TYPE#STDP}
   *     consisting of series labelled "Time" and "Efficacy" and if logSpikesAndStateVariables ==
   *     true then also "Pre-synaptic spikes", "Post-synaptic spikes" and any state variables
   *     exposed by the synapse model.
   */
  public static TestResults singleTest(
      NeuralNetwork network,
      long simSteps,
      boolean logSpikesAndStateVariables,
      long simStepsNoSpikes) {
    if (network.getNeurons().getSize() != 2 || network.getSynapses().getSize() != 1) {
      throw new IllegalArgumentException(
          "The neural network must contain at least 2 neurons and 1 synapse.");
    }

    simStepsNoSpikes = Math.max(0, simStepsNoSpikes);

    int displayTimeResolution = Math.min(1000, network.getTimeResolution());

    int logStepCount = network.getTimeResolution() / displayTimeResolution;
    int logSize = (int) ((simSteps + simStepsNoSpikes) / logStepCount);

    double[] timeLog = new double[logSize];
    double[] efficacyLog = new double[logSize];
    double[][] prePostLogs = null, traceLogs = null;
    double[] stateVars;
    if (logSpikesAndStateVariables) {
      prePostLogs = new double[2][logSize];
      traceLogs = new double[network.getSynapses().getStateVariableNames().length][logSize];
    }

    int logIndex = 0;
    long step = 0;
    for (; step < simSteps; step++) {
      double time = network.getTime();
      network.step();

      if (step % logStepCount == 0) {
        timeLog[logIndex] = time;
        efficacyLog[logIndex] = network.getSynapses().getEfficacy(0);
        // If we're only testing a few repetitions, include some extra
        // data.
        if (logSpikesAndStateVariables) {
          prePostLogs[0][logIndex] = network.getNeurons().getOutput(0);
          prePostLogs[1][logIndex] = network.getNeurons().getOutput(1);
          stateVars = network.getSynapses().getStateVariableValues(0);
          for (int v = 0; v < stateVars.length; v++) {
            traceLogs[v][logIndex] = stateVars[v];
          }
        }
        logIndex++;
      }
    }

    if (simStepsNoSpikes > 0) {
      FixedProtocolNeuronConfiguration config =
          new FixedProtocolNeuronConfiguration(100, new double[] {});
      network.getNeurons().setConfiguration(0, config);
      network.getNeurons().setComponentConfiguration(0, 0);
      network.getNeurons().setComponentConfiguration(1, 0);

      for (; step < simSteps + simStepsNoSpikes; step++) {
        double time = network.getTime();
        network.step();

        if (step % logStepCount == 0) {
          timeLog[logIndex] = time;
          efficacyLog[logIndex] = network.getSynapses().getEfficacy(0);
          // If we're only testing a few repetitions, include some extra data.
          if (logSpikesAndStateVariables) {
            prePostLogs[0][logIndex] = network.getNeurons().getOutput(0);
            prePostLogs[1][logIndex] = network.getNeurons().getOutput(1);
            stateVars = network.getSynapses().getStateVariableValues(0);
            for (int v = 0; v < stateVars.length; v++) {
              traceLogs[v][logIndex] = stateVars[v];
            }
          }
          logIndex++;
        }
      }
    }

    TestResults results = new TestResults();
    results.setProperty("type", TYPE.STDP);
    results.addResult("Efficacy", efficacyLog);
    results.addResult("Time", timeLog);
    if (logSpikesAndStateVariables) {
      results.addResult("Pre-synaptic spikes", prePostLogs[0]);
      results.addResult("Post-synaptic spikes", prePostLogs[1]);
      String[] stateVariableNames = network.getSynapses().getStateVariableNames();
      for (int v = 0; v < stateVariableNames.length; v++) {
        results.addResult(stateVariableNames[v], traceLogs[v]);
      }
    }

    return results;
  }
示例#3
0
  /**
   * Test a synapse on the specified spiking protocol or a series of spiking protocols derived from
   * initial and final protocols by interpolation over one or two dimensions.
   *
   * @param synapse The SynapseCollection containing the synapse to test (the first synapse is
   *     used).
   * @param timeResolution The time resolution to use in the simulation, see {@link
   *     com.ojcoleman.bain.NeuralNetwork}
   * @param period The period of the spike pattern in seconds.
   * @param repetitions The number of times to apply the spike pattern.
   * @param patterns Array containing spike patterns, in the form [initial, dim 1, dim 2][pre,
   *     post][spike number] = spike time. The [spike number] array contains the times (s) of each
   *     spike, relative to the beginning of the pattern. See {@link
   *     com.ojcoleman.bain.neuron.spiking.FixedProtocolNeuronCollection}.
   * @param refSpikeIndexes Array specifying indexes of the two spikes to use as timing variation
   *     references for each variation dimension, in the form [dim 1, dim 2][reference spike,
   *     relative spike] = spike index.
   * @param refSpikePreOrPost Array specifying whether the timing variation reference spikes
   *     specified by refSpikeIndexes belong to the pre- or post-synaptic neurons, in the form [dim
   *     1, dim 2][base spike, relative spike] = Constants.PRE or Constants.POST.
   * @param logSpikesAndStateVariables Whether to record pre- and post-synaptic spikes and any state
   *     variables exposed by the synapse model in the test results.
   * @param progressMonitor If not null, this will be updated with the current progress.
   * @return For a single spike protocol, a TestResults object with type {@link TYPE#STDP}
   *     consisting of series labelled "Time" and "Efficacy" and if logSpikesAndStateVariables ==
   *     true then also "Pre-synaptic spikes", "Post-synaptic spikes" and any state variables
   *     exposed by the synapse model. For a protocol varied over one dimension, a TestResults
   *     object with type {@link TYPE#STDP_1D} consisting of series labelled "Time delta" and
   *     "Efficacy". For a protocol varied over two dimensions, a TestResults object with type
   *     {@link TYPE#STDP_2D} consisting of series labelled "Time delta 1", "Time delta 2" and
   *     "Efficacy".
   */
  public static TestResults testPattern(
      SynapseCollection<? extends ComponentConfiguration> synapse,
      int timeResolution,
      double period,
      int repetitions,
      double[][][] patterns,
      int[][] refSpikeIndexes,
      int[][] refSpikePreOrPost,
      boolean logSpikesAndStateVariables,
      ProgressMonitor progressMonitor)
      throws IllegalArgumentException {
    int variationDimsCount =
        patterns.length - 1; // Number of dimensions over which spike timing patterns vary.
    if (variationDimsCount > 2) {
      throw new IllegalArgumentException(
          "The number of variation dimensions may not exceed 2 (patterns.length must be <= 3)");
    }

    if (progressMonitor != null) {
      progressMonitor.setMinimum(0);
    }

    TestResults results = new TestResults();

    FixedProtocolNeuronCollection neurons = new FixedProtocolNeuronCollection(2);
    FixedProtocolNeuronConfiguration preConfig =
        new FixedProtocolNeuronConfiguration(period, patterns[0][0]);
    neurons.addConfiguration(preConfig);
    neurons.setComponentConfiguration(0, 0);
    FixedProtocolNeuronConfiguration postConfig =
        new FixedProtocolNeuronConfiguration(period, patterns[0][1]);
    neurons.addConfiguration(postConfig);
    neurons.setComponentConfiguration(1, 1);

    synapse.setPreNeuron(0, 0);
    synapse.setPostNeuron(0, 1);

    NeuralNetwork sim = new NeuralNetwork(timeResolution, neurons, synapse);

    int simSteps = (int) Math.round(period * repetitions * timeResolution);

    int displayTimeResolution = Math.min(1000, timeResolution);

    results.setProperty("simulation time resolution", timeResolution);
    results.setProperty("display time resolution", displayTimeResolution);

    // long startTime = System.currentTimeMillis();

    // If we're just testing a single spike pattern. // Handle separately as logging is quite
    // different from testing spike
    // patterns with gradually altered spike times.
    if (variationDimsCount == 0) {
      results = singleTest(sim, simSteps, logSpikesAndStateVariables, 0);
    } else { // We're testing spike patterns with gradually altered spike times over one or two
      // dimensions.

      int[] spikeCounts = {patterns[0][0].length, patterns[0][1].length};
      // The initial and final time deltas (s), given base and relative spike times in initial and
      // final spike patterns,
      // for each variation dimension.
      double[] timeDeltaInitial = new double[2], timeDeltaFinal = new double[2];
      // The time delta range(s) for each variation dimension.
      double[] timeDeltaRange = new double[2];
      int[] positionsCount = new int[2];
      int[][] variationDimForSpike =
          new int[2][Math.max(spikeCounts[0], spikeCounts[1])]; // [pre, post][spike index]

      // Set-up parameters for testing spike patterns with gradually altered spike times over one or
      // two dimensions.
      for (int d = 0; d < variationDimsCount; d++) {
        double baseRefSpikeTimeInitial =
            patterns[0][refSpikePreOrPost[d][0]][refSpikeIndexes[d][0]];
        double relativeRefSpikeTimeInitial =
            patterns[0][refSpikePreOrPost[d][1]][refSpikeIndexes[d][1]];
        double baseRefSpikeTimeFinal =
            patterns[d + 1][refSpikePreOrPost[d][0]][refSpikeIndexes[d][0]];
        double relativeRefSpikeTimeFinal =
            patterns[d + 1][refSpikePreOrPost[d][1]][refSpikeIndexes[d][1]];

        timeDeltaInitial[d] = relativeRefSpikeTimeInitial - baseRefSpikeTimeInitial;
        timeDeltaFinal[d] = relativeRefSpikeTimeFinal - baseRefSpikeTimeFinal;
        timeDeltaRange[d] = Math.abs(timeDeltaInitial[d] - timeDeltaFinal[d]);

        // From the initial and final spiking protocols we generate intermediate spiking protocols
        // by interpolation. //
        // Each position in between the initial and final protocol adjusts the time differential
        // between the base and
        // reference spikes by (1/timeResolution) seconds.
        positionsCount[d] = (int) Math.round(timeDeltaRange[d] * displayTimeResolution) + 1;

        // Determine which dimension, if any, a spikes timing varies over (and ensure that a spikes
        // timing only varies
        // over at most one dimension).
        // If the spikes time in variation dimension d is different to the initial spike time.
        for (int p = 0; p < 2; p++) {
          for (int si = 0; si < spikeCounts[p]; si++) {
            // If it also differs in another dimension.
            if (patterns[0][p][si] != patterns[d + 1][p][si]) {
              if (variationDimForSpike[p][si] != 0) {
                throw new IllegalArgumentException(
                    "A spikes timing may vary at most over one variation dimension. "
                        + (p == 0 ? "Pre" : "Post")
                        + "-synaptic spike "
                        + (si + 1)
                        + " varies over two.");
              }
              variationDimForSpike[p][si] = d + 1;
            }
          }
        }
      }

      double[][] currentSpikeTimings =
          new double[2][]; // Current pre and post spiking patterns [pre, post][spike index]
      for (int p = 0; p < 2; p++) {
        currentSpikeTimings[p] = new double[spikeCounts[p]];
        System.arraycopy(patterns[0][p], 0, currentSpikeTimings[p], 0, spikeCounts[p]);
      }

      // If we're testing spike patterns with gradually altered spike times over one dimension.
      if (variationDimsCount == 1) {
        // Arrays to record results.
        double[] time =
            new double[positionsCount[0]]; // The time delta in seconds [time delta index]
        // The change in synapse efficacy after all repetitions for each pattern [time delta index]
        double[] efficacyLog = new double[positionsCount[0]];

        if (progressMonitor != null) {
          progressMonitor.setMaximum(positionsCount[0]);
        }

        for (int timeDeltaIndex = 0; timeDeltaIndex < positionsCount[0]; timeDeltaIndex++) {
          if (progressMonitor != null) {
            progressMonitor.setProgress(timeDeltaIndex);
          }

          double position =
              (double) timeDeltaIndex
                  / (positionsCount[0] - 1); // Position in variation dimension 1

          // Generate pre and post spike timing patterns for this position.
          for (int p = 0; p < 2; p++) {
            for (int si = 0; si < spikeCounts[p]; si++) { // If this spikes timing varies.
              int variationDim = variationDimForSpike[p][si];
              if (variationDim != 0) {
                currentSpikeTimings[p][si] =
                    position * patterns[0][p][si] + (1 - position) * patterns[variationDim][p][si];
              }
            }
          }

          preConfig.spikeTimings = currentSpikeTimings[0];
          postConfig.spikeTimings = currentSpikeTimings[1];
          preConfig.fireChangeEvent();
          postConfig.fireChangeEvent();

          sim.reset();
          sim.run(simSteps);

          time[timeDeltaIndex] =
              position * timeDeltaInitial[0] + (1 - position) * timeDeltaFinal[0];
          efficacyLog[timeDeltaIndex] = synapse.getEfficacy(0);
        }

        results.setProperty("type", TYPE.STDP_1D);
        results.addResult("Efficacy", efficacyLog);
        results.addResult("Time delta", time);

        // We're testing spike patterns with gradually altered spike times over two dimensions.
      } else {
        // The change in synapse efficacy after all repetitions for each pattern
        // [time delta for var dim 1, time delta for var dim 2, synapse efficacy][result index]
        double[][] efficacyLog = new double[3][(positionsCount[0]) * (positionsCount[1])];

        if (progressMonitor != null) {
          progressMonitor.setMaximum(efficacyLog[0].length);
        }

        double[] position = new double[2]; // Position in variation dimensions 1 and 2
        for (int timeDeltaIndex1 = 0, resultIndex = 0;
            timeDeltaIndex1 < positionsCount[0];
            timeDeltaIndex1++) {
          position[0] = (double) timeDeltaIndex1 / (positionsCount[0] - 1);

          for (int timeDeltaIndex2 = 0;
              timeDeltaIndex2 < positionsCount[1];
              timeDeltaIndex2++, resultIndex++) {
            if (progressMonitor != null) {
              progressMonitor.setProgress(resultIndex);
            }

            position[1] = (double) timeDeltaIndex2 / (positionsCount[1] - 1);

            // Generate pre and post spike timing patterns for this position.
            for (int p = 0; p < 2; p++) {
              for (int si = 0; si < spikeCounts[p]; si++) { // If this spikes timing varies.
                int variationDim = variationDimForSpike[p][si];
                if (variationDim != 0) {
                  currentSpikeTimings[p][si] =
                      (1 - position[variationDim - 1]) * patterns[0][p][si]
                          + position[variationDim - 1] * patterns[variationDim][p][si];
                }
              }
            }

            preConfig.spikeTimings = currentSpikeTimings[0];
            postConfig.spikeTimings = currentSpikeTimings[1];
            preConfig.fireChangeEvent();
            postConfig.fireChangeEvent();

            sim.reset();
            sim.run(simSteps);

            efficacyLog[0][resultIndex] =
                (1 - position[0]) * timeDeltaInitial[0] + position[0] * timeDeltaFinal[0];
            efficacyLog[1][resultIndex] =
                (1 - position[1]) * timeDeltaInitial[1] + position[1] * timeDeltaFinal[1];
            efficacyLog[2][resultIndex] = synapse.getEfficacy(0);
          }
        }

        results.setProperty("type", TYPE.STDP_2D);
        results.addResult("Time delta 1", "Time delta 2", "Efficacy", efficacyLog);
      }
    }

    return results;
  }