public double tryChangeSource(double temp, double currentEnergy) {
    PEdge<V, E> fakeEdge = sourceFakeEdges.get(random.nextInt(sourceFakeEdges.size()));
    PNode<V, E> child = fakeEdge.to;
    int oldIndex = fakeEdge.from.getChildren().indexOf(fakeEdge);
    child.removeParent(fakeEdge);
    fakeEdge.from.removeChild(fakeEdge);
    PEdge<V, E> fakeEdge2;

    int fakeEdge2Swap = -1;

    if (fakeEdge.from.isRoot || random.nextDouble() < 0.4) { // try attaching it somewhere else
      PNode<V, E> parent;
      if (Math.random() < 0.5
          || nodesWithSeveralChildrenScaled.size() == 0) { // try some completely random node
        parent = nodes.get(random.nextInt(nodes.size()));
      } else {
        parent =
            nodesWithSeveralChildrenScaled.get(
                random.nextInt(nodesWithSeveralChildrenScaled.size()));
      }
      fakeEdge2 = new PEdge<V, E>(parent, child);
      parent.addChild(fakeEdge2);
      if (parent.getChildren().size() > 1) {
        fakeEdge2Swap = random.nextInt(parent.getChildren().size() - 1);
        parent.swapChildren(parent.getChildren().size() - 1, fakeEdge2Swap);
      }
      child.addParent(fakeEdge2);
    } else { // try attaching it back to root.
      fakeEdge2 = new PEdge<V, E>(root, child);
      root.addChild(fakeEdge2);
      if (root.getChildren().size() > 1) {
        fakeEdge2Swap = random.nextInt(root.getChildren().size() - 1);
        root.swapChildren(root.getChildren().size() - 1, fakeEdge2Swap);
      }
      child.addParent(fakeEdge2);
    }
    if (!graphHasCycle(fakeEdge2.to)) {
      double newEnergy = getEnergy();
      if (acceptChange(currentEnergy, newEnergy, temp)) {
        sourceFakeEdges.remove(fakeEdge);
        sourceFakeEdges.add(fakeEdge2);
        return newEnergy;
      }
    }

    fakeEdge2.from.removeChild(fakeEdge2);
    child.removeParent(fakeEdge2);
    if (fakeEdge2Swap != -1) {
      PEdge<V, E> e = fakeEdge2.from.getChildren().get(fakeEdge2.from.getChildren().size() - 1);
      fakeEdge2.from.getChildren().remove(fakeEdge2.from.getChildren().size() - 1);
      fakeEdge2.from.getChildren().add(fakeEdge2Swap, e);
    }
    // fakeEdge.from.addChild(fakeEdge);
    fakeEdge.from.getChildren().add(oldIndex, fakeEdge);
    child.addParent(fakeEdge);
    return currentEnergy;
  }
  public double tryAddFakeEdge(double temp, double currentEnergy) {
    // it's sufficient to only add fake edges from leaves to nodes with multiple parents
    if (nodesWithoutChildren.size() > 0 && nodesWithSeveralParents.size() > 0) {
      PNode<V, E> from = nodesWithoutChildren.get(random.nextInt(nodesWithoutChildren.size()));
      PNode<V, E> to = nodesWithSeveralParents.get(random.nextInt(nodesWithSeveralParents.size()));
      PEdge<V, E> fakeEdge = new PEdge<V, E>(from, to);
      from.addChild(fakeEdge);
      to.addParent(fakeEdge);

      if (!graphHasCycle(to)) {
        double newEnergy = getEnergy();
        if (acceptChange(currentEnergy, newEnergy, temp)) {
          fakeEdges.add(fakeEdge);
          return newEnergy;
        }
      }
      // otherwise undo changes
      from.removeChild(fakeEdge);
      to.removeParent(fakeEdge);
      return currentEnergy;
    }
    return currentEnergy;
  }