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