From 09d03e972601c5c12f35cc834ab97ee04cdbe957 Mon Sep 17 00:00:00 2001 From: berleon <github@leon-sixt.de> Date: Wed, 19 Jul 2017 17:14:58 +0200 Subject: [PATCH] Add evaluation & small fixes for transaction logs --- evaluation.ipynb | 158 ++++++++++++++++++ log/2017-07-19T09:49Z.json | 1 + log/2017-07-19T09:51Z.json | 1 + log/2017-07-19T10:20Z.json | 1 + log/2017-07-19T14:35Z.json | 1 + log/2017-07-19T14:52Z.json | 1 + log/2017-07-19T15:03Z.json | 1 + log/2017-07-19T15:12Z.json | 1 + src/main/java/fucoin/AbstractNode.java | 18 +- src/main/java/fucoin/Snapshot.java | 36 ++-- .../actions/snap/SnapShotSaveState.java | 8 +- .../actions/snap/SnapShotSaveTransaction.java | 1 - .../GGBWalletConfiguration.java | 83 +++++---- 13 files changed, 261 insertions(+), 50 deletions(-) create mode 100644 evaluation.ipynb create mode 100644 log/2017-07-19T09:49Z.json create mode 100644 log/2017-07-19T09:51Z.json create mode 100644 log/2017-07-19T10:20Z.json create mode 100644 log/2017-07-19T14:35Z.json create mode 100644 log/2017-07-19T14:52Z.json create mode 100644 log/2017-07-19T15:03Z.json create mode 100644 log/2017-07-19T15:12Z.json diff --git a/evaluation.ipynb b/evaluation.ipynb new file mode 100644 index 0000000..7a87832 --- /dev/null +++ b/evaluation.ipynb @@ -0,0 +1,158 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import json\n", + "import os\n", + "\n", + "from pylab import rcParams\n", + "rcParams['figure.figsize'] = 10, 6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "log_dir = 'log'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "snapshots = {}\n", + "for filename in os.listdir(log_dir):\n", + " with open(os.path.join(log_dir, filename), 'r') as f:\n", + " snapshots[filename] = json.load(f)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for filename, snap in snapshots.items():\n", + " if len(snap) != 9:\n", + " os.remove(os.path.join(log_dir, filename))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "snapshots = {fname: snap for fname, snap in snapshots.items() if len(snap) == 9}\n", + "print(\"got {} snapshots\".format(len(snapshots)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "next(iter(snapshots.values()))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_all_values(prefix):\n", + " all_values = []\n", + " for snap in snapshots.values():\n", + " snap_values = []\n", + " for node, value in snap.items():\n", + " if node.startswith(prefix):\n", + " snap_values.append(value[0])\n", + " all_values.append(snap_values)\n", + " return np.array(all_values)\n", + "\n", + "get_all_values('balanced')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "labels = ['generous', 'balanced', 'greedy']\n", + "gbg = np.stack([get_all_values(prefix).flatten() for prefix in labels]).T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gbg.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.boxplot(gbg)\n", + "plt.xticks(np.arange(1, len(labels)+1), labels)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/log/2017-07-19T09:49Z.json b/log/2017-07-19T09:49Z.json new file mode 100644 index 0000000..f4b9181 --- /dev/null +++ b/log/2017-07-19T09:49Z.json @@ -0,0 +1 @@ +{"greedy_2":[84],"balanced_1":[233],"greedy_1":[144],"balanced_2":[113],"greedy_0":[61],"balanced_0":[235],"generous_1":[5],"generous_2":[6],"generous_0":[19]} diff --git a/log/2017-07-19T09:51Z.json b/log/2017-07-19T09:51Z.json new file mode 100644 index 0000000..c7d18bf --- /dev/null +++ b/log/2017-07-19T09:51Z.json @@ -0,0 +1 @@ +{"balanced_1":[108],"greedy_2":[123],"greedy_1":[112],"balanced_2":[40],"greedy_0":[40],"balanced_0":[387],"generous_1":[38],"generous_2":[21],"generous_0":[31]} diff --git a/log/2017-07-19T10:20Z.json b/log/2017-07-19T10:20Z.json new file mode 100644 index 0000000..26d298e --- /dev/null +++ b/log/2017-07-19T10:20Z.json @@ -0,0 +1 @@ +{"greedy_2":[248],"balanced_1":[110],"greedy_1":[134],"balanced_2":[143],"greedy_0":[56],"balanced_0":[206],"generous_1":[0],"generous_2":[1],"generous_0":[2]} diff --git a/log/2017-07-19T14:35Z.json b/log/2017-07-19T14:35Z.json new file mode 100644 index 0000000..aabba12 --- /dev/null +++ b/log/2017-07-19T14:35Z.json @@ -0,0 +1 @@ +{"greedy_2":[77],"balanced_1":[147],"greedy_1":[236],"balanced_2":[106],"greedy_0":[141],"balanced_0":[172],"generous_1":[0],"generous_2":[20],"generous_0":[1]} diff --git a/log/2017-07-19T14:52Z.json b/log/2017-07-19T14:52Z.json new file mode 100644 index 0000000..fa4c775 --- /dev/null +++ b/log/2017-07-19T14:52Z.json @@ -0,0 +1 @@ +{"greedy_2":[31],"balanced_1":[78],"greedy_1":[147],"balanced_2":[272],"greedy_0":[58],"balanced_0":[147],"generous_1":[119],"generous_2":[16],"generous_0":[32]} diff --git a/log/2017-07-19T15:03Z.json b/log/2017-07-19T15:03Z.json new file mode 100644 index 0000000..217ebd9 --- /dev/null +++ b/log/2017-07-19T15:03Z.json @@ -0,0 +1 @@ +{"greedy_2":[91],"balanced_1":[122],"greedy_1":[174],"balanced_2":[171],"greedy_0":[64],"balanced_0":[151],"generous_1":[100],"generous_2":[1],"generous_0":[26]} diff --git a/log/2017-07-19T15:12Z.json b/log/2017-07-19T15:12Z.json new file mode 100644 index 0000000..1ada103 --- /dev/null +++ b/log/2017-07-19T15:12Z.json @@ -0,0 +1 @@ +{"greedy_2":[39],"balanced_1":[21],"greedy_1":[407],"balanced_2":[132],"greedy_0":[120],"balanced_0":[92],"generous_1":[23],"generous_2":[65],"generous_0":[1]} diff --git a/src/main/java/fucoin/AbstractNode.java b/src/main/java/fucoin/AbstractNode.java index 4c69ab7..6d855d9 100644 --- a/src/main/java/fucoin/AbstractNode.java +++ b/src/main/java/fucoin/AbstractNode.java @@ -91,7 +91,11 @@ public abstract class AbstractNode extends UntypedActor implements Serializable } - public Promise<Snapshot> makeSnapshot() throws InterruptedException{ + public Promise<Snapshot> makeSnapshot() throws InterruptedException { + return makeSnapshot(true); + } + + public Promise<Snapshot> makeSnapshot(boolean useDelay) throws InterruptedException{ String prefix = ""; if (this instanceof SuperVisorImpl) { prefix = "supervisor"; @@ -110,11 +114,13 @@ public abstract class AbstractNode extends UntypedActor implements Serializable int i = 0; for (ActorRef act : getKnownNeighbors().values()) { if(i % 3 == 0){ - try { - //Add a little delay now and then - Thread.sleep(500); - } catch (InterruptedException ex) { - Exceptions.printStackTrace(ex); + if (useDelay) { + try { + //Add a little useDelay now and then + Thread.sleep(500); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } } } act.tell(new SnapShotBegin(this.currentSnapshotToken), self()); diff --git a/src/main/java/fucoin/Snapshot.java b/src/main/java/fucoin/Snapshot.java index 3af9feb..51bdf6d 100644 --- a/src/main/java/fucoin/Snapshot.java +++ b/src/main/java/fucoin/Snapshot.java @@ -5,25 +5,41 @@ import fucoin.actions.snap.SnapShotSaveState; import org.json.JSONObject; import scala.concurrent.Promise; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class Snapshot { - public HashMap<String, Integer> states = new HashMap<>(); - public List<SnapshotTransaction> transactions = new ArrayList<>(); + public Map<String, Integer> states = new ConcurrentHashMap<String, Integer>(); + public List<SnapshotTransaction> transactions = Collections.synchronizedList(new ArrayList<>()); private Promise<Snapshot> complete = Futures.promise(); + @Override public String toString() { - return "Snapshot{" + - "states=" + states + - ", transactions=" + transactions + - '}'; + StringBuilder sb = new StringBuilder(); + sb.append("Snapshot {\n"); + sb.append(" States {\n"); + for (String name : states.keySet()) { + sb.append(" " + name + ": " + states.get(name) + "\n"); + } + sb.append(" }\n"); + sb.append(" Transactions {\n"); + for (int i = 0; i < transactions.size(); i++) { + SnapshotTransaction ts = transactions.get(i); + sb.append(" " + ts.amount + " FU's from " + ts.sender + " to " + ts.receiver + "\n"); + } + sb.append(" }\n"); + sb.append("}\n"); + + return sb.toString(); } public JSONObject toJson() { - return null; + JSONObject obj = new JSONObject(); + for (String key : states.keySet()) { + obj.append(key, states.get(key)); + } + return obj; } public void addState(String name, int amount) { diff --git a/src/main/java/fucoin/actions/snap/SnapShotSaveState.java b/src/main/java/fucoin/actions/snap/SnapShotSaveState.java index 529d669..ec40a52 100644 --- a/src/main/java/fucoin/actions/snap/SnapShotSaveState.java +++ b/src/main/java/fucoin/actions/snap/SnapShotSaveState.java @@ -26,7 +26,13 @@ public class SnapShotSaveState extends SnapAction { for (ActorRef node : abstractNode.getKnownNeighbors().values()) { node.tell(new SnapShotEnd(), self); } - snap.completed(); + try { + if (! snap.promise().isCompleted()) { + snap.completed(); + } + } catch (IllegalStateException e) { + throw e; + } } } } diff --git a/src/main/java/fucoin/actions/snap/SnapShotSaveTransaction.java b/src/main/java/fucoin/actions/snap/SnapShotSaveTransaction.java index 53839d8..e20dedc 100644 --- a/src/main/java/fucoin/actions/snap/SnapShotSaveTransaction.java +++ b/src/main/java/fucoin/actions/snap/SnapShotSaveTransaction.java @@ -4,7 +4,6 @@ import akka.actor.ActorRef; import akka.actor.UntypedActorContext; import fucoin.AbstractNode; import fucoin.SnapshotTransaction; -import fucoin.actions.Action; public class SnapShotSaveTransaction extends SnapAction { public SnapshotTransaction transaction; diff --git a/src/main/java/fucoin/configurations/GGBWalletConfiguration.java b/src/main/java/fucoin/configurations/GGBWalletConfiguration.java index 94059b8..75d565e 100644 --- a/src/main/java/fucoin/configurations/GGBWalletConfiguration.java +++ b/src/main/java/fucoin/configurations/GGBWalletConfiguration.java @@ -2,6 +2,7 @@ package fucoin.configurations; import akka.actor.ActorRef; import akka.dispatch.Futures; +import akka.dispatch.OnComplete; import akka.dispatch.OnSuccess; import akka.pattern.Patterns; import fucoin.Snapshot; @@ -11,19 +12,24 @@ import fucoin.actions.transaction.ActionGetAmount; import fucoin.actions.transaction.ActionGetAmountAnswer; import fucoin.actions.transaction.ActionNotifyObserver; import fucoin.configurations.internal.ConfigurationName; -import fucoin.supervisor.SuperVisorLogImpl; import org.apache.commons.math3.distribution.EnumeratedDistribution; import org.apache.commons.math3.util.Pair; +import org.json.JSONObject; import scala.Function1; +import scala.PartialFunction; import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.Promise; import scala.runtime.BoxedUnit; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import scala.util.Try; + +import java.io.File; +import java.io.PrintWriter; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import org.openide.util.Exceptions; @@ -101,11 +107,12 @@ class WalletSimulation { @ConfigurationName("GGBWallet") public class GGBWalletConfiguration extends AbstractConfiguration { - private static int n_generous = 1; - private static int n_greedy = 1; - private static int n_balanced = 1; - private static double n_steps = 40000; - private static String log_filename = "log.json"; + private static int n_generous = 3; + private static int n_greedy = 3; + private static int n_balanced = 3; + private static int n_steps = 500; + private static String logDirectory = "log"; + private Map<String, WalletSimulation> simulations; private HashMap<String,ActorRef> walletMap; @@ -127,9 +134,13 @@ public class GGBWalletConfiguration extends AbstractConfiguration { private int currentStep = 0; private Promise<Void> simulationFinished = Futures.promise(); - private double start; - + static { + File logDir = new File(logDirectory); + if (!logDir.exists()) { + logDir.mkdir(); + } + } @Override public void run() { simulations = new HashMap<>(); @@ -185,23 +196,40 @@ public class GGBWalletConfiguration extends AbstractConfiguration { simulations.put(name, new WalletSimulation(name, allWalletNames, BALANCED_PROBABILITY, BALANCED_MIN, BALANCED_MAX, BALANCED_ADAPTION)); } - start = System.currentTimeMillis() / 1000.; try { this.nextTransaction(); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); } - simulationFinished.future().onSuccess(new OnSuccess<Void>() { + simulationFinished.future().andThen(new OnComplete<Void>() { @Override - public void onSuccess(Void result) { - // You can start your algorithm here if you want to. - // Alternatively, you can also notify the user that all transactions are finished - System.out.println("All random transactions finished!"); + public void onComplete(Throwable failure, Void success) throws Throwable { + final boolean noDelay = false; + GGBWalletConfiguration.this.makeSnapshot(noDelay).future().onSuccess( + new OnSuccess<Snapshot>() { + @Override + public void onSuccess(Snapshot snap) throws Throwable { + JSONObject json = snap.toJson(); + TimeZone tz = TimeZone.getTimeZone("UTC"); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); + df.setTimeZone(tz); + String nowAsISO = df.format(new Date()); + + Path logFilename = Paths.get(GGBWalletConfiguration.logDirectory, nowAsISO + ".json"); + + try(PrintWriter f = new PrintWriter(logFilename.toString())) { + f.println(json.toString()); + } + System.out.println("SNAPSHOT saved under: " + logFilename.toString()); + } + } + , context().dispatcher()); } }, context().dispatcher()); } private void nextTransaction() throws InterruptedException { for(;currentStep < n_steps; currentStep++) { + for (WalletSimulation currentWallet : simulations.values()) { if(currentWallet.willSend()){ String receiverName = currentWallet.sampleReceiver(); @@ -229,6 +257,10 @@ public class GGBWalletConfiguration extends AbstractConfiguration { } } } + if (simulationFinished != null) { + simulationFinished.success(null); + simulationFinished = null; + } } @@ -246,19 +278,6 @@ public class GGBWalletConfiguration extends AbstractConfiguration { } return 0; } - @Override - ActorRef initSupervisor() { - superVisor = context().actorOf( - SuperVisorLogImpl.props(log_filename), "SuperVisorImpl"); - // Don't ask. - try { - Thread.sleep(200); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return superVisor; - } - @Override public void onReceive(Object message) { -- GitLab