Repository: SoftwareQuTech/SimulaQron (language: python)

Issue:
Release 3.0.16
- Fix bug with noisy-qubit option

closes #261 


Discussion / hints:

Fix patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 59f8d41..f8834d0 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 3.0.16
+current_version = 4.0.0
 commit = True
 tag = False
 
diff --git a/.flake8 b/.flake8
index b74c3bb..122b8bd 100644
--- a/.flake8
+++ b/.flake8
@@ -4,3 +4,4 @@ exclude = docs, .git, .idea, __pycache__
 ignore = E203 W291 W293 E743 E265 W605 F811 E741
 max-line-length = 120
 statistics = True
+per-file-ignores = */__init__.py:F401, */typing.py:F401
diff --git a/.github/workflows/actions.yaml b/.github/workflows/actions.yaml
new file mode 100644
index 0000000..78e913c
--- /dev/null
+++ b/.github/workflows/actions.yaml
@@ -0,0 +1,40 @@
+name: General check
+
+on: push
+
+jobs:
+  lint:
+    name: Run linter  
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@master
+    - uses: actions/setup-python@master
+      with:
+        python-version: 3.8
+    - run: |
+        make install
+        make lint
+
+  tests:
+    name: Run tests  
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@master
+    - uses: actions/setup-python@master
+      with:
+        python-version: 3.8
+    - run: |
+        make install
+        make tests
+
+  examples:
+    name: Run examples
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@master
+    - uses: actions/setup-python@master
+      with:
+        python-version: 3.8
+    - run: |
+        make install
+        make examples
diff --git a/.github/workflows/netqasm.yaml b/.github/workflows/netqasm.yaml
new file mode 100644
index 0000000..07c2b0f
--- /dev/null
+++ b/.github/workflows/netqasm.yaml
@@ -0,0 +1,27 @@
+name: NetQASM backend tests
+
+on: push
+
+jobs:
+  examples:
+    name: Run examples
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@master
+    - uses: actions/setup-python@master
+      with:
+        python-version: 3.8
+    - name: Clone and install netqasm
+      run: |
+        git clone https://github.com/QuTech-Delft/netqasm.git
+        cd netqasm
+        make install
+        cd ..
+    - name: Install simulaqron
+      run: make install
+    - name: Install projectq
+      run: pip install -Iv projectq==0.5.1
+    - name: Run examples
+      env:
+          NETQASM_SIMULATOR: simulaqron
+      run: make -C netqasm external-examples
diff --git a/.gitignore b/.gitignore
index a3126a8..22e570f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,5 +8,7 @@
 /simulaqron/config/*
 
 .idea/*
+.vscode/*
 
 cqc/backend/logFile*
+examples/**/log/*
diff --git a/.travis.yml b/.travis.yml
index 8b4eceb..bc375f1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,44 +1,44 @@
 language: python
 branches:
   only:
-    - master
-    - Develop
+  - master
+  - Develop
 matrix:
   include:
-    - name: Python 3.5 on Linux
-      python: 3.5
-    - name: Python 3.6 on Linux
-      python: 3.6
-      env:
-      - BUILD=True
-    - name: Python 3.7 on Xenial Linux
-      python: 3.7
-      dist: xenial
-    - name: Python 3.7 on macOS
-      os: osx
-      osx_image: xcode10.2
-      language: shell
-      env:
-      - PIPFLAG="--user"
+  - name: Python 3.5 on Linux
+    python: 3.5
+  - name: Python 3.6 on Linux
+    python: 3.6
+    env:
+    - BUILD=True
+  - name: Python 3.7 on Xenial Linux
+    python: 3.7
+    dist: xenial
+  - name: Python 3.7 on macOS
+    os: osx
+    osx_image: xcode10.2
+    language: shell
+    env:
+    - PIPFLAG="--user"
 before_install:
-  - make build
+- make build
 install:
-  - pip3 install dist/*.whl $PIPFLAG
+- pip3 install dist/*.whl $PIPFLAG
 script:
-  - make lint
-  - make tests
+- make lint
+- make tests
 before_deploy:
-  - cd docs
-  - make build
-  - cd ..
+- cd docs
+- make build
+- cd ..
 deploy:
   - provider: pypi
-    user: adahlberg
+    user: __token__
     password:
-      secure: DwhFEJlCg5477emRHek2sgoz6l51fJoH6Uf1MGV1v9OYwS0KJu9CZGzfxWf+4+S5HotK6lyQP16YxTnBUf5MUW3m7vn1q4QOPS02Ga3ETZ50ndxvnob0nxgZAkvHYpG2nSwx73g/yU2cHTqekBhtM2WUg7SzXGt8cwJblGKcckA4mZrbxOYOOmE6G4VpjtgTab107eJt5lgwnRan7UUV6bg/+FuR3nsrDtIuUm66jzhhdPCqpIRm2e7npkejpGm3zl3s8dpD7b1wN/q/3YCnMJvy6JcO9PfyFmB/nmVPhk9a+fuRGK8/uj/b5Vmk9t2NTpaqlaUvTp/S/agojiPl3+UxIfhpsaycWbtDNBhNPtbSkDcgyJDdnl0EsQZEnfIHci/7L7GdeYt0M+N7m8orApHuyhfORUind5hDLu/0UMDI7K/UhRFXTOMLrCKFtbk/02KxapBL51+SE/G2kgaXE6HCO+8knGob+LAdJY+1xL3WKhfB+5+DyIYoigJuJv0ObElYNuJJe5a0uy+0kKyjcEEozhr59ad4qoQr8u9piF8DoF3lVLBuadivtUME0mvlFHdm7ShNYE74HYjDzIkDGWtl5ONakpXH4dWOflGuJfG/tzJeN5sgGJMG6rkXNbN5OtU11CwUupII+/mtVrBv/mn4mEU2/3GW4qsmbpV2+k0=
+      secure: HS1N/sC4eBKH/SQWLNxxO1wS1/P2lp5aV69plaw0sU3aj97uz1RkLDPI8YUG1y3yEIR053JB+MGSnZ1DxXEqu0lH6XXI+pBtej47asRB+mbjFUZpCSPtq2KzcVkGF5bxpyA1k6l6Q3NauyJw2gAJrP+YYX6lO1Fd+WDdzEFw4sPnyM+r77krfOPMozEr1vBls2H5kVxA61yxb+uaydefL4tNqaZy8oCTOGEDUT/OzgiX+pWGtmOYP1MIs7ZBboODY3KzwgkVfPtEPsqR0AAic8KHzrZxjd32a3FqmVAV8+Gv0f7HaL6eRP9hJcxwOHRpv7v66BFBRe9zS16CV9CyaSEQ61ZDKdnEIvjSUiXwQeHnLahMNpX7qdRKmjbvM1Odij1qd44KHg10H0hywf2brqQJEUDh3iquas5A9cB/5aAqalHBJ5+pThkOhG23PoE/OfuXAjBjKIrAkHgugekG0aPC3KskdX1EHxiyobvuyvJFqchT7xpYY1NDFVrawdYpLWNPU6Mw2cobCKIg7WvkJa0oN7ndOE40YUNeuzfP+/0QYuAf/CKE344Rk7+1j2mg6vRxshm7N58hZ2d1wAPTa0ucX8PwhT2RPmylZQm2TBfXgRAuM7ggSpeYGOvbVryqfV76s9mdm5AwRtEyPvWt8KMkL2FGaSnFlx2juRkpk7Q=
     distributions: sdist bdist_wheel
     skip_existing: true
-    on: 
+    on:
       branch: master
       condition: "$BUILD = True"
   - provider: pages
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc34df5..9d00ca9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,13 @@ For more details refer to the [documentation](https://softwarequtech.github.io/S
 Upcoming
 --------
 
+2020-11-24 (v4.0.0)
+-------------------
+- Major breaking change! Using `cqc` is now deprecated and replaced with `netqasm`.
+  `netqasm` improves on `cqc` on many levels, for more details.
+  As with `cqc`, `netqasm` also has a SDK in python for writing applications, which is very similar to the SDK for `cqc`.
+  However, there are some difference, for details see [the netqasm repo](https://github.com/QuTech-Delft/netqasm) and [its documentation](https://netqasm.readthedocs.io/en/latest/).
+
 2020-08-24 (v3.0.16)
 -------------------
 - Fixed bug with setting `noisy-qubit` option from cli.
diff --git a/Makefile b/Makefile
index bf807a5..88d9f2d 100644
--- a/Makefile
+++ b/Makefile
@@ -2,13 +2,8 @@ PYTHON         = python3
 PIP            = pip3
 EXAMPLES_DIR   = examples
 SIMULAQRON_DIR = simulaqron
-GENERAL_DIR    = simulaqron/cqc_backend
-GENERAL_DIR    = simulaqron/general
-LOCAL_DIR      = simulaqron/local
-RUN_DIR        = simulaqron/run
-TESTS_DIR      = simulaqron/tests
-TOOLBOX_DIR    = simulaqron/toolbox
-VIRTNODE_DIR   = simulaqron/virtNode
+TEST_DIR       = tests
+RESET_FILE     = ${SIMULAQRON_DIR}/toolbox/reset.py
 
 clean: _delete_pyc _delete_pid _clear_build _reset
 
@@ -19,24 +14,33 @@ _delete_pid:
 	@find ${SIMULAQRON_DIR} -name '*.pid' -delete
 
 lint:
-	@${PYTHON} -m flake8 ${SIMULAQRON_DIR} ${CQC_DIR} ${EXAMPLES_DIR} ${GENERAL_DIR} ${LOCAL_DIR} ${RUN_DIR} ${TESTS_DIR} ${TOOLBOX_DIR} ${VIRTNODE_DIR}
+	@${PYTHON} -m flake8 ${SIMULAQRON_DIR} ${EXAMPLES_DIR} ${TEST_DIR}
 
-python-deps:
+test-deps:
+	@${PYTHON} -m pip install -r test_requirements.txt
+
+requirements python-deps:
 	@cat requirements.txt | xargs -n 1 -L 1 $(PIP) install
 
+install-optional: install
+	@cat optional-requirements.txt | xargs -n 1 -L 1 $(PIP) install
+
 _reset:
-	@${PYTHON} ${TOOLBOX_DIR}/reset.py
+	@${PYTHON} ${RESET_FILE}
 
 _tests:
-	@${PYTHON} ${SIMULAQRON_DIR}/tests_run.py --quick
+	@${PYTHON} -m pytest ${TEST_DIR}/quick
 
 tests: _tests _reset
 
 _tests_all:
-	@${PYTHON} ${SIMULAQRON_DIR}/tests_run.py --full
+	@${PYTHON} -m pytest ${TEST_DIR}
 
 tests_all: _tests_all _reset
 
+install: test-deps
+	@$(PYTHON) -m pip install -e . ${PIP_FLAGS}
+
 _verified:
 	@echo "SimulaQron is verified!"
 
diff --git a/README.md b/README.md
index 68a8235..aa7858c 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 [![Build Status](https://travis-ci.com/SoftwareQuTech/SimulaQron.svg?branch=Develop)](https://travis-ci.com/SoftwareQuTech/SimulaQron)
 
-SimulaQron - simple quantum network simulator (3.0.16)
+SimulaQron - simple quantum network simulator (4.0.0)
 =====================================================
 
 The purpose of this simulator of quantum network nodes is to allow you to develop new applications for
diff --git a/examples/README.md b/examples/README.md
deleted file mode 100644
index e2af155..0000000
--- a/examples/README.md
+++ /dev/null
@@ -1,1 +0,0 @@
-Examples on how to program SimulaQron using the python library for CQC can be found in the repo [https://github.com/SoftwareQuTech/CQC-Python]().
diff --git a/examples/nativeMode/teleport/aliceTest.py b/examples/nativeMode/teleport/aliceTest.py
index 0660f63..ca12f34 100644
--- a/examples/nativeMode/teleport/aliceTest.py
+++ b/examples/nativeMode/teleport/aliceTest.py
@@ -31,8 +31,8 @@
 import numpy as np
 
 from simulaqron.local.setup import setup_local, assemble_qubit
-from simulaqron.general.hostConfig import socketsConfig
-from simulaqron.toolbox.stabilizerStates import StabilizerState
+from simulaqron.general.host_config import socketsConfig
+from simulaqron.toolbox.stabilizer_states import StabilizerState
 from simulaqron.settings import simulaqron_settings
 from twisted.internet.defer import inlineCallbacks
 from twisted.spread import pb
@@ -69,17 +69,17 @@ def runClientNode(qReg, virtRoot, myName, classicalNet):
     yield q1.callRemote("apply_H")
 
     # For information purposes, let's print the state of that qubit
-    if simulaqron_settings.backend == "qutip":
+    if simulaqron_settings.sim_backend == "qutip":
         realRho, imagRho = yield q1.callRemote("get_qubit")
         state = np.array(assemble_qubit(realRho, imagRho), dtype=complex)
-    elif simulaqron_settings.backend == "projectq":
+    elif simulaqron_settings.sim_backend == "projectq":
         realvec, imagvec = yield virtRoot.callRemote("get_register_RI", q1)
         state = [r + (1j * j) for r, j in zip(realvec, imagvec)]
-    elif simulaqron_settings.backend == "stabilizer":
+    elif simulaqron_settings.sim_backend == "stabilizer":
         array, _ = yield virtRoot.callRemote("get_register_RI", q1)
         state = StabilizerState(array)
     else:
-        ValueError("Unknown backend {}".format(simulaqron_settings.backend))
+        ValueError("Unknown backend {}".format(simulaqron_settings.sim_backend))
 
     print("Qubit to be teleported is:\n{}".format(state))
 
diff --git a/examples/nativeMode/teleport/bobTest.py b/examples/nativeMode/teleport/bobTest.py
index fb4b832..3ed10d4 100644
--- a/examples/nativeMode/teleport/bobTest.py
+++ b/examples/nativeMode/teleport/bobTest.py
@@ -111,18 +111,18 @@ def remote_recover_teleport(self, a, b, virtualNum):
             yield eprB.callRemote("apply_Z")
 
         # Just print the qubit we received
-        if simulaqron_settings.backend == "qutip":
+        if simulaqron_settings.sim_backend == "qutip":
             print("here")
             (realRho, imagRho) = yield eprB.callRemote("get_qubit")
             state = np.array(assemble_qubit(realRho, imagRho), dtype=complex)
-        elif simulaqron_settings.backend == "projectq":
+        elif simulaqron_settings.sim_backend == "projectq":
             realvec, imagvec = yield self.virtRoot.callRemote("get_register_RI", eprB)
             state = [r + (1j * j) for r, j in zip(realvec, imagvec)]
-        elif simulaqron_settings.backend == "stabilizer":
+        elif simulaqron_settings.sim_backend == "stabilizer":
             array, _, = yield self.virtRoot.callRemote("get_register_RI", eprB)
             state = StabilizerState(array)
         else:
-            ValueError("Unknown backend {}".format(simulaqron_settings.backend))
+            ValueError("Unknown backend {}".format(simulaqron_settings.sim_backend))
 
         print("Qubit is:\n{}".format(state))
 
diff --git a/optional-requirements.txt b/optional-requirements.txt
new file mode 100644
index 0000000..7166df9
--- /dev/null
+++ b/optional-requirements.txt
@@ -0,0 +1,2 @@
+projectq
+qutip
diff --git a/requirements.txt b/requirements.txt
index ffd33a6..74dd0ef 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,6 @@ numpy>=1.14.0,<2.0.0
 scipy>=1.1.0,<2.0.0
 twisted>=20.3.0,<21.0.0
 networkx>=2.2,<3.0
-flake8>=3.6.0,<4.0.0
 click>=7.0,<8.0
 daemons>=1.3.0,<2.0.0
-cqc>=3.1.0,<4.0.0
+netqasm>=0.8.2,<0.9.0
diff --git a/setup.py b/setup.py
index 46450c3..6958b1b 100644
--- a/setup.py
+++ b/setup.py
@@ -31,9 +31,9 @@
     long_description_content_type="text/markdown",
     url="https://github.com/SoftwareQuTech/SimulaQron",
     include_package_data=True,
-    packages=setuptools.find_packages(include=("simulaqron*",)),
+    packages=setuptools.find_packages(exclude=('tests', 'docs', 'examples')),
     package_data={
-        'simulaqron': ['config/.keep', '.simulaqron_pids/.keep', 'tests/unittests/slow/merges/configs/*.cfg']
+        'simulaqron': ['config/.keep', '.simulaqron_pids/.keep']
     },
     install_requires=install_requires,
     python_requires='>=3.5',
@@ -48,6 +48,6 @@
     ],
     entry_points='''
         [console_scripts]
-        simulaqron=simulaqron.SimulaQron:cli
+        simulaqron=simulaqron.simulaqron:cli
     '''
 )
diff --git a/simulaqron/__init__.py b/simulaqron/__init__.py
index 1147bc8..ff67685 100644
--- a/simulaqron/__init__.py
+++ b/simulaqron/__init__.py
@@ -1,4 +1,6 @@
-from simulaqron.tests_run import main as tests
+from simulaqron.toolbox.reset import check_config_files
 
-__all__ = ['tests']
-__version__ = '3.0.16'
+__version__ = '4.0.0'
+
+# Create default config files if there are none
+check_config_files()
diff --git a/simulaqron/cqc_backend/cqcFactory.py b/simulaqron/cqc_backend/cqcFactory.py
deleted file mode 100644
index 88c585a..0000000
--- a/simulaqron/cqc_backend/cqcFactory.py
+++ /dev/null
@@ -1,151 +0,0 @@
-# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import json
-import logging
-
-from twisted.internet.defer import DeferredLock
-from twisted.internet.protocol import Factory
-
-from simulaqron.settings import simulaqron_settings
-from simulaqron.toolbox.manage_nodes import NetworksConfigConstructor
-
-from cqc.Protocol import CQCProtocol
-
-
-###############################################################################
-#
-# CQC Factory
-#
-# Twisted factory for the CQC protocol
-#
-
-
-class CQCFactory(Factory):
-    def __init__(self, host, name, cqc_net, backend, network_name="default"):
-        """
-        Initialize CQC Factory.
-
-        lhost	details of the local host (class host)
-        """
-
-        self.host = host
-        self.name = name
-        self.cqcNet = cqc_net
-        self.virtRoot = None
-        self.qReg = None
-        self.backend = backend(self)
-        self.network_name = network_name
-
-        # Dictionary that keeps qubit dictorionaries for each application
-        self.qubitList = {}
-
-        # Lock governing access to the qubitList
-        self._lock = DeferredLock()
-
-        # Read in topology, if specified. topology=None means fully connected
-        # topology
-        self.topology = None
-        if simulaqron_settings.topology_file is not None and simulaqron_settings.topology_file != "":
-            self._setup_topology(simulaqron_settings.topology_file)
-        else:
-            if simulaqron_settings.network_config_file is not None:
-                networks_config = NetworksConfigConstructor(file_path=simulaqron_settings.network_config_file)
-                self.topology = networks_config.networks[network_name].topology
-
-    def buildProtocol(self, addr):
-        """
-        Return an instance of CQCProtocol when a connection is made.
-        """
-        return CQCProtocol(self)
-
-    def set_virtual_node(self, virtRoot):
-        """
-        Set the virtual root allowing connections to the SimulaQron backend.
-        """
-        self.virtRoot = virtRoot
-
-    def lookup(self, ip, port):
-        """
-        Lookup name of remote host used within SimulaQron given ip and
-        portnumber.
-        """
-        for entry in self.cqcNet.hostDict:
-            node = self.cqcNet.hostDict[entry]
-            if (node.ip == ip) and (node.port == port):
-                return node.name
-
-        logging.debug("CQC %s: No such node", self.name)
-        return None
-
-    def _setup_topology(self, topology_file):
-        """
-        Sets up the topology, if specified.
-        :param topology_file: str
-            The relative path to the json-file defining the topology. It will
-            be assumed that the absolute path to the file is
-            $simulaqron_path/topology_file.
-            If topology is an empty string then a fully connected topology will
-            be used.
-        :return: None
-        """
-        try:
-            with open(topology_file, "r") as top_file:
-                try:
-                    self.topology = json.load(top_file)
-                except json.JSONDecodeError:
-                    raise RuntimeError("Could not parse the json file: {}".format(topology_file))
-        except FileNotFoundError:
-            raise FileNotFoundError("Could not find the file specifying the topology:" " {}".format(topology_file))
-        except IsADirectoryError:
-            raise FileNotFoundError("Could not find the file specifying the topology: " "{}".format(topology_file))
-
-    def is_adjacent(self, remote_host_name):
-        """
-        Checks if remote host is adjacent to this node, according to the
-        specified topology.
-
-        :param remote_host_name: str
-            The name of the remote host
-        :return:
-        """
-        # Check if a topology is defined, otherwise use fully connected
-        if self.topology is None:
-            return True
-
-        if self.name in self.topology:
-            if remote_host_name in self.topology[self.name]:
-                return True
-            else:
-                return False
-        else:
-            logging.warning(
-                "Node {} is not in the specified topology and is therefore "
-                "assumed to have no neighbors".format(self.name)
-            )
-            return False
diff --git a/simulaqron/cqc_backend/cqcMessageHandler.py b/simulaqron/cqc_backend/cqcMessageHandler.py
deleted file mode 100644
index 39b457a..0000000
--- a/simulaqron/cqc_backend/cqcMessageHandler.py
+++ /dev/null
@@ -1,1375 +0,0 @@
-# Copyright (c) 2017-2018, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from heapq import heappush, heappop
-from collections import defaultdict
-import logging
-import numpy as np
-import time
-from twisted.internet.defer import inlineCallbacks
-from twisted.spread.pb import RemoteError
-
-from cqc.cqcHeader import (
-    CQCCmdHeader,
-    CQC_TP_HELLO,
-    CQC_CMD_H,
-    CQCXtraQubitHeader,
-    CQC_XTRA_QUBIT_HDR_LENGTH,
-    CQC_ERR_UNSUPP,
-    CQC_ERR_GENERAL,
-    CQC_CMD_HDR_LENGTH,
-    CQC_TP_INF_TIME,
-    CQC_NOTIFY_LENGTH,
-    CQC_ERR_NOQUBIT,
-    CQCNotifyHeader,
-    CQCMeasOutHeader,
-    CQC_MEAS_OUT_HDR_LENGTH,
-    CQCTimeinfoHeader,
-    CQC_TIMEINFO_HDR_LENGTH,
-    CQC_TP_MEASOUT,
-    CQC_ERR_TIMEOUT,
-    CQC_ERR_INUSE,
-    CQC_TP_RECV,
-    CQC_TP_EPR_OK,
-    CQC_TP_NEW_OK,
-)
-from cqc.entInfoHeader import EntInfoHeader, ENT_INFO_LENGTH
-from cqc.MessageHandler import CQCMessageHandler, UnknownQubitError
-
-from simulaqron.virtNode.basics import quantumError, noQubitError
-from simulaqron.settings import simulaqron_settings
-
-
-class SimulaqronCQCHandler(CQCMessageHandler):
-    # Dictionary storing the next unique qubit id for each used app_id
-    _available_q_ids = {}
-
-    # Dictionary storing the next unique entanglement id for each used (host_app_id,remote_node,remote_app_id)
-    _next_ent_id = {}
-
-    def __init__(self, factory):
-        super().__init__(factory)
-        self.factory = factory
-
-        # Dictionary that keeps qubit dictorionaries for each application
-        # TODO this is in factory right?
-        self.qubitList = {}
-
-    @staticmethod
-    def get_error_class(remote_err):
-        """
-        This is a function to get the error class of a remote thrown error when using callRemote.
-        :param remote_err: :obj:`twisted.spread.pb.RemoteError`
-        :return: class
-        """
-        # Get name of remote error
-        error_name = remote_err.remoteType.split(b".")[-1].decode()
-
-        # Get class of remote error
-        error_class = eval(error_name)
-
-        return error_class
-
-    def handle_hello(self, header, data):
-        """
-        Hello just requires us to return hello - for testing availability.
-        """
-        qubits = defaultdict(list)
-        for appID, qID in self.factory.qubitList.keys():
-            qubits[appID].append(qID)
-
-        to_print = "Hello! I'm node {} with the following qubits:\n".format(self.name)
-        for appID, qIDs in qubits.items():
-            to_print += "    App ID {}: {}\n".format(appID, qIDs)
-        logging.info(to_print[:-1])
-        msg = self.create_return_message(header.app_id, CQC_TP_HELLO, cqc_version=header.version)
-        self.return_messages[header.app_id].append(msg)
-
-    def handle_time(self, header, data):
-
-        # Read the command header to learn the qubit ID
-        raw_cmd_header = data[:CQC_CMD_HDR_LENGTH]
-        cmd_hdr = CQCCmdHeader(raw_cmd_header)
-
-        # Get the qubit list
-        q_list = self.factory.qubitList
-
-        # Lookup the desired qubit
-        if (header.app_id, cmd_hdr.qubit_id) in q_list:
-            q = q_list[(header.app_id, cmd_hdr.qubit_id)]
-        else:
-            # Specified qubit is unknown
-            self.return_messages[header.app_id].append(
-                self.create_return_message(header.app_id, CQC_ERR_NOQUBIT, cqc_version=header.version))
-            return False
-
-        # Craft reply
-        # First send an appropriate CQC Header
-        if header.version < 2:
-            length = CQC_NOTIFY_LENGTH
-        else:
-            length = CQC_TIMEINFO_HDR_LENGTH
-        cqc_msg = self.create_return_message(header.app_id, CQC_TP_INF_TIME, length=length, cqc_version=header.version)
-        self.return_messages[header.app_id].append(cqc_msg)
-
-        # Then we send a notify header with the timing details
-        if header.version < 2:
-            hdr = CQCNotifyHeader()
-            hdr.setVals(cmd_hdr.qubit_id, 0, 0, 0, 0, q.timestamp)
-        else:
-            hdr = CQCTimeinfoHeader()
-            hdr.setVals(q.timestamp)
-        msg = hdr.pack()
-        self.return_messages[header.app_id].append(msg)
-
-    def cmd_i(self, cqc_header, cmd, xtra):
-        """
-        Do nothing. In reality we would wait a timestep but in SimulaQron we just do nothing.
-        """
-        logging.debug("CQC %s: Doing Nothing to App ID %d qubit id %d", self.name, cqc_header.app_id, cmd.qubit_id)
-
-    def cmd_x(self, cqc_header, cmd, xtra):
-        """
-        Apply X Gate
-        """
-        try:
-            return self.apply_single_qubit_gate(cqc_header, cmd.qubit_id, "apply_X")
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a X gate to qubit {}: Error: {}".format(
-                    cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    def cmd_y(self, cqc_header, cmd, xtra):
-        """
-        Apply Y Gate
-        """
-        try:
-            return self.apply_single_qubit_gate(cqc_header, cmd.qubit_id, "apply_Y")
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a Y gate to qubit {}: Error: {}".format(
-                    cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    def cmd_z(self, cqc_header, cmd, xtra):
-        """
-        Apply Z Gate
-        """
-        try:
-            return self.apply_single_qubit_gate(cqc_header, cmd.qubit_id, "apply_Z")
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a Z gate to qubit {}: Error: {}".format(
-                    cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    def cmd_t(self, cqc_header, cmd, xtra):
-        """
-        Apply T Gate
-        """
-        try:
-            return self.apply_single_qubit_gate(cqc_header, cmd.qubit_id, "apply_T")
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a T gate to qubit {}: Error: {}".format(
-                    cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    def cmd_h(self, cqc_header, cmd, xtra):
-        """
-        Apply H Gate
-        """
-        try:
-            return self.apply_single_qubit_gate(cqc_header, cmd.qubit_id, "apply_H")
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a H gate to qubit {}: Error: {}".format(
-                    cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    def cmd_k(self, cqc_header, cmd, xtra):
-        """
-        Apply K Gate
-        """
-        try:
-            return self.apply_single_qubit_gate(cqc_header, cmd.qubit_id, "apply_K")
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a K gate to qubit {}: Error: {}".format(
-                    cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    @inlineCallbacks
-    def apply_rotation(self, cqc_header, cmd, xtra, axis):
-        """
-        Apply a rotation of the qubit specified in cmd with an angle specified in xtra
-        around the axis
-        """
-        logging.debug(
-            "CQC %s: Applying a rotation around %s to App ID %d qubit id %d",
-            self.name,
-            axis,
-            cqc_header.app_id,
-            cmd.qubit_id,
-        )
-        try:
-            virt_qubit = self.get_virt_qubit(cqc_header, cmd.qubit_id)
-        except UnknownQubitError as e:
-            logging.debug(e)
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT, cqc_version=cqc_header.version))
-            return False
-        try:
-            success = yield virt_qubit.callRemote("apply_rotation", axis, 2 * np.pi / 256 * xtra.step)
-            if success is False:
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-                return False
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a rotation gate to qubit {}: Error: {}".format(
-                    cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-        return True
-
-    def cmd_rotx(self, cqc_header, cmd, xtra):
-        """
-        Rotate around x axis
-        """
-        try:
-            return self.apply_rotation(cqc_header, cmd, xtra, [1, 0, 0])
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a ROTX gate to qubit {}: Error: {}".format(
-                    cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    def cmd_roty(self, cqc_header, cmd, xtra):
-        """
-        Rotate around y axis
-        """
-        try:
-            return self.apply_rotation(cqc_header, cmd, xtra, [0, 1, 0])
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a ROTY gate to qubit {}: Error: {}".format(
-                    cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    def cmd_rotz(self, cqc_header, cmd, xtra):
-        """
-        Rotate around z axis
-        """
-        try:
-            return self.apply_rotation(cqc_header, cmd, xtra, [0, 0, 1])
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a ROTZ gate to qubit {}: Error: {}".format(
-                    cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    def cmd_cnot(self, cqc_header, cmd, xtra):
-        """
-        Apply CNOT Gate
-        """
-        try:
-            return self.apply_two_qubit_gate(cqc_header, cmd, xtra, "cnot_onto")
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a CNOT gate to qubit {} and {}: Error: {}".format(
-                    cmd.qubit_id, xtra.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    def cmd_cphase(self, cqc_header, cmd, xtra):
-        """
-        Apply CPHASE Gate
-        """
-        try:
-            return self.apply_two_qubit_gate(cqc_header, cmd, xtra, "cphase_onto")
-        except Exception as err:
-            logging.error(
-                "Following error occurred when trying to apply a CPHASE gate to qubit {} and {}: Error: {}".format(
-                    cmd.qubit_id, xtra.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-    @inlineCallbacks
-    def cmd_measure(self, cqc_header, cmd, xtra, inplace=False):
-        """
-        Measure
-        """
-        logging.debug("CQC %s: Measuring App ID %d qubit id %d", self.name, cqc_header.app_id, cmd.qubit_id)
-        try:
-            virt_qubit = self.get_virt_qubit(cqc_header, cmd.qubit_id)
-        except UnknownQubitError as e:
-            logging.warning(e)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-        try:
-            outcome = yield virt_qubit.callRemote("measure", inplace)
-        except Exception as err:
-            logging.error(
-                "CQC {}: Got the following unexpected error when trying to measure qubit {}: {}".format(
-                    self.name, cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-        if outcome is None:
-            logging.warning("CQC %s: Measurement failed", self.name)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        logging.debug("CQC %s: Measured outcome %d", self.name, outcome)
-
-        # Store the outcome in self.references under the reference specified in the CQCAssignHeader
-        self.references[cqc_header.app_id][xtra.ref_id] = outcome
-
-        # Send the outcome back as MEASOUT
-        if cqc_header.version < 2:
-            length = CQC_NOTIFY_LENGTH
-        else:
-            length = CQC_MEAS_OUT_HDR_LENGTH
-        cqc_msg = self.create_return_message(cqc_header.app_id, CQC_TP_MEASOUT, length=length,
-                                             cqc_version=cqc_header.version)
-        self.return_messages[cqc_header.app_id].append(cqc_msg)
-
-        # Send notify header with outcome
-        if cqc_header.version < 2:
-            hdr = CQCNotifyHeader()
-            hdr.setVals(cmd.qubit_id, outcome, 0, 0, 0, 0)
-        else:
-            hdr = CQCMeasOutHeader()
-            hdr.setVals(outcome=outcome)
-        msg = hdr.pack()
-        self.return_messages[cqc_header.app_id].append(msg)
-        # self.protocol.transport.write(msg)
-        logging.debug("CQC %s: Notify %s", self.name, hdr)
-
-        if not inplace:
-            # Remove from active mapped qubits
-            self.remove_qubit_id(cqc_header.app_id, cmd.qubit_id)
-
-        return True
-
-    def cmd_measure_inplace(self, cqc_header, cmd, xtra):
-        return self.cmd_measure(cqc_header, cmd, xtra, inplace=True)
-
-    @inlineCallbacks
-    def cmd_reset(self, cqc_header, cmd, xtra):
-        """
-        Reset Qubit to \|0\>
-        """
-        logging.debug("CQC %s: Reset App ID %d qubit id %d", self.name, cqc_header.app_id, cmd.qubit_id)
-        try:
-            virt_qubit = self.get_virt_qubit(cqc_header, cmd.qubit_id)
-        except UnknownQubitError as e:
-            logging.debug(e)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        try:
-            outcome = yield virt_qubit.callRemote("measure", inplace=True)
-        except Exception as err:
-            logging.error(
-                "CQC {}: Got the following unexpected error when trying to reset qubit {}: {}".format(
-                    self.name, cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-        # If state is |1> do correction
-        if outcome:
-            try:
-                yield virt_qubit.callRemote("apply_X")
-            except Exception as err:
-                logging.error(
-                    "CQC {}: Got the following unexpected error when trying to correct a the reset qubit {}: {}".format(
-                        self.name, cmd.qubit_id, err
-                    )
-                )
-                self.return_messages[cqc_header.app_id].append(
-                    self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-                return False
-        return True
-
-    @inlineCallbacks
-    def cmd_send(self, cqc_header, cmd, xtra):
-        """
-        Send qubit to another node.
-        """
-        # Lookup the name of the remote node used within SimulaQron
-        target_name = self.factory.lookup(xtra.remote_node, xtra.remote_port)
-        if target_name is None:
-            logging.warning("CQC %s: Remote node not found %s", self.name, xtra)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        # Check so that it is not the same node
-        if self.name == target_name:
-            logging.warning("CQC %s: Trying to send from node to itself.", self.name)
-            # self.protocol._send_back_cqc(cqc_header, CQC_ERR_GENERAL)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        # Check that other node is adjacent to us
-        if not self.factory.is_adjacent(target_name):
-            logging.warning(
-                "CQC {}: Node {} is not adjacent to {} in the specified topology.".format(
-                    self.name, target_name, self.name
-                )
-            )
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        # Lookup the virtual qubit from identifier
-        try:
-            virt_num = yield self.get_virt_qubit_indep(cqc_header, cmd.qubit_id)
-        except UnknownQubitError as e:
-            logging.debug(e)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        # Send instruction to transfer the qubit
-        try:
-            yield self.factory.virtRoot.callRemote(
-                "cqc_send_qubit", virt_num, target_name, cqc_header.app_id, xtra.remote_app_id
-            )
-        except RemoteError as remote_err:
-            error_class = self.get_error_class(remote_err)
-            if error_class == noQubitError:
-                logging.error("CQC {}: Trying to send qubit but remote node is out of qubits".format(self.name))
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-                return False
-            elif error_class == quantumError:
-                logging.error("CQC {}: Unknown quantum error occurred when trying to send qubit.".format(self.name))
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-                return False
-            else:
-                logging.error(
-                    "CQC {}: Got the following unexpected error when trying to send qubit {}: {}".format(
-                        self.name, cmd.qubit_id, remote_err
-                    )
-                )
-                self.return_messages[cqc_header.app_id].append(
-                    self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-                return False
-        except Exception as err:
-            logging.error(
-                "CQC {}: Got the following unexpected error when trying to send qubit {}: {}".format(
-                    self.name, cmd.qubit_id, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-        logging.debug(
-            "CQC %s: Sent App ID %d qubit id %d to %s", self.name, cqc_header.app_id, cmd.qubit_id, target_name
-        )
-
-        # Remove from active mapped qubits
-        self.remove_qubit_id(cqc_header.app_id, cmd.qubit_id)
-        return True
-
-    @inlineCallbacks
-    def cmd_recv(self, cqc_header, cmd, xtra):
-        """
-        Receive qubit from another node. Block until qubit is received.
-        """
-        logging.debug("CQC %s: Asking to receive for App ID %d", self.name, cqc_header.app_id)
-
-        # First get the app_id
-        app_id = cqc_header.app_id
-
-        # This will block until a qubit is received.
-        no_qubit = True
-        virt_qubit = None
-        # recv_timeout is in 100ms (for legacy reasons there are no plans to change it to seconds)
-        sleep_time = simulaqron_settings.recv_retry_time  # seconds
-        for _ in range(int(simulaqron_settings.recv_timeout * 0.1 / sleep_time)):
-            try:
-                virt_qubit = yield self.factory.virtRoot.callRemote("cqc_get_recv", cqc_header.app_id)
-            except RemoteError as remote_err:
-                error_class = self.get_error_class(remote_err)
-                if error_class == quantumError:
-                    logging.error("CQC {}: Unknown quantum error occurred when trying to recv qubit.".format(self.name))
-                    err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL,
-                                                         cqc_version=cqc_header.version)
-                    self.return_messages[cqc_header.app_id].append(err_msg)
-                    return False
-                else:
-                    logging.error(
-                        "CQC {}: Got the following unexpected error when trying to recv a qubit: {}".format(
-                            self.name, remote_err
-                        )
-                    )
-                    self.return_messages[cqc_header.app_id].append(
-                        self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-                    return False
-            except Exception as err:
-                logging.error(
-                    "CQC {}: Got the following unexpected error when trying to recv a qubit: {}".format(self.name, err)
-                )
-                self.return_messages[cqc_header.app_id].append(
-                    self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-                return False
-
-            if virt_qubit:
-                no_qubit = False
-                break
-            else:
-                time.sleep(sleep_time)
-        if no_qubit:
-            logging.debug("CQC %s: TIMEOUT, no qubit received.", self.name)
-            # self.protocol._send_back_cqc(cqc_header, CQC_ERR_TIMEOUT)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_TIMEOUT, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        logging.debug("CQC %s: Qubit received for app_id %d", self.name, cqc_header.app_id)
-
-        # Once we have the qubit, add it to the local list and send a reply we received it. Note that we will
-        # recheck whether it exists: it could have been added by another connection in the mean time
-        try:
-            self.factory._lock.acquire()
-
-            # Get new qubit ID
-            q_id = self.new_qubit_id(app_id)
-
-            if (app_id, q_id) in self.factory.qubitList:
-                logging.debug("CQC %s: Qubit already in use (%d,%d)", self.name, app_id, q_id)
-                # self.protocol._send_back_cqc(cqc_header, CQC_ERR_INUSE)
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_INUSE, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-                return
-
-            q = CQCQubit(q_id, int(time.time()), virt_qubit)
-            self.factory.qubitList[(app_id, q_id)] = q
-        finally:
-            self.factory._lock.release()
-
-        # Send message we received a qubit back
-        if cqc_header.version < 2:
-            length = CQC_NOTIFY_LENGTH
-        else:
-            length = CQC_XTRA_QUBIT_HDR_LENGTH
-        recv_msg = self.create_return_message(cqc_header.app_id, CQC_TP_RECV, length=length,
-                                              cqc_version=cqc_header.version)
-        self.return_messages[cqc_header.app_id].append(recv_msg)
-
-        # Send notify header with qubit ID
-        if cqc_header.version < 2:
-            hdr = CQCNotifyHeader()
-            hdr.setVals(q_id, 0, 0, 0, 0, 0)
-        else:
-            hdr = CQCXtraQubitHeader()
-            hdr.setVals(qubit_id=q_id)
-        msg = hdr.pack()
-        # self.protocol.transport.write(msg)
-        logging.debug("CQC %s: Notify %s", self.name, hdr)
-        self.return_messages[cqc_header.app_id].append(msg)
-        return True
-
-    @inlineCallbacks
-    def cmd_epr(self, cqc_header, cmd, xtra):
-        """
-        Create EPR pair with another node.
-        Depending on the ips and ports this will either create an EPR-pair and send one part, or just receive.
-        """
-        # Get ip and port of this host
-        host_node = self.factory.host.ip
-        host_port = self.factory.host.port
-        host_app_id = cqc_header.app_id
-
-        # Get ip and port of remote host
-        remote_node = xtra.remote_node
-        remote_port = xtra.remote_port
-        remote_app_id = xtra.remote_app_id
-
-        # Lookup the name of the remote node used within SimulaQron
-        target_name = self.factory.lookup(remote_node, remote_port)
-        if target_name is None:
-            logging.warning("CQC %s: Remote node not found %s", self.name, xtra)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        # Check so that it is not the same node
-        if self.name == target_name:
-            logging.warning("CQC %s: Trying to create EPR from node to itself.", self.name)
-            # self.protocol._send_back_cqc(cqc_header, CQC_ERR_GENERAL)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        # Check that other node is adjacent to us
-        if not self.factory.is_adjacent(target_name):
-            logging.warning(
-                "CQC {}: Node {} is not adjacent to {} in the specified topology.".format(
-                    self.name, target_name, self.name
-                )
-            )
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        # Create the first qubit
-        try:
-            (succ, q_id1) = yield self.cmd_new(cqc_header, cmd, xtra, return_q_id=True)
-        except RemoteError as remote_err:
-            error_class = self.get_error_class(remote_err)
-            succ = False
-            if error_class == noQubitError:
-                logging.error("CQC {}: Trying to create qubit for EPR but out of qubits".format(self.name))
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-            elif error_class == quantumError:
-                logging.error(
-                    "CQC {}: Unknown quantum error occurred when trying to create qubit for EPR.".format(self.name)
-                )
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-            else:
-                logging.error(
-                    "CQC {}: Got the following unexpected error when trying to create qubit for EPR: {}".format(
-                        self.name, remote_err
-                    )
-                )
-                self.return_messages[cqc_header.app_id].append(
-                    self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-        except Exception as err:
-            logging.error(
-                "CQC {}: Got the following unexpected error when trying to create qubit for EPR: {}".format(
-                    self.name, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-        if not succ:
-            return False
-
-        # Create the second qubit
-        try:
-            (succ, q_id2) = yield self.cmd_new(cqc_header, cmd, xtra, return_q_id=True, ignore_max_qubits=True)
-        except Exception as err:
-            logging.error(
-                "CQC {}: Got the following unexpected error when trying to create qubit for EPR: {}".format(
-                    self.name, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-        if not succ:
-            # Failed to create the second qubit, destroy the first
-            logging.warning("CQC %s: Failed to create second qubit, destroying the first", self.name)
-            try:
-                cmd1 = CQCCmdHeader()
-                cmd1.setVals(q_id1, 0, 0, 0, 0)
-                yield self.cmd_release(cqc_header, cmd1, None)
-            except Exception as err:
-                logging.warning("CQC %s: Failed to destroy qubits", self.name)
-                logging.error(err)
-            return False
-
-        # Create headers for qubits
-        cmd1 = CQCCmdHeader()
-        cmd1.setVals(q_id1, CQC_CMD_H, 0, 0, 0)
-
-        cmd2 = CQCCmdHeader()
-        cmd2.setVals(q_id2, 0, 0, 0, 0)
-
-        xtra_cnot = CQCXtraQubitHeader()
-        xtra_cnot.setVals(q_id2)
-
-        # Produce EPR-pair
-        try:
-            yield self.cmd_h(cqc_header, cmd1, None)
-        except Exception as err:
-            logging.error(
-                "CQC {}: Got the following unexpected error when trying to apply H to EPR qubit: {}".format(
-                    self.name, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            succ = False
-
-        if not succ:
-            # Failed to perform H, destroy qubits
-            logging.warning("CQC %s: Failed to perform H, destroying qubits", self.name)
-            try:
-                yield self.cmd_release(cqc_header, cmd1, None)
-            except Exception as err:
-                logging.warning("CQC %s: Failed to destroy qubits", self.name)
-                logging.error(err)
-            try:
-                yield self.cmd_release(cqc_header, cmd2, None)
-            except Exception as err:
-                logging.warning("CQC %s: Failed to destroy qubits", self.name)
-                logging.error(err)
-            return False
-
-        try:
-            yield self.cmd_cnot(cqc_header, cmd1, xtra_cnot)
-        except Exception as err:
-            logging.error(
-                "CQC {}: Got the following unexpected error when trying to apply CNOT to EPR qubit: {}".format(
-                    self.name, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            succ = False
-
-        if not succ:
-            # Failed to perform CNOT, destroy qubits
-            logging.warning("CQC %s: Failed to perform CNOT, destroying qubits", self.name)
-            try:
-                yield self.cmd_release(cqc_header, cmd1, None)
-            except Exception as err:
-                logging.warning("CQC %s: Failed to destroy qubits", self.name)
-                logging.error(err)
-            try:
-                yield self.cmd_release(cqc_header, cmd2, None)
-            except Exception as err:
-                logging.warning("CQC %s: Failed to destroy qubits", self.name)
-                logging.error(err)
-            return False
-
-        # Get entanglement id XXX lock here?
-        ent_id = self.new_ent_id(host_app_id, remote_node, remote_app_id)
-
-        # Prepare ent_info header with entanglement information
-        ent_info = EntInfoHeader()
-        ent_info.setVals(
-            host_node,
-            host_port,
-            host_app_id,
-            remote_node,
-            remote_port,
-            remote_app_id,
-            ent_id,
-            int(time.time()),
-            int(time.time()),
-            0,
-            1,
-        )
-        # Send second qubit
-        try:
-            succ = yield self.send_epr_half(cqc_header, cmd2, xtra, ent_info)
-        except RemoteError as remote_err:
-            error_class = self.get_error_class(remote_err)
-            succ = False
-            if error_class == noQubitError:
-                logging.error("CQC {}: Trying to send qubit of EPR but remote node is out of qubits".format(self.name))
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-            elif error_class == quantumError:
-                logging.error(
-                    "CQC {}: Unknown quantum error occurred when trying to send qubit of EPR.".format(self.name)
-                )
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-            else:
-                logging.error(
-                    "CQC {}: Got the following unexpected error when trying to send EPR qubit: {}".format(
-                        self.name, remote_err
-                    )
-                )
-                self.return_messages[cqc_header.app_id].append(
-                    self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-        except Exception as err:
-            logging.error(
-                "CQC {}: Got the following unexpected error when trying to send EPR qubit: {}".format(self.name, err)
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-
-        if not succ:
-            # Failed to send the qubit, destroy it instead
-            logging.warning("CQC %s: Failed to send epr qubit, destroying qubits", self.name)
-            try:
-                yield self.cmd_release(cqc_header, cmd1, None)
-            except Exception as err:
-                logging.warning("CQC %s: Failed to destroy qubits", self.name)
-                logging.error(err)
-            try:
-                yield self.cmd_release(cqc_header, cmd2, None)
-            except Exception as err:
-                logging.warning("CQC %s: Failed to destroy qubits", self.name)
-                logging.error(err)
-            return False
-
-        # Send message we created EPR pair
-        if cqc_header.version < 2:
-            length = CQC_NOTIFY_LENGTH + ENT_INFO_LENGTH
-        else:
-            length = CQC_XTRA_QUBIT_HDR_LENGTH + ENT_INFO_LENGTH
-        msg_ok = self.create_return_message(
-            cqc_header.app_id, CQC_TP_EPR_OK, length=length, cqc_version=cqc_header.version
-        )
-
-        self.return_messages[cqc_header.app_id].append(msg_ok)
-
-        # Send notify header with qubit ID
-        if cqc_header.version < 2:
-            hdr = CQCNotifyHeader()
-            hdr.setVals(q_id1, 0, 0, 0, 0, 0)
-        else:
-            hdr = CQCXtraQubitHeader()
-            hdr.setVals(qubit_id=q_id1)
-        msg = hdr.pack()
-        self.return_messages[cqc_header.app_id].append(msg)
-
-        logging.debug("CQC %s: Notify %s", self.name, hdr)
-
-        # Send entanglement info
-        msg_ent_info = ent_info.pack()
-        self.return_messages[cqc_header.app_id].append(msg_ent_info)
-        logging.debug("CQC %s: Entanglement information %s", self.name, ent_info)
-
-        logging.debug("CQC %s: EPR Pair ID %d qubit id %d", self.name, cqc_header.app_id, cmd.qubit_id)
-        return True
-
-    @inlineCallbacks
-    def send_epr_half(self, cqc_header, cmd, xtra, ent_info):
-        """
-        Send qubit to another node.
-        """
-        # Lookup the virtual qubit from identifier
-        try:
-            virt_num = yield self.get_virt_qubit_indep(cqc_header, cmd.qubit_id)
-        except UnknownQubitError as e:
-            logging.debug(e)
-            return False
-        except Exception as e:
-            raise e
-
-        # Lookup the name of the remote node used within SimulaQron
-        target_name = self.factory.lookup(xtra.remote_node, xtra.remote_port)
-        if target_name is None:
-            logging.debug("CQC %s: Remote node not found %s", self.name, xtra)
-            return False
-
-        # Prepare update raw entanglement information header
-        updated_ent_info = EntInfoHeader(ent_info.pack())
-        updated_ent_info.switch_nodes()
-        raw_updated_ent_info = updated_ent_info.pack()
-        # Send instruction to transfer the qubit
-        try:
-            yield self.factory.virtRoot.callRemote(
-                "cqc_send_epr_half", virt_num, target_name, cqc_header.app_id, xtra.remote_app_id, raw_updated_ent_info
-            )
-        except Exception as e:
-            raise e
-
-        logging.debug(
-            "CQC %s: Sent App ID %d half a EPR pair as qubit id %d to %s",
-            self.name,
-            cqc_header.app_id,
-            cmd.qubit_id,
-            target_name,
-        )
-        # Remove from active mapped qubits
-        self.remove_qubit_id(cqc_header.app_id, cmd.qubit_id)
-
-        return True
-
-    @inlineCallbacks
-    def cmd_epr_recv(self, cqc_header, cmd, xtra):
-        """
-        Receive half of epr from another node. Block until qubit is received.
-        """
-        logging.debug("CQC %s: Asking to receive for App ID %d", self.name, cqc_header.app_id)
-
-        # First get the app_id and q_id
-        app_id = cqc_header.app_id
-        q_id = self.new_qubit_id(app_id)
-
-        # This will block until a qubit is received.
-        no_qubit = True
-        virt_qubit = None
-        ent_info = None
-        # recv_timeout is in 100ms (for legacy reasons there are no plans to change it to seconds)
-        sleep_time = simulaqron_settings.recv_retry_time
-        for _ in range(int(simulaqron_settings.recv_timeout * 0.1 / sleep_time)):
-            try:
-                data = yield self.factory.virtRoot.callRemote("cqc_get_epr_recv", cqc_header.app_id)
-            except RemoteError as remote_err:
-                error_class = self.get_error_class(remote_err)
-                if error_class == quantumError:
-                    logging.error(
-                        "CQC {}: Unknown quantum error occurred when trying to recv qubit of EPR.".format(self.name)
-                    )
-                    err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL,
-                                                         cqc_version=cqc_header.version)
-                    self.return_messages[cqc_header.app_id].append(err_msg)
-                    return False
-                else:
-                    logging.error(
-                        "CQC {}: Got the following unexpected error when trying to recv EPR qubit: {}".format(
-                            self.name, remote_err
-                        )
-                    )
-                    self.return_messages[cqc_header.app_id].append(
-                        self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-                    return False
-            except Exception as err:
-                logging.error(
-                    "CQC {}: Got the following unexpected error when trying to send EPR qubit: {}".format(
-                        self.name, err
-                    )
-                )
-                self.return_messages[cqc_header.app_id].append(
-                    self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-                return False
-
-            if data:
-                no_qubit = False
-                (virt_qubit, rawEntInfo) = data
-                ent_info = EntInfoHeader(rawEntInfo)
-                break
-            else:
-                time.sleep(sleep_time)
-        if no_qubit:
-            logging.debug("CQC %s: TIMEOUT, no qubit received.", self.name)
-            # self.protocol._send_back_cqc(cqc_header, CQC_ERR_TIMEOUT)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_TIMEOUT, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        logging.debug("CQC %s: Qubit received for app_id %d", self.name, cqc_header.app_id)
-
-        # Once we have the qubit, add it to the local list and send a reply we received it. Note that we will
-        # recheck whether it exists: it could have been added by another connection in the mean time
-        try:
-            self.factory._lock.acquire()
-
-            if (app_id, q_id) in self.factory.qubitList:
-                logging.debug("CQC %s: Qubit already in use (%d,%d)", self.name, app_id, q_id)
-                # self.protocol._send_back_cqc(cqc_header, CQC_ERR_INUSE)
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_INUSE, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-                return False
-
-            q = CQCQubit(q_id, int(time.time()), virt_qubit)
-            self.factory.qubitList[(app_id, q_id)] = q
-        finally:
-            self.factory._lock.release()
-
-        # Send message we received a qubit back
-        if cqc_header.version < 2:
-            length = CQC_NOTIFY_LENGTH + ENT_INFO_LENGTH
-        else:
-            length = CQC_XTRA_QUBIT_HDR_LENGTH + ENT_INFO_LENGTH
-        cqc_msg = self.create_return_message(
-            cqc_header.app_id, CQC_TP_EPR_OK, length=length, cqc_version=cqc_header.version
-        )
-        self.return_messages[cqc_header.app_id].append(cqc_msg)
-
-        # Send notify header with qubit ID
-        if cqc_header.version < 2:
-            hdr = CQCNotifyHeader()
-            hdr.setVals(q_id, 0, 0, 0, 0, 0)
-            logging.debug("CQC %s: Notify %s", self.name, hdr)
-        else:
-            hdr = CQCXtraQubitHeader()
-            hdr.setVals(qubit_id=q_id)
-            logging.debug("CQC %s: %s", self.name, hdr)
-        msg = hdr.pack()
-        self.return_messages[cqc_header.app_id].append(msg)
-
-        # Send entanglement info
-        ent_info_msg = ent_info.pack()
-        self.return_messages[cqc_header.app_id].append(ent_info_msg)
-        logging.debug("CQC %s: Entanglement information %s", self.name, ent_info)
-
-        logging.debug("CQC %s: EPR Pair ID %d qubit id %d", self.name, cqc_header.app_id, cmd.qubit_id)
-        return True
-
-    @inlineCallbacks
-    def cmd_new(self, cqc_header, cmd, xtra, return_q_id=False, ignore_max_qubits=False):
-        """
-        Request a new qubit. Since we don't need it, this python CQC just provides very crude timing information.
-        (return_q_id is used internally)
-        (ignore_max_qubits is used internally to ignore the check of number of virtual qubits at the node
-        such that the node can temporarily create a qubit for EPR creation.)
-        """
-        app_id = cqc_header.app_id
-        try:
-            self.factory._lock.acquire()
-            try:
-                virt = yield self.factory.virtRoot.callRemote("new_qubit", ignore_max_qubits=ignore_max_qubits)
-                succ = True
-            except RemoteError as remote_err:
-                error_class = self.get_error_class(remote_err)
-                succ = False
-                if error_class == noQubitError:
-                    logging.error(
-                        "CQC {}: Out of simulated qubits in register or virtual qubits in node".format(self.name)
-                    )
-                    err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT,
-                                                         cqc_version=cqc_header.version)
-                    self.return_messages[cqc_header.app_id].append(err_msg)
-                elif error_class == quantumError:
-                    logging.error(
-                        "CQC {}: Unknown quantum error occurred when trying to create new qubit.".format(self.name)
-                    )
-                    err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL,
-                                                         cqc_version=cqc_header.version)
-                    self.return_messages[cqc_header.app_id].append(err_msg)
-                else:
-                    logging.error(
-                        "CQC {}: Got the following unexpected error when trying to create new qubit: {}".format(
-                            self.name, remote_err
-                        )
-                    )
-                    self.return_messages[cqc_header.app_id].append(
-                        self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            except Exception as err:
-                succ = False
-                logging.error(
-                    "CQC {}: Got the following unexpected error when trying to create new qubit: {}".format(
-                        self.name, err
-                    )
-                )
-                self.return_messages[cqc_header.app_id].append(
-                    self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-
-            if succ:
-                q_id = self.new_qubit_id(app_id)
-                q = CQCQubit(q_id, int(time.time()), virt)
-                self.factory.qubitList[(app_id, q_id)] = q
-                logging.info("CQC %s: Requested new qubit (%d,%d)", self.name, app_id, q_id)
-
-                if not return_q_id:
-                    # Send message we created a qubit back
-                    if cqc_header.version < 2:
-                        length = CQC_NOTIFY_LENGTH
-                    else:
-                        length = CQC_XTRA_QUBIT_HDR_LENGTH
-                    cqc_msg = self.create_return_message(cqc_header.app_id, CQC_TP_NEW_OK, length=length,
-                                                         cqc_version=cqc_header.version)
-                    self.return_messages[cqc_header.app_id].append(cqc_msg)
-
-                    # Send notify header with qubit ID
-                    if cqc_header.version < 2:
-                        hdr = CQCNotifyHeader()
-                        hdr.setVals(q_id, 0, 0, 0, 0, 0)
-                        logging.info("CQC %s: Notify %s", self.name, hdr)
-                    else:
-                        hdr = CQCXtraQubitHeader()
-                        hdr.setVals(qubit_id=q_id)
-                        logging.info("CQC %s: %s", self.name, hdr)
-                    msg = hdr.pack()
-                    self.return_messages[cqc_header.app_id].append(msg)
-        finally:
-            self.factory._lock.release()
-        if return_q_id:
-            if succ:
-                return succ, q_id
-            else:
-                return succ, None
-        else:
-            return succ
-
-    @inlineCallbacks
-    def cmd_allocate(self, cqc_header, cmd, xtra):
-        """
-        Allocate multiple qubits.
-        """
-        num_qubits = cmd.qubit_id
-        cmd.qubit_id = 0
-        logging.debug("CQC %s: Allocating %d qubits", self.name, num_qubits)
-        succ = True
-        try:
-            for _ in range(num_qubits):
-                succ_tmp = yield self.cmd_new(cqc_header, cmd, xtra)
-                succ = succ and succ_tmp
-        except Exception as err:
-            logging.error(
-                "CQC {}: Got the following unexpected error when trying to create allocate qubits: {}".format(
-                    self.name, err
-                )
-            )
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-        return succ
-
-    @inlineCallbacks
-    def cmd_release(self, cqc_header, cmd, xtra):
-        """
-        Release
-        """
-        logging.debug("CQC %s: Releasing App ID %d qubit id %d", self.name, cqc_header.app_id, cmd.qubit_id)
-        try:
-            virt_qubit = self.get_virt_qubit(cqc_header, cmd.qubit_id)
-        except UnknownQubitError as err:
-            logging.debug(err)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-        except Exception as err:
-            logging.debug(err)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        try:
-            outcome = yield virt_qubit.callRemote("measure", False)
-        except Exception as err:
-            logging.error("Following unknown error occurred when trying to release qubit by measuring: {}".format(err))
-            self.return_messages[cqc_header.app_id].append(
-                self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version))
-            return False
-
-        if outcome is None:
-            logging.debug("CQC %s: Release failed", self.name)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_GENERAL, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        self.remove_qubit_id(cqc_header.app_id, cmd.qubit_id)
-        return True
-
-    @inlineCallbacks
-    def apply_single_qubit_gate(self, cqc_header, qubit_id, gate):
-        logging.debug("CQC %s: %s on App ID %d to qubit id %d", self.name, gate, cqc_header.app_id, qubit_id)
-        try:
-            virt_qubit = self.get_virt_qubit(cqc_header, qubit_id)
-        except UnknownQubitError as error:
-            logging.debug(error)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-        try:
-            success = yield virt_qubit.callRemote(gate)
-            if success is False:
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-                return False
-        except Exception as e:
-            raise e
-
-        return True
-
-    @inlineCallbacks
-    def apply_two_qubit_gate(self, cqc_header, cmd, xtra, gate):
-        if not xtra:
-            logging.warning("CQC %s: Missing XTRA Header", self.name)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-        #
-        logging.debug(
-            "CQC %s: Applying %s to App ID %d qubit id %d target %d",
-            self.name,
-            gate,
-            cqc_header.app_id,
-            cmd.qubit_id,
-            xtra.qubit_id,
-        )
-        try:
-            control = self.get_virt_qubit(cqc_header, cmd.qubit_id)
-            target = self.get_virt_qubit(cqc_header, xtra.qubit_id)
-        except UnknownQubitError as e:
-            logging.debug(e)
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_NOQUBIT, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-        # Return an error if the control and target are equal, can not do this
-        if control == target:
-            err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-            self.return_messages[cqc_header.app_id].append(err_msg)
-            return False
-
-        try:
-            success = yield control.callRemote(gate, target)
-            if success is False:
-                err_msg = self.create_return_message(cqc_header.app_id, CQC_ERR_UNSUPP, cqc_version=cqc_header.version)
-                self.return_messages[cqc_header.app_id].append(err_msg)
-                return False
-        except Exception as e:
-            raise e
-
-        return True
-
-    def get_virt_qubit(self, header, qubit_id):
-        """
-        Get reference to the virtual qubit reference in SimulaQron given app and qubit id, if it exists.
-        If not found, send back no qubit error.
-
-        Caution: Twisted PB does not allow references to objects to be passed back between connections.
-        If you need to pass a qubit reference back to the Twisted PB on a _different_ connection,
-        then use get_virt_qubit_indep below.
-        """
-        if not (header.app_id, qubit_id) in self.factory.qubitList:
-            raise UnknownQubitError("CQC {}: Qubit not found".format(self.name))
-        qubit = self.factory.qubitList[(header.app_id, qubit_id)]
-        return qubit.virt
-
-    @inlineCallbacks
-    def get_virt_qubit_indep(self, header, qubit_id):
-        """
-        Get NUMBER (not reference!) to virtual qubit in SimulaQron specific to this connection.
-        If not found, send back no qubit error.
-        """
-        # First let's get the general virtual qubit reference, if any
-        general_ref = self.get_virt_qubit(header, qubit_id)
-
-        try:
-            num = yield general_ref.callRemote("get_virt_num")
-        except Exception as e:
-            raise e
-
-        return num
-
-    @staticmethod
-    def new_qubit_id(app_id):
-        """
-        Returns a new unique qubit id for the specified app_id. Used by cmd_new and cmd_recv
-        """
-        if app_id in SimulaqronCQCHandler._available_q_ids:
-            q_ids = SimulaqronCQCHandler._available_q_ids[app_id]
-            q_id = heappop(q_ids)
-            if not q_ids:
-                heappush(q_ids, q_id + 1)
-            return q_id
-        else:
-            SimulaqronCQCHandler._available_q_ids[app_id] = []
-            heappush(SimulaqronCQCHandler._available_q_ids[app_id], 2)
-
-            return 1
-
-    @staticmethod
-    def new_ent_id(host_app_id, remote_node, remote_app_id):
-        """
-        Returns a new unique entanglement id for the specified host_app_id, remote_node and remote_app_id.
-        Used by cmd_epr.
-        """
-        pair_id = (host_app_id, remote_node, remote_app_id)
-        if pair_id in SimulaqronCQCHandler._next_ent_id:
-            ent_id = SimulaqronCQCHandler._next_ent_id[pair_id]
-            SimulaqronCQCHandler._next_ent_id[pair_id] += 1
-            return ent_id
-        else:
-            SimulaqronCQCHandler._next_ent_id[pair_id] = 1
-            return 0
-
-    def remove_qubit_id(self, app_id, qubit_id):
-        """
-        Remove qubit id from current used qubit_id so it can be reused
-        :param app_id: The app id of the current qubit_id
-        :param qubit_id: The qubit id to be removed
-        """
-        if app_id in SimulaqronCQCHandler._available_q_ids:
-            q_ids = SimulaqronCQCHandler._available_q_ids[app_id]
-            heappush(q_ids, qubit_id)
-        del self.factory.qubitList[(app_id, qubit_id)]
-
-
-#######################################################################################################
-#
-# CQC Internal qubit object to translate to the native mode of SimulaQron
-#
-
-
-class CQCQubit:
-    def __init__(self, qubit_id=0, timestamp=0, virt=0):
-        self.qubit_id = qubit_id
-        self.timestamp = timestamp
-        self.virt = virt
diff --git a/simulaqron/general/__init__.py b/simulaqron/general/__init__.py
index e69de29..9aa90a2 100644
--- a/simulaqron/general/__init__.py
+++ b/simulaqron/general/__init__.py
@@ -0,0 +1,1 @@
+from .errors import SimUnsupportedError
diff --git a/simulaqron/general/errors.py b/simulaqron/general/errors.py
new file mode 100644
index 0000000..2f52959
--- /dev/null
+++ b/simulaqron/general/errors.py
@@ -0,0 +1,2 @@
+class SimUnsupportedError(RuntimeError):
+    pass
diff --git a/simulaqron/general/hostConfig.py b/simulaqron/general/host_config.py
similarity index 73%
rename from simulaqron/general/hostConfig.py
rename to simulaqron/general/host_config.py
index 42adf0e..e24bc01 100644
--- a/simulaqron/general/hostConfig.py
+++ b/simulaqron/general/host_config.py
@@ -34,21 +34,28 @@
 
 from simulaqron.toolbox.manage_nodes import NetworksConfigConstructor
 
-from cqc.hostConfig import host
 
-
-def cqc_node_id(fam, ip):
+def node_id(fam, ip):
     if fam == socket.AF_INET:
         return struct.unpack("!L", IPv4Address(ip).packed)[0]
     else:
         raise ValueError("No IPv6 yet :(")
 
 
-def cqc_node_id_from_addrinfo(addr):
+def node_id_from_addrinfo(addr):
     fam = addr[0]
     sockaddr = addr[4]
     ip = sockaddr[0]
-    return cqc_node_id(fam, ip)
+    return node_id(fam, ip)
+
+
+def get_node_id_from_net_config(net_config, node_name):
+    """
+    NOTE node ID is the index of the node name of a sorted list of all the node names in the network.
+    """
+    if node_name not in net_config.hostDict:
+        raise ValueError(f"node name {node_name} not in host_dict ({net_config.hostDict.keys()})")
+    return list(sorted(net_config.hostDict.keys())).index(node_name)
 
 
 def load_node_names(config_file):
@@ -62,13 +69,41 @@ def load_node_names(config_file):
         return [line.strip() for line in f.readlines()]
 
 
-class socketsConfig(pb.Referenceable):
+class Host(pb.Referenceable):
+    def __init__(self, name, hostname, port):
+        """
+        Initialize the details of the host. For now, we just keep the following:
+
+        name        informal name of the host (e.g. Alice)
+        hostname    name of the node on the network (e.g. localhost or yournode.qutech.nl)
+        port        port number on hostname
+        """
+
+        self.name = name
+        self.hostname = hostname
+        self.port = int(port)
+
+        # Lookup IP address
+        addrs = socket.getaddrinfo(hostname, port, proto=socket.IPPROTO_TCP, family=socket.AF_INET)
+        addr = addrs[0]
+        self.family = addr[0]
+        self.addr = addr
+
+        self.ip = node_id_from_addrinfo(addr)
+
+        # Connection identifiers used after connected
+        self.factory = 0
+        self.root = 0
+        self.defer = 0
+
+
+class SocketsConfig(pb.Referenceable):
     def __init__(self, filename, network_name="default", config_type="vnode"):
         """
         Initialize by reading in the configuration file.
 
         With version 3.0.0 there is a single config used for all networks and all config types.
-        Therefore one needs to also specify the network_name and config_type ('vnode', 'cqc', 'app).
+        Therefore one needs to also specify the network_name and config_type ('vnode', 'app')
         """
         # Dictionary where we will keep host details, indexed by node name (e.g. Alice)
         self.hostDict = {}
@@ -84,8 +119,8 @@ def read_config(self, filename, network_name="default", config_type="vnode"):
         """
         with open(filename) as confFile:
             if filename.endswith(".json"):
-                if config_type not in ["vnode", "cqc", "app"]:
-                    raise ValueError("Type needs to be either 'vnode', 'cqc' or 'app'")
+                if config_type not in ["vnode", "qnodeos", "app"]:
+                    raise ValueError("Type needs to be either 'vnode', 'qnodeos' or 'app'")
                 if network_name is None:
                     network_name = "default"
                 network_config = NetworksConfigConstructor(file_path=filename).networks[network_name]
@@ -93,7 +128,7 @@ def read_config(self, filename, network_name="default", config_type="vnode"):
                 for node_name, node_config in nodes.items():
                     hostname = getattr(node_config, "{}_hostname".format(config_type))
                     port = getattr(node_config, "{}_port".format(config_type))
-                    self.hostDict[node_name] = host(node_name, hostname, port)
+                    self.hostDict[node_name] = Host(node_name, hostname, port)
 
             elif filename.endswith(".cfg"):
                 for line in confFile:
@@ -102,7 +137,7 @@ def read_config(self, filename, network_name="default", config_type="vnode"):
 
                         # We will simply ignore lines which are not of the right form
                         if len(words) == 3:
-                            newHost = host(words[0].strip(), words[1].strip(), words[2].strip())
+                            newHost = Host(words[0].strip(), words[1].strip(), words[2].strip())
                             self.hostDict[words[0]] = newHost
             else:
                 raise ValueError("Unknown file type {}".format(filename.split(".")[-1]))
diff --git a/simulaqron/local/setup.py b/simulaqron/local/setup.py
index 09c104d..9443ddb 100644
--- a/simulaqron/local/setup.py
+++ b/simulaqron/local/setup.py
@@ -37,7 +37,6 @@
 from simulaqron.settings import simulaqron_settings
 
 
-# logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', level=logging.DEBUG)
 #####################################################################################################
 #
 # setup_local
@@ -131,11 +130,10 @@ def init_register(resList, myName, virtualNet, classicalNet, lNode, func, *args,
         if lNode is not None:
             lNode.set_virtual_node(virtRoot)
     else:
-        print(resList)
         logging.error("LOCAL %s: Connection to virtual server failed!", myName)
         reactor.stop()
 
-        # Retrieve connections to the classical nodes
+    # Retrieve connections to the classical nodes
     for node in classicalNet.hostDict:
         nb = classicalNet.hostDict[node]
         if nb.name != myName:
@@ -147,7 +145,7 @@ def init_register(resList, myName, virtualNet, classicalNet, lNode, func, *args,
                 logging.error("LOCAL %s: Connection to %s failed!", myName, nb.name)
                 reactor.stop()
 
-                # On the local virtual node, we still want to initialize a qubit register
+    # On the local virtual node, we still want to initialize a qubit register
     defer = virtRoot.callRemote("add_register")
     defer.addCallback(fill_register, myName, lNode, virtRoot, classicalNet, func, *args, **kwargs)
     defer.addErrback(localError)
@@ -161,7 +159,7 @@ def fill_register(obj, myName, lNode, virtRoot, classicalNet, func, *args, **kwa
     if lNode is not None:
         lNode.set_virtual_reg(qReg)
 
-        # Run client side function
+    # Run client side function
     func(qReg, virtRoot, myName, classicalNet, *args, **kwargs)
 
 
@@ -169,7 +167,7 @@ def localError(reason):
     """
     Error handling for the connection.
     """
-    print("Critical error: ", reason)
+    logging.error("Critical error: ", reason)
     try:
         reactor.stop()
     except ReactorNotRunning:
diff --git a/simulaqron/cqc_backend/__init__.py b/simulaqron/netqasm_backend/__init__.py
similarity index 100%
rename from simulaqron/cqc_backend/__init__.py
rename to simulaqron/netqasm_backend/__init__.py
diff --git a/simulaqron/netqasm_backend/executioner.py b/simulaqron/netqasm_backend/executioner.py
new file mode 100644
index 0000000..8ecf77d
--- /dev/null
+++ b/simulaqron/netqasm_backend/executioner.py
@@ -0,0 +1,822 @@
+import random
+import time
+import traceback
+from collections import defaultdict
+from enum import Enum
+from functools import partial
+
+from netqasm.backend.executor import EprCmdData, Executor
+from netqasm.backend.messages import (ErrorCode, ErrorMessage,
+                                      ReturnArrayMessage, ReturnRegMessage)
+from netqasm.backend.network_stack import BaseNetworkStack
+from netqasm.lang import instr as instructions
+from netqasm.lang import operand
+from netqasm.qlink_compat import (Basis, BellState, LinkLayerErr,
+                                  LinkLayerOKTypeK, LinkLayerOKTypeM,
+                                  LinkLayerOKTypeR, RandomBasis, RequestType,
+                                  ReturnType)
+from simulaqron.general.host_config import get_node_id_from_net_config
+from simulaqron.settings import simulaqron_settings
+from simulaqron.virtual_node.virtual import call_method
+from twisted.internet import reactor, task
+from twisted.internet.defer import inlineCallbacks
+
+
+class UnknownQubitError(RuntimeError):
+    pass
+
+
+class NetworkStack(BaseNetworkStack):
+
+    def __init__(self, executioner):
+        """This is just a wrapper around the executioners methods for entanglement generation
+        in order to use the correct framework as used by the netqasm executioner.
+        """
+        self._executioner = executioner
+        self._sockets = {}
+
+    def put(self, request):
+        """Handles an request to the network stack"""
+        raise NotImplementedError("NetworkStack.put")
+
+    def setup_epr_socket(self, epr_socket_id, remote_node_id, remote_epr_socket_id, timeout=1):
+        """Asks the network stack to setup circuits to be used"""
+        # NOTE this just records the information but does not actually set up the socket
+        self._sockets[epr_socket_id] = (remote_node_id, remote_epr_socket_id)
+
+    def get_purpose_id(self, remote_node_id: int, epr_socket_id: int) -> int:
+        pass
+
+
+class VanillaSimulaQronExecutioner(Executor):
+
+    SIMULAQRON_OPS = {
+        instructions.vanilla.GateXInstruction: "apply_X",
+        instructions.vanilla.GateYInstruction: "apply_Y",
+        instructions.vanilla.GateZInstruction: "apply_Z",
+        instructions.vanilla.GateHInstruction: "apply_H",
+        instructions.vanilla.GateSInstruction: "apply_S",
+        instructions.vanilla.GateKInstruction: "apply_K",
+        instructions.vanilla.GateTInstruction: "apply_T",
+        instructions.vanilla.CnotInstruction: "cnot_onto",
+        instructions.vanilla.CphaseInstruction: "cphase_onto",
+    }
+
+    ROTATION_AXIS = {
+        instructions.vanilla.RotXInstruction: (1, 0, 0),
+        instructions.vanilla.RotYInstruction: (0, 1, 0),
+        instructions.vanilla.RotZInstruction: (0, 0, 1),
+    }
+
+    # Dictionary storing the next unique entanglement id for each used (host_app_id,remote_node,remote_app_id)
+    _next_ent_id = defaultdict(int)
+
+    # Next create id
+    _next_create_id = defaultdict(int)
+
+    # TODO this should live somewhere else and not hardcoded here
+    # also the case for the magic link layer in netsquid-magic
+    _num_bits_prob = 8
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self._return_msg_func = None
+        self._factory = None
+        self._network_stack = NetworkStack(self)
+
+    @property
+    def factory(self):
+        return self._factory
+
+    @property
+    def node_id(self):
+        return get_node_id_from_net_config(self.factory.qnodeos_net, self.name)
+
+    @staticmethod
+    def get_error_class(remote_err):
+        """
+        This is a function to get the error class of a remote thrown error when using callRemote.
+        :param remote_err: :obj:`twisted.spread.pb.RemoteError`
+        :return: class
+        """
+        # Get name of remote error
+        error_name = remote_err.remoteType.split(b".")[-1].decode()
+
+        # Get class of remote error
+        error_class = eval(error_name)
+
+        return error_class
+
+    def add_return_msg_func(self, func):
+        self._return_msg_func = func
+
+    def add_factory(self, factory):
+        self._factory = factory
+
+    def _handle_command_exception(self, exc, prog_counter, traceback_str):
+        self._logger.error(f"At line {prog_counter}: {exc}\n{traceback_str}")
+        self._return_msg(msg=ErrorMessage(err_code=ErrorCode.GENERAL))
+
+    def _return_msg(self, msg):
+        if self._return_msg_func is None:
+            raise RuntimeError("Cannot return msg since no function is set")
+        self._return_msg_func(msg=msg)
+
+    def _instr_qalloc(self, subroutine_id, instr: instructions.core.QAllocInstruction):
+        physical_address = yield from super()._instr_qalloc(
+            subroutine_id=subroutine_id,
+            instr=instr,
+        )
+        yield self.cmd_new(physical_address=physical_address)
+
+    @inlineCallbacks
+    def cmd_new(self, physical_address):
+        """
+        Request a new qubit. Since we don't need it, this python NetQASM just provides very crude timing information.
+        (return_q_id is used internally)
+        (ignore_max_qubits is used internally to ignore the check of number of virtual qubits at the node
+        such that the node can temporarily create a qubit for EPR creation.)
+        """
+        try:
+            yield self.factory._lock.acquire()
+            virt = yield call_method(self.factory.virtRoot, "new_qubit")
+            q_id = physical_address
+            q = VirtualQubitRef(q_id, int(time.time()), virt)
+            self.factory.qubitList[q_id] = q
+            self._logger.info(f"Requested new physical qubit {q_id})")
+
+        finally:
+            self.factory._lock.release()
+
+    def _do_single_qubit_instr(self, instr, subroutine_id, address):
+        position = self._get_position(subroutine_id=subroutine_id, address=address)
+        if isinstance(instr, instructions.core.InitInstruction):
+            yield self.cmd_reset(qubit_id=position)
+        else:
+            simulaqron_gate = self._get_simulaqron_gate(instr=instr)
+            yield self.apply_single_qubit_gate(
+                gate=simulaqron_gate,
+                qubit_id=position,
+            )
+
+    def _do_single_qubit_rotation(self, instr, subroutine_id, address, angle):
+        position = self._get_position(subroutine_id=subroutine_id, address=address)
+        axis = self._get_axis(instr=instr)
+        yield self.apply_rotation(
+            axis=axis,
+            angle=angle,
+            qubit_id=position,
+        )
+
+    @classmethod
+    def _get_axis(cls, instr):
+        axis = cls.ROTATION_AXIS.get(type(instr))
+        if axis is None:
+            raise ValueError(f"Unknown rotation instruction {instr}")
+        return axis
+
+    @inlineCallbacks
+    def apply_rotation(self, axis, angle, qubit_id):
+        """
+        Apply a rotation of the qubit specified in cmd with an angle specified in xtra
+        around the axis
+        """
+        self._logger.debug(f"Applying a rotation around {axis} to physical qubit id {qubit_id}")
+        virt_qubit = self.get_virt_qubit(qubit_id=qubit_id)
+        yield call_method(virt_qubit, "apply_rotation", axis, angle)
+
+    def _do_two_qubit_instr(self, instr, subroutine_id, address1, address2):
+        positions = self._get_positions(subroutine_id=subroutine_id, addresses=[address1, address2])
+        simulaqron_gate = self._get_simulaqron_gate(instr=instr)
+        yield self.apply_two_qubit_gate(
+            gate=simulaqron_gate,
+            qubit_id1=positions[0],
+            qubit_id2=positions[1],
+        )
+
+    @inlineCallbacks
+    def apply_two_qubit_gate(self, gate, qubit_id1, qubit_id2):
+        self._logger.debug(f"Applying {gate} to physical qubit id {qubit_id1} target {qubit_id2}")
+        control = self.get_virt_qubit(qubit_id=qubit_id1)
+        target = self.get_virt_qubit(qubit_id=qubit_id2)
+        if control == target:
+            raise ValueError("target and control in two-qubit gate cannot be equal")
+        yield call_method(control, gate, target)
+
+    @classmethod
+    def _get_simulaqron_gate(cls, instr):
+        simulaqron_gate = cls.SIMULAQRON_OPS.get(type(instr))
+        if simulaqron_gate is None:
+            raise TypeError(f"Unknown gate type {type(instr)}")
+        return simulaqron_gate
+
+    @inlineCallbacks
+    def apply_single_qubit_gate(self, gate, qubit_id):
+        virt_qubit = self.get_virt_qubit(qubit_id=qubit_id)
+        yield call_method(virt_qubit, gate)
+
+    def get_virt_qubit(self, qubit_id):
+        """
+        Get reference to the virtual qubit reference in SimulaQron given app and qubit id, if it exists.
+        If not found, send back no qubit error.
+        Caution: Twisted PB does not allow references to objects to be passed back between connections.
+        If you need to pass a qubit reference back to the Twisted PB on a _different_ connection,
+        then use get_virt_qubit_indep below.
+        """
+        if qubit_id not in self.factory.qubitList:
+            raise UnknownQubitError(f"{self.name}: Qubit {qubit_id} not found")
+        qubit = self.factory.qubitList[qubit_id]
+        return qubit.virt
+
+    @inlineCallbacks
+    def get_virt_qubit_num(self, qubit_id):
+        """
+        Get NUMBER (not reference!) to virtual qubit in SimulaQron specific to this connection.
+        If not found, send back no qubit error.
+        """
+        # First let's get the general virtual qubit reference, if any
+        virt = self.get_virt_qubit(qubit_id=qubit_id)
+        num = yield call_method(virt, "get_virt_num")
+        return num
+
+    def _do_meas(self, subroutine_id, q_address):
+        position = self._get_position(subroutine_id=subroutine_id, address=q_address)
+        outcome = yield self.cmd_measure(qubit_id=position, inplace=True)
+        return outcome
+
+    @inlineCallbacks
+    def cmd_measure(self, qubit_id, inplace=True):
+        """
+        Measure
+        """
+        self._logger.debug(f"Measuring physical qubit id {qubit_id}")
+        virt_qubit = self.get_virt_qubit(qubit_id=qubit_id)
+        outcome = yield call_method(virt_qubit, "measure", inplace)
+        if outcome is None:
+            raise RuntimeError("Measurement failed")
+        self._logger.debug(f"Measured outcome {outcome}")
+        return outcome
+
+    @inlineCallbacks
+    def cmd_reset(self, qubit_id, correct=True):
+        r"""
+        Reset Qubit to \|0\>
+        """
+        self._logger.debug(f"Reset physical qubit id {qubit_id}")
+        virt_qubit = self.get_virt_qubit(qubit_id=qubit_id)
+        outcome = yield call_method(virt_qubit, "measure", inplace=True)
+
+        # If state is |1> do correction
+        if correct and outcome:
+            yield call_method(virt_qubit, "apply_X")
+
+    def _do_wait(self, delay=0.1):
+        d = task.deferLater(reactor, delay, lambda: self._logger.debug("Wait finished"))
+        self._logger.debug("waiting a bit")
+        yield d
+
+    def _update_shared_memory(self, app_id, entry, value):
+        if isinstance(entry, operand.Register):
+            self._logger.debug(f"Updating host about register {entry} with value {value}")
+            self._return_msg(msg=ReturnRegMessage(
+                register=entry.cstruct,
+                value=value,
+            ))
+        elif isinstance(entry, operand.Address):
+            self._logger.debug(f"Updating host about array {entry} with value {value}")
+            address = entry.address
+            self._return_msg(msg=ReturnArrayMessage(
+                address=address,
+                values=value,
+            ))
+        else:
+            raise TypeError(f"Cannot update shared memory with entry specified as {entry}")
+
+    def _do_create_epr(
+        self,
+        subroutine_id,
+        remote_node_id,
+        epr_socket_id,
+        q_array_address,
+        arg_array_address,
+        ent_results_array_address,
+    ):
+        create_request = self._get_create_request(
+            subroutine_id=subroutine_id,
+            remote_node_id=remote_node_id,
+            epr_socket_id=epr_socket_id,
+            arg_array_address=arg_array_address,
+        )
+        create_id = self._get_new_create_id(remote_node_id=remote_node_id)
+        remote_epr_socket_id = self._get_remote_epr_socket_id(epr_socket_id=epr_socket_id)
+
+        # Check that we have the right amount of virtual qubit addresses to be used
+        app_id = self._get_app_id(subroutine_id=subroutine_id)
+        if create_request.type == RequestType.K:
+            num_qubits = len(self._app_arrays[app_id][q_array_address, :])
+            assert num_qubits == create_request.number, "Not enough qubit addresses"
+
+        self._epr_create_requests[remote_node_id, create_request.purpose_id].append(EprCmdData(
+            subroutine_id=subroutine_id,
+            ent_results_array_address=ent_results_array_address,
+            q_array_address=q_array_address,
+            request=create_request,
+            tot_pairs=create_request.number,
+            pairs_left=create_request.number,
+        ))
+        for _ in range(create_request.number):
+            qubit_id_host = self._get_unused_physical_qubit()
+
+            yield self.cmd_epr(
+                create_id=create_id,
+                remote_node_id=remote_node_id,
+                epr_socket_id=epr_socket_id,
+                remote_epr_socket_id=remote_epr_socket_id,
+                qubit_id=qubit_id_host,
+                create_request=create_request,
+            )
+
+    def _do_recv_epr(
+        self,
+        subroutine_id,
+        remote_node_id,
+        epr_socket_id,
+        q_array_address,
+        ent_results_array_address
+    ):
+        app_id = self._get_app_id(subroutine_id=subroutine_id)
+        num_pairs = self._get_num_pairs_from_array(
+            app_id=app_id,
+            ent_results_array_address=ent_results_array_address,
+        )
+
+        purpose_id = self._get_purpose_id(remote_node_id=remote_node_id, epr_socket_id=epr_socket_id)
+        self._epr_recv_requests[remote_node_id, purpose_id].append(EprCmdData(
+            subroutine_id=subroutine_id,
+            ent_results_array_address=ent_results_array_address,
+            q_array_address=q_array_address,
+            request=None,
+            tot_pairs=num_pairs,
+            pairs_left=num_pairs,
+        ))
+
+        for _ in range(num_pairs):
+            qubit_id = self._get_unused_physical_qubit()
+            yield self.cmd_epr_recv(
+                epr_socket_id=epr_socket_id,
+                qubit_id=qubit_id,
+            )
+
+    def _get_remote_epr_socket_id(self, epr_socket_id):
+        remote_entry = self.network_stack._sockets.get(epr_socket_id)
+        if remote_entry is None:
+            raise ValueError(f"Unknown EPR socket ID {epr_socket_id}")
+        return remote_entry[1]
+
+    @inlineCallbacks
+    def cmd_epr(
+        self,
+        create_id,
+        remote_node_id,
+        epr_socket_id,
+        remote_epr_socket_id,
+        qubit_id,
+        create_request,
+    ):
+        """
+        Create EPR pair with another node.
+        Depending on the ips and ports this will either create an EPR-pair and send one part, or just receive.
+        """
+        # Get ip and port of remote host
+        for remote_node_name, remote_host in self.factory.qnodeos_net.hostDict.items():
+            node_id = get_node_id_from_net_config(self.factory.qnodeos_net, remote_host.name)
+            if node_id == remote_node_id:
+                break
+        else:
+            raise ValueError(f"Unknown node with ID {remote_node_id}")
+
+        self._logger.debug(f"Creating EPR with {remote_node_name} on socket {epr_socket_id}")
+
+        # Check so that it is not the same node
+        if self.name == remote_node_name:
+            raise ValueError("Trying to create EPR from node to itself.")
+
+        # Check that other node is adjacent to us
+        if not self.factory.is_adjacent(remote_node_name):
+            raise ValueError(f"Node {self.name} is not adjacent to {remote_node_name} in the specified topology.")
+
+        # Create the qubits
+        # NOTE we don't actually allocate it since it will be sent to the other node (or measured)
+        # NOTE we will use negative address to not mix up with normal qubits
+        second_qubit_id = -(1 + qubit_id)
+        for q_id in [qubit_id, second_qubit_id]:
+            yield self.cmd_new(
+                physical_address=q_id,
+            )
+
+        # Produce EPR-pair
+        h_gate = self._get_simulaqron_gate(instr=instructions.vanilla.GateHInstruction())
+        yield self.apply_single_qubit_gate(
+            gate=h_gate,
+            qubit_id=qubit_id,
+        )
+        cnot_gate = self._get_simulaqron_gate(instr=instructions.vanilla.CnotInstruction())
+        yield self.apply_two_qubit_gate(
+            gate=cnot_gate,
+            qubit_id1=qubit_id,
+            qubit_id2=second_qubit_id,
+        )
+
+        # Get entanglement id
+        # TODO lock here?
+        ent_id = self.new_ent_id(
+            epr_socket_id=epr_socket_id,
+            remote_node_id=remote_node_id,
+            remote_epr_socket_id=remote_epr_socket_id,
+        )
+        if create_request.type == RequestType.K:
+            # Prepare ent_info header with entanglement information
+            ent_info = LinkLayerOKTypeK(
+                type=ReturnType.OK_K,
+                create_id=create_id,
+                logical_qubit_id=qubit_id,
+                directionality_flag=0,
+                sequence_number=ent_id,
+                # NOTE We use EPR socket ID
+                purpose_id=epr_socket_id,
+                remote_node_id=remote_node_id,
+                goodness=1,
+                goodness_time=int(time.time()),
+                bell_state=BellState.PHI_PLUS,
+            )
+
+            # Send second qubit (and epr info)
+            yield self.send_epr_half(
+                qubit_id=second_qubit_id,
+                epr_socket_id=epr_socket_id,
+                remote_node_name=remote_node_name,
+                remote_epr_socket_id=remote_epr_socket_id,
+                ent_info=ent_info,
+            )
+        elif create_request.type == RequestType.M:
+            local_outcome, local_basis = yield self._measure_epr_qubit(
+                qubit_id=qubit_id,
+                request=create_request,
+                remote=False,
+            )
+            remote_outcome, remote_basis = yield self._measure_epr_qubit(
+                qubit_id=second_qubit_id,
+                request=create_request,
+                remote=True,
+            )
+            # Prepare ent_info header with entanglement information
+            ent_info = LinkLayerOKTypeM(
+                type=ReturnType.OK_M,
+                create_id=create_id,
+                measurement_outcome=local_outcome,
+                measurement_basis=local_basis,
+                directionality_flag=0,
+                sequence_number=ent_id,
+                # NOTE We use EPR socket ID
+                purpose_id=epr_socket_id,
+                remote_node_id=remote_node_id,
+                goodness=1,
+                bell_state=BellState.PHI_PLUS,
+            )
+
+            # Send the outcome (and epr info)
+            yield self.send_epr_outcome_half(
+                epr_socket_id=epr_socket_id,
+                remote_node_name=remote_node_name,
+                remote_epr_socket_id=remote_epr_socket_id,
+                ent_info=ent_info,
+                remote_outcome=remote_outcome,
+                remote_basis=remote_basis,
+            )
+        else:
+            raise NotImplementedError(f"EPR requests of type {create_request.type} are not yet supported in simulaqron")
+
+        self._handle_epr_response(response=ent_info)
+        self._logger.debug("finished cmd_epr")
+
+    @inlineCallbacks
+    def _measure_epr_qubit(self, qubit_id, request, remote: bool):
+        # Check the arguments depending on if this is the local or remote qubit
+        if remote:
+            assert request.rotation_X_remote1 == 0, "Measure directly with rotations not yet supported"
+            assert request.rotation_Y_remote == 0, "Measure directly with rotations not yet supported"
+            assert request.rotation_X_remote2 == 0, "Measure directly with rotations not yet supported"
+            random_basis = request.random_basis_remote
+            probability_dist1 = request.probability_dist_remote1
+            probability_dist2 = request.probability_dist_remote2
+
+        else:
+            assert request.rotation_X_local1 == 0, "Measure directly with rotations not yet supported"
+            assert request.rotation_Y_local == 0, "Measure directly with rotations not yet supported"
+            assert request.rotation_X_local2 == 0, "Measure directly with rotations not yet supported"
+            random_basis = request.random_basis_local
+            probability_dist1 = request.probability_dist_local1
+            probability_dist2 = request.probability_dist_local2
+
+        # Sample the basis to use
+        probability_dist_spec = [probability_dist1, probability_dist2]
+        basis = self._sample_basis_choice(random_basis_set=random_basis, probability_dist_spec=probability_dist_spec)
+        if basis == Basis.Z:
+            pass
+        elif basis == Basis.X:
+            h_gate = self._get_simulaqron_gate(instr=instructions.vanilla.GateHInstruction())
+            yield self.apply_single_qubit_gate(
+                gate=h_gate,
+                qubit_id=qubit_id,
+            )
+        elif basis == Basis.Y:
+            k_gate = self._get_simulaqron_gate(instr=instructions.vanilla.GateKInstruction())
+            yield self.apply_single_qubit_gate(
+                gate=k_gate,
+                qubit_id=qubit_id,
+            )
+        else:
+            raise NotImplementedError(f"Cannot yet measure in basis {basis}")
+
+        # Measure the qubit
+        outcome = yield self.cmd_measure(qubit_id=qubit_id, inplace=False)
+        self.remove_qubit_id(qubit_id=qubit_id)
+        return outcome, basis
+
+    # NOTE this method is copied from netsquid magic
+    def _get_probability_weights(self, probability_dist_spec, num_choices):
+        """
+        Used internally by `_sample_basis_choice` to convert specified probability distribution to correct form
+
+        :param probability_dist_spec: list of ints
+        :param num_choices: int
+        :return: list of ints
+        """
+        num_values = 2 ** self._num_bits_prob
+        if num_choices == 2:
+            p = probability_dist_spec[0]
+            weights = [p, num_values - p]
+        elif num_choices == 3:
+            p1, p2 = probability_dist_spec[:2]
+            weights = [p1, p2, num_values - (p1 + p2)]
+        else:
+            raise ValueError("Unknown number of choices for basis")
+
+        return weights
+
+    # NOTE this method is copied from netsquid magic
+    def _sample_basis_choice(self, random_basis_set, probability_dist_spec):
+        """
+        Samples the random basis, given the specified bases set and probability distribution
+
+        :param random_basis_set: int
+        :param probability_dist_spec: list of ints
+        :return: list of ints
+        """
+        # Convert to a integer represented by 8 bits
+        num_values = 2 ** self._num_bits_prob
+        try:
+            probability_dist_spec = [int(p) % num_values for p in probability_dist_spec]
+        except (ValueError, TypeError):
+            raise TypeError("Could not convert probability dist ({}, {}, {}) to integers.", *probability_dist_spec)
+        # Possibly chose a random operator to perform before the measurement
+        if not isinstance(random_basis_set, RandomBasis):
+            random_basis_set = RandomBasis(random_basis_set)
+        if random_basis_set == RandomBasis.NONE:
+            # Measure in Z
+            basis = Basis.Z
+        elif random_basis_set == RandomBasis.XZ:
+            # Measure in X or Z
+            weights = self._get_probability_weights(probability_dist_spec, num_choices=2)
+            basis = random.choices([Basis.X, Basis.Z], weights)[0]
+        elif random_basis_set == RandomBasis.XYZ:
+            # Measure in X, Y or Z
+            weights = self._get_probability_weights(probability_dist_spec, num_choices=3)
+            basis = random.choices([Basis.X, Basis.Y, Basis.Z], weights)[0]
+        elif random_basis_set == RandomBasis.CHSH:
+            # Measure in (Z + X)/2 or (Z - X)/2
+            weights = self._get_probability_weights(probability_dist_spec, num_choices=2)
+            basis = random.choices([Basis.ZPLUSX, Basis.ZMINUSX], weights)[0]
+        else:
+            raise ValueError("Unsupported random basis choice {}".format(random_basis_set))
+
+        return basis
+
+    @classmethod
+    def new_ent_id(cls, epr_socket_id, remote_node_id, remote_epr_socket_id):
+        """
+        Returns a new unique entanglement id for the specified host_app_id, remote_node and remote_app_id.
+        Used by cmd_epr.
+        """
+        pair_id = (epr_socket_id, remote_node_id, remote_epr_socket_id)
+        ent_id = cls._next_ent_id[pair_id]
+        cls._next_ent_id[pair_id] += 1
+        return ent_id
+
+    @classmethod
+    def _get_new_create_id(cls, remote_node_id):
+        create_id = cls._next_create_id[remote_node_id]
+        cls._next_create_id[remote_node_id] += 1
+        return create_id
+
+    @inlineCallbacks
+    def send_epr_half(
+        self,
+        qubit_id,
+        epr_socket_id,
+        remote_node_name,
+        remote_epr_socket_id,
+        ent_info
+    ):
+        """
+        Send qubit to another node.
+        """
+        # Lookup the virtual qubit from identifier
+        virt_num = yield self.get_virt_qubit_num(qubit_id=qubit_id)
+
+        # Update raw entanglement information for remote node
+        remote_ent_info = LinkLayerOKTypeK(
+            type=ent_info.type,
+            create_id=ent_info.create_id,
+            logical_qubit_id=qubit_id,
+            directionality_flag=1,
+            sequence_number=ent_info.sequence_number,
+            # NOTE We use EPR socket ID
+            purpose_id=remote_epr_socket_id,
+            remote_node_id=self.node_id,
+            goodness=ent_info.goodness,
+            goodness_time=ent_info.goodness_time,
+            bell_state=ent_info.bell_state,
+        )
+
+        # Send instruction to transfer the qubit
+        remote_ent_info = tuple(v.value if isinstance(v, Enum) else v for v in remote_ent_info)
+        yield call_method(
+            self.factory.virtRoot,
+            "netqasm_send_epr_half",
+            virt_num,
+            remote_node_name,
+            epr_socket_id,
+            remote_epr_socket_id,
+            remote_ent_info,
+        )
+
+        self._logger.debug(f"Sent half a EPR pair as qubit id {qubit_id} to {remote_node_name}")
+        # Remove from active mapped qubits
+        self.remove_qubit_id(qubit_id=qubit_id)
+
+    @inlineCallbacks
+    def send_epr_outcome_half(
+        self,
+        epr_socket_id,
+        remote_node_name,
+        remote_epr_socket_id,
+        ent_info,
+        remote_outcome,
+        remote_basis
+    ):
+        """
+        Send outcome from measure directly to another node.
+        """
+
+        # Update raw entanglement information for remote node
+        remote_ent_info = LinkLayerOKTypeM(
+            type=ent_info.type,
+            create_id=ent_info.create_id,
+            measurement_outcome=remote_outcome,
+            measurement_basis=remote_basis,
+            directionality_flag=1,
+            sequence_number=ent_info.sequence_number,
+            # NOTE We use EPR socket ID
+            purpose_id=remote_epr_socket_id,
+            remote_node_id=self.node_id,
+            goodness=ent_info.goodness,
+            bell_state=ent_info.bell_state,
+        )
+
+        # Transfer info to remote node
+        remote_ent_info = tuple(v.value if isinstance(v, Enum) else v for v in remote_ent_info)
+        yield call_method(
+            self.factory.virtRoot,
+            "netqasm_send_epr_half",
+            None,
+            remote_node_name,
+            epr_socket_id,
+            remote_epr_socket_id,
+            remote_ent_info,
+        )
+
+        self._logger.debug(f"Sent half a measure direclty EPR pair to {remote_node_name}")
+
+    @staticmethod
+    def _unpack_ent_info(raw_ent_info):
+        return_type_id = raw_ent_info[0]
+        ent_info_class = {
+            ReturnType.OK_K.value: LinkLayerOKTypeK,
+            ReturnType.OK_M.value: LinkLayerOKTypeM,
+            ReturnType.OK_R.value: LinkLayerOKTypeR,
+            ReturnType.ERR.value: LinkLayerErr,
+        }[return_type_id]
+        return ent_info_class(
+            ReturnType(return_type_id),
+            *raw_ent_info[1:-1],
+            bell_state=BellState(raw_ent_info[-1]),
+        )
+
+    @staticmethod
+    def _update_qubit_id(ent_info, qubit_id):
+        dct = ent_info._asdict()
+        dct["logical_qubit_id"] = qubit_id
+        return ent_info.__class__(**dct)
+
+    @inlineCallbacks
+    def cmd_epr_recv(self, epr_socket_id, qubit_id=None):
+        """
+        Receive half of epr from another node. Block until qubit is received.
+        """
+        self._logger.debug(f"Asking to receive for EPR socket ID {epr_socket_id}")
+
+        # This will block until a qubit is received.
+        no_gen = True
+        virt_qubit = None
+        ent_info = None
+        # recv_timeout is in 100ms (for legacy reasons there are no plans to change it to seconds)
+        sleep_time = simulaqron_settings.recv_retry_time
+        for _ in range(int(simulaqron_settings.recv_timeout * 0.1 / sleep_time)):
+            data = yield call_method(self.factory.virtRoot, "netqasm_get_epr_recv", epr_socket_id)
+            if data:
+                no_gen = False
+                (virt_qubit, raw_ent_info) = data
+                ent_info = self._unpack_ent_info(raw_ent_info=raw_ent_info)
+                if isinstance(ent_info, LinkLayerOKTypeK):
+                    ent_info = self._update_qubit_id(ent_info=ent_info, qubit_id=qubit_id)
+                break
+            else:
+                yield from self._do_wait(delay=sleep_time)
+        if no_gen:
+            raise TimeoutError("TIMEOUT, no EPR generation received.")
+
+        if isinstance(ent_info, LinkLayerOKTypeK):
+            self._logger.debug(
+                f"Qubit received for EPR socket ID {epr_socket_id}, "
+                f"will use {qubit_id} as physical qubit ID"
+            )
+
+            # Once we have the qubit, add it to the local list and send a reply we received it. Note that we will
+            # recheck whether it exists: it could have been added by another connection in the mean time
+            try:
+                self.factory._lock.acquire()
+
+                if qubit_id in self.factory.qubitList:
+                    raise RuntimeError(f"Qubit with ID {qubit_id} already in use")
+
+                q = VirtualQubitRef(qubit_id, int(time.time()), virt_qubit)
+                self.factory.qubitList[qubit_id] = q
+            finally:
+                self.factory._lock.release()
+        elif isinstance(ent_info, LinkLayerOKTypeM):
+            self._logger.debug(
+                f"Measure directly EPR request received for EPR socket ID {epr_socket_id}."
+            )
+
+        self._handle_epr_response(response=ent_info)
+
+    def remove_qubit_id(self, qubit_id):
+        self._logger.debug(f"Removing physical qubit with ID {qubit_id} from handles to simulated qubits")
+        self.factory.qubitList.pop(qubit_id)
+
+    def _get_purpose_id(self, remote_node_id, epr_socket_id):
+        # NOTE this is for now since we communicate directly to link layer
+        # Use the EPR socket ID for now
+        return epr_socket_id
+
+    def _wait_to_handle_epr_responses(self):
+        d = task.deferLater(reactor, 0.1, self._handle_pending_epr_responses)
+        self._logger.debug("waiting a bit to handle epr response")
+        d.addErrback(partial(self._print_error, "_handle_pending_epr_responses"))
+
+    def _print_error(self, scope, failure):
+        traceback_str = ''.join(traceback.format_tb(failure.__traceback__))
+        self._logger.error(f"{scope} failed with error failure {failure}\n traceback: {traceback_str}")
+
+    def _reserve_physical_qubit(self, physical_address):
+        # NOTE does not do anything, done by cmd_new instead
+        pass
+
+    def _clear_phys_qubit_in_memory(self, physical_address):
+        self._logger.debug(f"clearing phys qubit {physical_address}")
+        yield self.cmd_measure(qubit_id=physical_address, inplace=False)
+        self.remove_qubit_id(qubit_id=physical_address)
+
+
+class VirtualQubitRef:
+    def __init__(self, qubit_id=0, timestamp=0, virt=0):
+        self.qubit_id = qubit_id
+        self.timestamp = timestamp
+        self.virt = virt
+
+    def __str__(self):
+        return f"{self.__class__.__name__}(qubit_id={self.qubit_id}, timestamp={self.timestamp}, virt={self.virt})"
+
+    def __repr__(self):
+        return str(self)
diff --git a/simulaqron/netqasm_backend/factory.py b/simulaqron/netqasm_backend/factory.py
new file mode 100644
index 0000000..fdb87de
--- /dev/null
+++ b/simulaqron/netqasm_backend/factory.py
@@ -0,0 +1,243 @@
+# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#    This product includes software developed by Stephanie Wehner, QuTech.
+# 4. Neither the name of the QuTech organization nor the
+#    names of its contributors may be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import sys
+
+from twisted.internet import reactor
+from twisted.internet.defer import DeferredLock, inlineCallbacks
+from twisted.internet.protocol import Factory, Protocol, connectionDone
+from twisted.internet.task import deferLater
+
+from netqasm.logging.glob import get_netqasm_logger
+from netqasm.backend.messages import MessageHeader, ErrorMessage, ErrorCode, deserialize_host_msg
+
+from simulaqron.settings import simulaqron_settings
+from simulaqron.toolbox.manage_nodes import NetworksConfigConstructor
+
+
+class IncompleteMessageError(ValueError):
+    pass
+
+
+class NetQASMProtocol(Protocol):
+    # Dictionary storing the next unique qubit id for each used app_id
+    _next_q_id = {}
+
+    # Dictionary storing the next unique entanglement id for each used
+    # (host_app_id,remote_node,remote_app_id)
+    _next_ent_id = {}
+
+    def __init__(self, factory):
+
+        # NetQASM Factory, including our connection to the SimulaQron backend
+        self.factory = factory
+
+        # Default application ID, typically one connection per application but
+        # we will deliberately NOT check for that since this is the task of
+        # higher layers or an OS
+        self.app_id = 0
+
+        # Define the backend to use.
+        self.messageHandler = factory.backend
+        self.messageHandler.protocol = self
+
+        # Flag to determine whether we already received _all_ of the NetQASM header
+        self.got_netqasm_header = False
+
+        # Header for which we are currently processing a packet
+        self.currHeader = None
+
+        # Buffer received data (which may arrive in chunks)
+        self.buf = None
+
+        # Convenience
+        self.name = self.factory.name
+
+        self._logger = get_netqasm_logger(f"{self.__class__.__name__}({self.name})")
+        self._logger.debug("Initialized Protocol")
+
+    def connectionMade(self):
+        pass
+
+    def connectionLost(self, reason=connectionDone):
+        pass
+
+    def dataReceived(self, data):
+        """
+        Receive data. We will always wait to receive enough data for the
+        header, and then the entire packet first before commencing processing.
+        """
+        # Read whatever we received into a buffer
+        if self.buf:
+            self.buf = self.buf + data
+        else:
+            self.buf = data
+
+        try:
+            msg_id, msg = self._parse_message()
+        except IncompleteMessageError:
+            return
+
+        d = self.messageHandler.handle_netqasm_message(msg_id=msg_id, msg=msg)
+        d.addCallback(self.log_handled_message)
+        d.addErrback(self.log_error)
+
+    def log_handled_message(self, result):
+        self._logger.info(f"Finished handling message with result = {result}")
+
+    @inlineCallbacks
+    def log_error(self, failure):
+        self._logger.error(f"Handling message failed with failure = {failure}")
+        sys.stderr.write(str(failure))
+        self._return_msg(msg=ErrorMessage(err_code=ErrorCode.GENERAL))
+        yield deferLater(reactor, 0.1, self.stop)
+
+    def stop(self):
+        self.factory.stop()
+
+    def _parse_message(self):
+        try:
+            msg_hdr = MessageHeader.from_buffer_copy(self.buf)
+        except ValueError:
+            raise IncompleteMessageError
+        if len(self.buf) < msg_hdr.length:
+            raise IncompleteMessageError
+        msg = deserialize_host_msg(self.buf[MessageHeader.len():])
+        self.buf = self.buf[msg_hdr.length:]
+
+        return msg_hdr.id, msg
+
+    def _handle_init_new_app(self, msg):
+        app_id = msg.app_id
+        self._add_app(app_id=app_id)
+        max_qubits = msg.max_qubits
+        self._logger.debug(f"Allocating a new "
+                           f"unit module of size {max_qubits} for application with app ID {app_id}.\n")
+        self._executioner.init_new_application(
+            app_id=app_id,
+            max_qubits=max_qubits,
+        )
+
+    def _return_msg(self, msg):
+        """
+        Return a msg to the host.
+        """
+        self.transport.write(msg)
+
+
+###############################################################################
+#
+# NetQASM Factory
+#
+# Twisted factory for the NetQASM protocol
+#
+
+
+class NetQASMFactory(Factory):
+    def __init__(self, host, name, qnodeos_net, backend, network_name="default"):
+        """
+        Initialize NetQASM Factory.
+
+        lhost	details of the local host (class host)
+        """
+
+        self.host = host
+        self.name = name
+        self.qnodeos_net = qnodeos_net
+        self.virtRoot = None
+        self.qReg = None
+        self.backend = backend(self)
+        self.network_name = network_name
+
+        # Dictionary that keeps qubit dictorionaries for each application
+        self.qubitList = {}
+
+        # Lock governing access to the qubitList
+        self._lock = DeferredLock()
+
+        self._logger = get_netqasm_logger(f"{self.__class__.__name__}({name})")
+
+        # Read in topology, if specified. topology=None means fully connected
+        # topology
+        self.topology = None
+        if simulaqron_settings.network_config_file is not None:
+            networks_config = NetworksConfigConstructor(file_path=simulaqron_settings.network_config_file)
+            self.topology = networks_config.networks[network_name].topology
+
+    def stop(self):
+        reactor.stop()
+
+    def buildProtocol(self, addr):
+        """
+        Return an instance of NetQASMProtocol when a connection is made.
+        """
+        return NetQASMProtocol(self)
+
+    def set_virtual_node(self, virtRoot):
+        """
+        Set the virtual root allowing connections to the SimulaQron backend.
+        """
+        self.virtRoot = virtRoot
+
+    def lookup(self, ip, port):
+        """
+        Lookup name of remote host used within SimulaQron given ip and
+        portnumber.
+        """
+        for entry in self.qnodeos_net.hostDict:
+            node = self.qnodeos_net.hostDict[entry]
+            if (node.ip == ip) and (node.port == port):
+                return node.name
+
+        self._logger.debug("No such node")
+        return None
+
+    def is_adjacent(self, remote_host_name):
+        """
+        Checks if remote host is adjacent to this node, according to the
+        specified topology.
+
+        :param remote_host_name: str
+            The name of the remote host
+        :return:
+        """
+        # Check if a topology is defined, otherwise use fully connected
+        if self.topology is None:
+            return True
+
+        if self.name in self.topology:
+            if remote_host_name in self.topology[self.name]:
+                return True
+            else:
+                return False
+        else:
+            self._logger.warning(
+                "Node {} is not in the specified topology and is therefore "
+                "assumed to have no neighbors".format(self.name)
+            )
+            return False
diff --git a/simulaqron/netqasm_backend/qnodeos.py b/simulaqron/netqasm_backend/qnodeos.py
new file mode 100644
index 0000000..add7429
--- /dev/null
+++ b/simulaqron/netqasm_backend/qnodeos.py
@@ -0,0 +1,50 @@
+from twisted.internet.defer import inlineCallbacks
+from netqasm.backend.messages import MsgDoneMessage
+from netqasm.backend.qnodeos import QNodeController
+
+from simulaqron.netqasm_backend.executioner import VanillaSimulaQronExecutioner
+
+
+class SubroutineHandler(QNodeController):
+    def __init__(self, factory, instr_log_dir=None, flavour=None):
+        super().__init__(factory.name, instr_log_dir=instr_log_dir, flavour=flavour)
+
+        self.factory = factory
+
+        # Give a way for the executioner to return messages
+        self._executor.add_return_msg_func(self._return_msg)
+
+        # Give the executioner a handle to the factory
+        self._executor.add_factory(self.factory)
+
+    @property
+    def protocol(self):
+        return self._protocol
+
+    @protocol.setter
+    def protocol(self, protocol):
+        self._protocol = protocol
+
+    @inlineCallbacks
+    def handle_netqasm_message(self, msg_id, msg):
+        yield from super().handle_netqasm_message(
+            msg_id=msg_id,
+            msg=msg,
+        )
+
+    @classmethod
+    def _get_executor_class(cls, flavour=None):
+        return VanillaSimulaQronExecutioner
+
+    def _mark_message_finished(self, msg_id, msg):
+        ret_msg = MsgDoneMessage(msg_id=msg_id)
+        self._return_msg(msg=ret_msg)
+
+    def stop(self):
+        self.factory.stop()
+
+    def _return_msg(self, msg):
+        """Return a message to the host"""
+        assert self._protocol is not None, "Seems protocol of handler has not yet been set"
+        self._logger.debug(f"sending message {msg} to host")
+        self.protocol._return_msg(msg=bytes(msg))
diff --git a/simulaqron/network.py b/simulaqron/network.py
index 47fa925..279fd6f 100644
--- a/simulaqron/network.py
+++ b/simulaqron/network.py
@@ -29,16 +29,16 @@
 
 import time
 import random
-import logging
 import multiprocessing as mp
 import networkx as nx
 from timeit import default_timer as timer
 
+from netqasm.logging.glob import get_netqasm_logger, get_log_level
+
 from simulaqron.toolbox.manage_nodes import NetworksConfigConstructor
 from simulaqron.settings import simulaqron_settings
-from simulaqron.run.startNode import main as start_node
-from simulaqron.run.startCQC import main as start_cqc
-from cqc.pythonLib import CQCConnection
+from simulaqron.start import start_vnode, start_qnodeos
+from simulaqron.sdk import SimulaQronConnection
 
 #########################################################################################
 # Network class, sets up (part of) a simulated network.                                 #
@@ -72,6 +72,9 @@ def __init__(self, name=None, nodes=None, topology=None, network_config_file=Non
         else:
             self.name = name
 
+        self.processes = []
+        self._logger = get_netqasm_logger(f"{self.__class__.__name__}({self.name})")
+
         if network_config_file is None:
             self._network_config_file = simulaqron_settings.network_config_file
         else:
@@ -116,7 +119,6 @@ def __init__(self, name=None, nodes=None, topology=None, network_config_file=Non
                                          "If you wish to overwrite the current network in the file, use the"
                                          "--new flag.".format(node_name, self.name, self._network_config_file))
 
-        self.processes = []
         self._setup_processes()
 
     @property
@@ -128,16 +130,18 @@ def running(self):
             return True
         for node in self.nodes:
             try:
-                cqc = CQCConnection(node, retry_connection=False, network_name=self.name)
+                SimulaQronConnection.try_connection(
+                    name=node,
+                    network_name=self.name,
+                )
             except ConnectionRefusedError:
                 self._running = False
                 break
             except Exception as err:
-                logging.exception("Got unexpected exception when trying to connect: {}".format(err))
+                self._logger.exception("Got unexpected exception when trying to connect: {}".format(err))
                 raise err
-            else:
-                cqc.close()
         else:
+            self._logger.debug(f"Network {self.name} is now running")
             self._running = True
 
         return self._running
@@ -152,24 +156,24 @@ def _setup_processes(self):
         mp.set_start_method("spawn", force=True)
         for node in self.nodes:
             process_virtual = mp.Process(
-                target=start_node, args=(node, self.name), name="VirtNode {}".format(node)
+                target=start_vnode, args=(node, self.name, get_log_level()), name="VirtNode {}".format(node)
             )
-            process_cqc = mp.Process(
-                target=start_cqc, args=(node, self.name), name="CQCNode {}".format(node)
+            process_qnodeos = mp.Process(
+                target=start_qnodeos, args=(node, self.name, get_log_level()), name="QnodeOSNode {}".format(node)
             )
-            self.processes += [process_virtual, process_cqc]
+            self.processes += [process_virtual, process_qnodeos]
 
-    def start(self, wait_until_running=True):
+    def start(self, wait_until_running=False):
         """
         Starts the network.
         The boolean flag 'wait_until_running' can be used whether the call to this method should
         blog until the all processes are running and are connected or not.
         :param wait_until_running: bool
         """
-        logging.info("Starting network with name {}".format(self.name))
+        self._logger.info("Starting network with name {}".format(self.name))
         for p in self.processes:
             if not p.is_alive():
-                logging.debug("Starting process {}".format(p.name))
+                self._logger.debug("Starting process {}".format(p.name))
                 p.deamon = True
                 p.start()
 
@@ -186,18 +190,15 @@ def stop(self):
         """
         Stops the network.
         """
-        if not self._running:
-            return
-
         self._running = False
-        logging.info("Stopping network with name {}".format(self.name))
+        self._logger.info("Stopping network with name {}".format(self.name))
         for p in self.processes:
             while p.is_alive():
                 time.sleep(0.1)
                 try:
                     p.terminate()
                 except Exception as err:
-                    print("Could not terminate one of the processes in the network due to error: {}".format(err))
+                    self._logger("Could not terminate one of the processes in the network due to error: {}".format(err))
 
 
 def construct_topology_config(topology, nodes):
diff --git a/simulaqron/run/__init__.py b/simulaqron/run/__init__.py
index e69de29..61b49f0 100644
--- a/simulaqron/run/__init__.py
+++ b/simulaqron/run/__init__.py
@@ -0,0 +1,1 @@
+from .run import run_applications
diff --git a/simulaqron/run/run.py b/simulaqron/run/run.py
new file mode 100644
index 0000000..22e3962
--- /dev/null
+++ b/simulaqron/run/run.py
@@ -0,0 +1,166 @@
+import logging
+import os
+from concurrent.futures import ProcessPoolExecutor as Pool
+from importlib import reload
+from time import sleep
+
+from netqasm.logging.glob import get_netqasm_logger
+from netqasm.logging.output import (reset_struct_loggers,
+                                    save_all_struct_loggers)
+from netqasm.runtime import env, process_logs
+from netqasm.runtime.app_config import AppConfig
+from netqasm.runtime.application import ApplicationInstance
+from netqasm.runtime.settings import Formalism
+from netqasm.sdk.classical_communication import reset_socket_hub
+from netqasm.sdk.config import LogConfig
+from netqasm.sdk.shared_memory import SharedMemoryManager
+from netqasm.util.yaml import dump_yaml
+from simulaqron.network import Network
+from simulaqron.settings import SimBackend, simulaqron_settings
+from simulaqron.toolbox import has_module
+
+logger = get_netqasm_logger()
+
+# TODO similar code to squidasm.run.run, make base-class and subclasses?
+
+
+_SIMULAQRON_BACKENDS = {
+    Formalism.STAB: SimBackend.STABILIZER,
+    Formalism.KET: SimBackend.PROJECTQ,
+    Formalism.DM: SimBackend.QUTIP,
+}
+
+
+def as_completed(futures, names=None, sleep_time=0):
+    futures = list(futures)
+    if names is not None:
+        names = list(names)
+    while len(futures) > 0:
+        for i, future in enumerate(futures):
+            if future.done():
+                futures.pop(i)
+                if names is None:
+                    yield future
+                else:
+                    name = names.pop(i)
+                    yield future, name
+        if sleep_time > 0:
+            sleep(sleep_time)
+
+
+def reset(save_loggers=False):
+    if save_loggers:
+        save_all_struct_loggers()
+    SharedMemoryManager.reset_memories()
+    reset_socket_hub()
+    reset_struct_loggers()
+    # Reset logging
+    logging.shutdown()
+    reload(logging)
+
+
+def check_sim_backend(sim_backend):
+    if sim_backend in [SimBackend.PROJECTQ, SimBackend.QUTIP]:
+        assert has_module.main(sim_backend.value), f"To use {sim_backend} as backend you need to install the package"
+
+
+def run_sim_backend(node_names, sim_backend):
+    logger.debug(f"Starting simulaqron sim_backend process with nodes {node_names}")
+    check_sim_backend(sim_backend=sim_backend)
+    simulaqron_settings.sim_backend = sim_backend.value
+    network = Network(name="default", nodes=node_names, force=True, new=True)
+    network.start()
+    return network
+
+
+def run_applications(
+    app_instance: ApplicationInstance,
+    num_rounds=1,
+    network_cfg=None,
+    log_cfg=None,
+    results_file=None,
+    formalism=Formalism.KET,
+    post_function=None,
+    flavour=None,
+    enable_logging=True,
+    hardware=None,
+    use_app_config=True,  # whether to give app_config as argument to app's main()
+):
+    """Executes functions containing application scripts,
+
+    Parameters
+    ----------
+    applications : dict
+        Keys should be names of nodes
+        Values should be the functions
+    """
+    # app_names = [app_cfg.app_name for app_cfg in app_cfgs]
+    app_names = [program.party for program in app_instance.app.programs]
+    sim_backend = _SIMULAQRON_BACKENDS[formalism]
+
+    if enable_logging:
+        log_cfg = LogConfig() if log_cfg is None else log_cfg
+        app_instance.logging_cfg = log_cfg
+
+        log_dir = (
+            os.path.abspath("./log") if log_cfg.log_dir is None else log_cfg.log_dir
+        )
+        if not os.path.exists(log_dir):
+            os.mkdir(log_dir)
+
+        timed_log_dir = env.get_timed_log_dir(log_dir)
+        app_instance.logging_cfg.log_subroutines_dir = timed_log_dir
+        app_instance.logging_cfg.comm_log_dir = timed_log_dir
+
+    with Pool(len(app_names)) as executor:
+        # Start the backend process
+        network = run_sim_backend(app_names, sim_backend=sim_backend)
+
+        # Start the application processes
+        app_futures = []
+
+        programs = app_instance.app.programs
+        for program in programs:
+            inputs = app_instance.program_inputs[program.party]
+            if use_app_config:
+                app_cfg = AppConfig(
+                    app_name=program.party,
+                    node_name=program.party,  # node name should be same as app name
+                    main_func=program.entry,
+                    log_config=app_instance.logging_cfg,
+                    inputs=inputs,
+                )
+                inputs["app_config"] = app_cfg
+            future = executor.submit(program.entry, **inputs)
+            app_futures.append(future)
+
+        # for app_cfg in app_cfgs:
+        #     inputs = app_cfg.inputs
+        #     if use_app_config:
+        #         inputs['app_config'] = app_cfg
+        #     future = executor.submit(app_cfg.main_func, **inputs)
+        #     app_futures.append(future)
+
+        # Join the application processes and the backend
+        names = [f'app_{app_name}' for app_name in app_names]
+        results = {}
+        for future, name in as_completed(app_futures, names=names):
+            results[name] = future.result()
+        # if results_file is not None:
+        #     save_results(results=results, results_file=results_file)
+        if enable_logging:
+            assert timed_log_dir is not None
+            path = os.path.join(timed_log_dir, "results.yaml")
+            dump_yaml(data=results, file_path=path)
+
+        network.stop()
+
+    if enable_logging:
+        process_logs.make_last_log(log_dir=timed_log_dir)
+
+    reset(save_loggers=True)
+    return [results]
+
+
+def save_results(results, results_file):
+    dump_yaml(data=results, file_path=results_file)
diff --git a/simulaqron/run/startCQC.py b/simulaqron/run/startCQC.py
deleted file mode 100755
index 88038df..0000000
--- a/simulaqron/run/startCQC.py
+++ /dev/null
@@ -1,137 +0,0 @@
-#!/usr/bin/env python
-import logging
-import sys
-import signal
-from twisted.internet import reactor
-from twisted.internet.error import ConnectionRefusedError, CannotListenError
-from twisted.spread import pb
-
-from simulaqron.cqc_backend.cqcFactory import CQCFactory
-from simulaqron.cqc_backend.cqcMessageHandler import SimulaqronCQCHandler
-from simulaqron.general.hostConfig import socketsConfig
-from simulaqron.settings import simulaqron_settings
-
-
-def init_register(virtRoot, myName, node):
-    """Retrieves the relevant root objects to talk to such remote connections"""
-    logging.debug("LOCAL %s: All connections set up.", myName)
-    # Set the virtual node
-    node.set_virtual_node(virtRoot)
-    # Start listening to CQC messages
-    setup_CQC_server(myName, node)
-
-
-def connect_to_virtNode(myName, cqc_factory, virtualNet):
-    """Trys to connect to local virtual node.
-
-    If connection is refused, we try again after a set amount of time
-    (specified in handle_connection_error)
-    """
-    logging.debug("LOCAL %s: Trying to connect to local virtual node.", myName)
-    virtual_node = virtualNet.hostDict[myName]
-    factory = pb.PBClientFactory()
-    # Connect
-    reactor.connectTCP(virtual_node.hostname, virtual_node.port, factory)
-    deferVirtual = factory.getRootObject()
-    # If connection succeeds do:
-    deferVirtual.addCallback(init_register, myName, cqc_factory)
-    # If connection fails do:
-    deferVirtual.addErrback(handle_connection_error, myName, cqc_factory, virtualNet)
-
-
-def handle_connection_error(reason, myName, cqc_factory, virtualNet):
-    """ Handles errors from trying to connect to local virtual node.
-
-    If a ConnectionRefusedError is raised another try will be made after
-    Settings.CONF_WAIT_TIME seconds. Any other error is raised again.
-    """
-    try:
-        reason.raiseException()
-    except ConnectionRefusedError:
-        logging.debug("LOCAL %s: Could not connect, trying again...", myName)
-        reactor.callLater(
-            simulaqron_settings.conn_retry_time,
-            connect_to_virtNode,
-            myName,
-            cqc_factory,
-            virtualNet,
-        )
-    except Exception as e:
-        logging.error(
-            "LOCAL %s: Critical error when connection to local virtual node: %s",
-            myName,
-            e,
-        )
-        reactor.stop()
-
-
-def setup_CQC_server(myName, cqc_factory):
-    """Setup CQC server to handle remote on the classical communication network."""
-    try:
-        logging.debug(
-            "LOCAL %s: Starting local classical communication server.", myName
-        )
-        myHost = cqc_factory.host
-        myHost.root = cqc_factory
-        myHost.factory = cqc_factory
-        reactor.listenTCP(myHost.port, myHost.factory)
-    except CannotListenError:
-        logging.error(
-            "LOCAL {}: CQC server address ({}) is already in use.".format(
-                myName, myHost.port
-            )
-        )
-        reactor.stop()
-    except Exception as e:
-        logging.error(
-            "LOCAL {}: Critical error when starting CQC server: {}".format(myName, e)
-        )
-        reactor.stop()
-
-
-def sigterm_handler(_signo, _stack_frame):
-    reactor.stop()
-
-
-def main(myName, network_name="default"):
-    """Start the indicated backend CQC Server"""
-    signal.signal(signal.SIGTERM, sigterm_handler)
-    signal.signal(signal.SIGINT, sigterm_handler)
-
-    logging.basicConfig(
-        format="%(asctime)s:%(levelname)s:%(message)s",
-        level=simulaqron_settings.log_level,
-    )
-
-    if simulaqron_settings.network_config_file is not None:
-        # Since version 3.0.0 a single config file is used
-        virtualFile = simulaqron_settings.network_config_file
-        cqcFile = simulaqron_settings.network_config_file
-    else:
-        # This file defines the network of virtual quantum nodes
-        virtualFile = simulaqron_settings.vnode_file
-
-        # This file defines the network of CQC servers interfacing to virtual quantum nodes
-        cqcFile = simulaqron_settings.cqc_file
-
-    # Read configuration files for the virtual quantum, as well as the classical network
-    virtualNet = socketsConfig(virtualFile, network_name=network_name, config_type="vnode")
-    cqcNet = socketsConfig(cqcFile, network_name=network_name, config_type="cqc")
-
-    # Check if we are in the host-dictionary
-    if myName in cqcNet.hostDict:
-        myHost = cqcNet.hostDict[myName]
-        cqc_factory = CQCFactory(myHost, myName, cqcNet, SimulaqronCQCHandler, network_name=network_name)
-    else:
-        logging.error("LOCAL %s: Cannot start classical communication servers.", myName)
-        return
-
-        # Connect to the local virtual node simulating the "local" qubits
-    connect_to_virtNode(myName, cqc_factory, virtualNet)
-
-    # Run reactor
-    reactor.run()
-
-
-if __name__ == '__main__':
-    main(sys.argv[1])
diff --git a/simulaqron/sdk/__init__.py b/simulaqron/sdk/__init__.py
new file mode 100644
index 0000000..024920a
--- /dev/null
+++ b/simulaqron/sdk/__init__.py
@@ -0,0 +1,3 @@
+from .connection import SimulaQronConnection
+from .socket import Socket
+from .broadcast_channel import BroadcastChannel
diff --git a/simulaqron/sdk/broadcast_channel.py b/simulaqron/sdk/broadcast_channel.py
new file mode 100644
index 0000000..10feb68
--- /dev/null
+++ b/simulaqron/sdk/broadcast_channel.py
@@ -0,0 +1,6 @@
+from netqasm.sdk.classical_communication.broadcast_channel import BroadcastChannelBySockets
+from .socket import Socket
+
+
+class BroadcastChannel(BroadcastChannelBySockets):
+    _socket_class = Socket
diff --git a/simulaqron/sdk/connection.py b/simulaqron/sdk/connection.py
new file mode 100644
index 0000000..eabf1ea
--- /dev/null
+++ b/simulaqron/sdk/connection.py
@@ -0,0 +1,358 @@
+import socket
+import time
+from typing import Type
+
+from netqasm.backend.messages import (ErrorMessage, MessageHeader,
+                                      MsgDoneMessage, ReturnArrayMessage,
+                                      ReturnRegMessage, deserialize_return_msg)
+from netqasm.lang.ir import GenericInstr
+from netqasm.lang.operand import Address, Register
+from netqasm.logging.glob import get_netqasm_logger
+from netqasm.sdk.connection import BaseNetQASMConnection
+from netqasm.sdk.network import NetworkInfo
+from netqasm.sdk.shared_memory import SharedMemoryManager
+from simulaqron.general import SimUnsupportedError
+from simulaqron.general.host_config import (SocketsConfig,
+                                            get_node_id_from_net_config)
+from simulaqron.settings import SimBackend, simulaqron_settings
+
+logger = get_netqasm_logger("SimulaQronConnection")
+
+
+class SimulaQronConnection(BaseNetQASMConnection):
+
+    NON_STABILIZER_INSTR = [GenericInstr.T]
+
+    def __init__(
+        self,
+        app_name,
+        app_id=None,
+        max_qubits=5,
+        log_config=None,
+        epr_sockets=None,
+        compiler=None,
+        socket_address=None,
+        conn_retry_time=0.1,
+        network_name=None,
+    ):
+        super().__init__(
+            app_name=app_name,
+            # NOTE currently node_name and app_name are the same in simulaqron
+            node_name=app_name,
+            app_id=app_id,
+            max_qubits=max_qubits,
+            log_config=log_config,
+            epr_sockets=epr_sockets,
+            compiler=compiler,
+            _init_app=False,
+            _setup_epr_sockets=False,
+        )
+
+        self._qnodeos_net, self._socket = self._create_socket(
+            name=self.node_name,
+            socket_address=socket_address,
+            network_name=network_name,
+            retry_time=conn_retry_time,
+        )
+
+        # Next message ID
+        self._next_msg_id = 0
+
+        # Messages IDs we're waiting to be done
+        self._waiting_msg_ids = set()
+
+        # Keep track of finished msg IDs
+        self._done_msg_ids = set()
+
+        # Buffer for returned messages
+        self.buf = b""
+
+        self._shared_memory = SharedMemoryManager.create_shared_memory(app_name)
+
+        self._init_new_app(max_qubits=max_qubits)
+
+        self._setup_epr_sockets(epr_sockets=epr_sockets)
+
+    @staticmethod
+    def try_connection(
+        name,
+        socket_address=None,
+        network_name=None,
+    ):
+        # NOTE using retry_time=None causes an error to be raised of the connection cannot
+        # be established, which can be used to check if the connection is available
+        logger.debug("Trying if connection is up yet")
+        SimulaQronConnection._create_socket(
+            name=name,
+            socket_address=socket_address,
+            network_name=network_name,
+            retry_time=None,
+        )
+
+    @staticmethod
+    def _create_socket(
+        name,
+        socket_address=None,
+        network_name=None,
+        retry_time=0.1,
+    ):
+        # Get network configuraton and addresses
+        addr, qnodeos_net = SimulaQronConnection._setup_network_data(
+            name=name,
+            socket_address=socket_address,
+            network_name=network_name,
+        )
+
+        # Open a socket to the backend
+        qnodeos_socket = SimulaQronConnection._setup_socket(
+            name=name, addr=addr, retry_time=retry_time
+        )
+        return qnodeos_net, qnodeos_socket
+
+    @staticmethod
+    def _setup_network_data(
+        name,
+        socket_address,
+        network_name,
+    ):
+        addr = None
+        qnodeos_net = None
+        if socket_address is None:
+            qnodeos_net = _get_qnodeos_net_config(network_name=network_name)
+
+            # Host data
+            if name in qnodeos_net.hostDict:
+                myHost = qnodeos_net.hostDict[name]
+            else:
+                raise ValueError(
+                    "Host name '{}' is not in the qnodeos network".format(name)
+                )
+
+                # Get IP and port number
+            addr = myHost.addr
+
+        if socket_address is not None:
+            hostname, port = socket_address
+            assert isinstance(hostname, str), "hostname should be a string"
+            assert isinstance(port, int), "port should be an int"
+            addrs = socket.getaddrinfo(
+                hostname, port, proto=socket.IPPROTO_TCP, family=socket.AF_INET
+            )
+            addr = addrs[0]
+
+        return addr, qnodeos_net
+
+    @staticmethod
+    def _setup_socket(name, addr, retry_time=0.1):
+        qnodeos_socket = None
+        while True:
+            try:
+                logger.debug(
+                    f"App {name} : Trying to connect to NetQASM server (at {addr[-1]})"
+                )
+
+                qnodeos_socket = socket.socket(addr[0], addr[1], addr[2])
+                qnodeos_socket.connect(addr[4])
+                break
+            except ConnectionRefusedError as err:
+                if retry_time is None or retry_time == 0:
+                    raise err
+                logger.debug(
+                    "App {} : Could not connect to  NetQASM server, trying again...".format(
+                        name
+                    )
+                )
+                time.sleep(retry_time)
+                qnodeos_socket.close()
+            except Exception as err:
+                logger.exception(
+                    "App {} : Critical error when connection to NetQASM server: {}".format(
+                        name, err
+                    )
+                )
+                qnodeos_socket.close()
+                raise err
+        logger.debug(
+            "App {} : Could not connect to  NetQASM server, trying again...".format(
+                name
+            )
+        )
+        return qnodeos_socket
+
+    def _get_network_info(self) -> Type[NetworkInfo]:
+        return SimulaQronNetworkInfo
+
+    def _commit_serialized_message(self, raw_msg, block=True, callback=None):
+        """Commit a message to the backend/qnodeos"""
+        msg_id = self._get_new_msg_id()
+        self._waiting_msg_ids.add(msg_id)
+        length = MessageHeader.len() + len(raw_msg)
+        msg_hdr = MessageHeader(id=msg_id, length=length)
+        self._socket.send(bytes(msg_hdr) + raw_msg)
+        if callback is not None:
+            raise NotImplementedError("Callback not yet implemented")
+        if block:
+            self._wait_for_done(msg_id=msg_id)
+
+    def _wait_for_done(self, msg_id=None):
+        """Waits for a message to be declared done by qnodeos.
+        If `msg_id` is None (default), then we wait once for any message to be done.
+        The ID of this message is then returned.
+        """
+        if msg_id is None:
+            self._logger.debug("Waiting for any msg to be done")
+        else:
+            self._logger.debug(f"Waiting for msg ID {msg_id}")
+        while True:
+            done_msg_id = self._handle_reply()
+            if msg_id is None:
+                # Finished waiting for any message
+                break
+            elif msg_id == done_msg_id:
+                # Finished waiting for specified message
+                break
+            else:
+                # Other message done, not the one we're waiting for
+                # Wait for another don
+                continue
+        self._logger.debug(f"Received done for msg ID {done_msg_id}")
+
+    def _read_more_data(self):
+        """Reads in some more data on the socket to qnodeos"""
+        data = self._socket.recv(1024)
+        if self.buf:
+            self.buf += data
+        else:
+            self.buf = data
+        self._logger.debug(f"Got new data {data} on socket to qnodeos")
+
+    def _handle_reply(self):
+        """Handle all next replies until a done message and return the msg ID for the done"""
+        # Try to read next message from the buffer otherwise read some more and try again
+        try:
+            ret_msg = deserialize_return_msg(self.buf)
+        except ValueError:
+            # Incomplete message
+            self._logger.debug("Incomplete message")
+            time.sleep(0.1)
+            self._read_more_data()
+            return self._handle_reply()
+
+        # Remove the data of this message from the buffer
+        self.buf = self.buf[len(ret_msg):]
+
+        self._logger.debug(f"Got message {ret_msg}")
+        if isinstance(ret_msg, MsgDoneMessage):
+            self._waiting_msg_ids.remove(ret_msg.msg_id)
+            self._done_msg_ids.add(ret_msg.msg_id)
+            return ret_msg.msg_id
+        elif isinstance(ret_msg, ReturnRegMessage):
+            self._update_shared_memory(
+                entry=Register.from_raw(raw=ret_msg.register),
+                value=ret_msg.value,
+            )
+        elif isinstance(ret_msg, ReturnArrayMessage):
+            self._update_shared_memory(
+                entry=Address(address=ret_msg.address),
+                value=ret_msg.values,
+            )
+        elif isinstance(ret_msg, ErrorMessage):
+            raise RuntimeError(f"Received error message from backend: {ret_msg}")
+        else:
+            raise NotImplementedError(f"Unknown return message of type {type(ret_msg)}")
+        # Continue handling replies until a done
+        return self._handle_reply()
+
+    def block(self):
+        while len(self._waiting_msg_ids) > 0:
+            self._logger.debug(
+                f"Blocking and waiting for msg IDs {self._waiting_msg_ids}"
+            )
+            # Wait for any msg to be done
+            self._wait_for_done()
+        self._logger.debug("All messages done, finished blocking")
+
+    def _update_shared_memory(self, entry, value):
+        shared_memory = self.shared_memory
+        if isinstance(entry, Register):
+            shared_memory.set_register(entry, value)
+        elif isinstance(entry, Address):
+            address = entry.address
+            shared_memory.init_new_array(address=address, new_array=value)
+        else:
+            raise TypeError(
+                f"Cannot update shared memory with entry specified as {entry}"
+            )
+
+    def add_single_qubit_commands(self, instr, qubit_id):
+        # NOTE override to check that formalism supports operation
+        if instr in self.NON_STABILIZER_INSTR:
+            if simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+                raise SimUnsupportedError(
+                    f"Cannot perform instr {instr} when using stabilizer formalism"
+                )
+        super().add_single_qubit_commands(instr=instr, qubit_id=qubit_id)
+
+    def add_single_qubit_rotation_commands(
+        self, instruction, virtual_qubit_id, n=0, d=0, angle=None
+    ):
+        # NOTE override to check that formalism supports operation
+        if simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+            raise SimUnsupportedError(
+                "Cannot perform rotations when using stabilizer formalism"
+            )
+        super().add_single_qubit_rotation_commands(
+            instruction=instruction,
+            virtual_qubit_id=virtual_qubit_id,
+            n=n,
+            d=d,
+            angle=angle,
+        )
+
+    def _is_done(self, msg_id):
+        return msg_id in self._done_msg_ids
+
+    def _get_new_msg_id(self):
+        msg_id = self._next_msg_id
+        self._next_msg_id += 1
+        return msg_id
+
+
+def _get_qnodeos_net_config(network_name):
+    network_config_file = simulaqron_settings.network_config_file
+    qnodeos_net = SocketsConfig(
+        network_config_file, network_name=network_name, config_type="qnodeos"
+    )
+
+    return qnodeos_net
+
+
+class SimulaQronNetworkInfo(NetworkInfo):
+    @classmethod
+    def _get_node_id(cls, node_name):
+        """Returns the node id for the node with the given name"""
+        # TODO always use network name "default"?
+        _qnodeos_net = _get_qnodeos_net_config(network_name="default")
+        return get_node_id_from_net_config(_qnodeos_net, node_name)
+
+    @classmethod
+    def _get_node_name(cls, node_id):
+        """Returns the node name for the node with the given ID"""
+        # TODO always use network name "default"?
+        _qnodeos_net = _get_qnodeos_net_config(network_name="default")
+        for node_name, host in _qnodeos_net.hostDict.items():
+            if node_id == host.ip:
+                return node_name
+        raise KeyError("Unknown node ID {node_id}")
+
+    @classmethod
+    def get_node_id_for_app(cls, app_name):
+        """Returns the node id for the app with the given name"""
+        # NOTE app_name and node_name are for now the same in simulaqron
+        return cls._get_node_id(node_name=app_name)
+
+    @classmethod
+    def get_node_name_for_app(cls, app_name):
+        """Returns the node name for the app with the given name"""
+        # NOTE app_name and node_name are for now the same in simulaqron
+        return app_name
diff --git a/simulaqron/sdk/socket.py b/simulaqron/sdk/socket.py
new file mode 100644
index 0000000..f133c52
--- /dev/null
+++ b/simulaqron/sdk/socket.py
@@ -0,0 +1,151 @@
+import pickle
+import socket
+import time
+
+from netqasm.logging.glob import get_netqasm_logger
+from netqasm.sdk.classical_communication.socket import Socket as _Socket
+from simulaqron.general.host_config import SocketsConfig
+from simulaqron.settings import simulaqron_settings
+
+
+class Socket(_Socket):
+
+    RETRY_TIME = 0.1
+
+    def __init__(
+        self,
+        app_name,
+        remote_app_name,
+        socket_id=0,
+        timeout=None,
+        use_callbacks=False,
+        network_name="default",
+        log_config=None,
+    ):
+        assert socket_id == 0, (
+            "SimulaQron socket does not support setting socket ID, this is instead done in the config file"
+        )
+        self._node_name = app_name
+        self._remote_node_name = remote_app_name
+        self._use_callbacks = use_callbacks
+        self._network_name = network_name
+
+        self._logger = get_netqasm_logger(f"{self.__class__.__name__}({app_name} <-> {remote_app_name})")
+        self._app_socket = None
+
+        self._connect()
+
+    def __del__(self):
+        if self._app_socket:
+            self._app_socket.close()
+
+    def send(self, msg):
+        """Sends a message to the remote node."""
+        self._logger.debug(f"Sending msg '{msg}'")
+        raw_msg = self._serialize_msg(msg=msg)
+        self._app_socket.send(raw_msg)
+
+    def send_structured(self, msg):
+        self._logger.debug(f"Sending structured msg '{msg}'")
+        raw_msg = self._serialize_structured_msg(msg=msg)
+        self._app_socket.send(raw_msg)
+
+    def send_silent(self, msg):
+        self.send(msg)
+
+    def recv(self, block=True, maxsize=1024):
+        """Receive a message from the remote node."""
+        self._logger.debug("Receiving msg")
+        self._app_socket.setblocking(block)
+        raw_msg = self._app_socket.recv(maxsize)
+        if not block and not raw_msg:
+            raise RuntimeError("No message to receive (not blocking)")
+        msg = self._deserialize_msg(raw_msg=raw_msg)
+        self._logger.debug(f"Msg '{msg}' received")
+        return msg
+
+    def recv_structured(self, block=True, maxsize=1024):
+        self._logger.debug("Receiving structured msg")
+        self._app_socket.setblocking(block)
+        raw_msg = self._app_socket.recv(maxsize)
+        if not block and not raw_msg:
+            raise RuntimeError("No message to receive (not blocking)")
+        msg = self._deserialize_structured_msg(raw_msg=raw_msg)
+        self._logger.debug(f"Msg '{msg}' received")
+        return msg
+
+    def recv_silent(self):
+        return self.recv()
+
+    @staticmethod
+    def _serialize_msg(msg):
+        return msg.encode('utf-8')
+
+    @staticmethod
+    def _deserialize_msg(raw_msg):
+        return raw_msg.decode('utf-8')
+
+    @staticmethod
+    def _serialize_structured_msg(msg):
+        return pickle.dumps(msg)
+
+    @ staticmethod
+    def _deserialize_structured_msg(raw_msg):
+        return pickle.loads(raw_msg)
+
+    @ property
+    def is_server(self):
+        # Server will always be the "first"
+        return self._node_name < self._remote_node_name
+
+    def _connect(self):
+        if self.is_server:
+            server_name = self._node_name
+        else:
+            server_name = self._remote_node_name
+        addr = self._get_addr_info(name=server_name)
+        app_socket = socket.socket(addr[0], addr[1], addr[2])
+
+        if self.is_server:
+            self._logger.debug("Trying to open application socket as server")
+            app_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+            while True:
+                try:
+                    app_socket.bind(addr[4])
+                except OSError as err:
+                    self._logger.debug(f"Could not bind socket since: {err}\n"
+                                       f"Trying again in {self.RETRY_TIME}s...")
+                    time.sleep(self.RETRY_TIME)
+                else:
+                    break
+            app_socket.listen(1)
+            conn, _ = app_socket.accept()
+            self._app_socket = conn
+        else:
+            self._logger.debug("Trying to open application socket as client")
+            while True:
+                try:
+                    app_socket.connect(addr[4])
+                except ConnectionRefusedError:
+                    self._logger.debug(
+                        f"Could not open application socket, "
+                        f"trying again in {self.RETRY_TIME}s..."
+                    )
+                    time.sleep(self.RETRY_TIME)
+                else:
+                    break
+            self._app_socket = app_socket
+
+        self._logger.debug("Application socket opened")
+
+    def _get_addr_info(self, name):
+        app_net = self._get_app_net_config()
+        remote_host = app_net.hostDict.get(name)
+        if remote_host is None:
+            raise ValueError(f"Host name '{name}' is not in the app network")
+        return remote_host.addr
+
+    def _get_app_net_config(self):
+        network_config_file = simulaqron_settings.network_config_file
+        app_net = SocketsConfig(network_config_file, network_name=self._network_name, config_type="app")
+        return app_net
diff --git a/simulaqron/settings.py b/simulaqron/settings.py
index b9d03fc..d251c23 100644
--- a/simulaqron/settings.py
+++ b/simulaqron/settings.py
@@ -30,10 +30,10 @@
 #########################
 # SETTINGS FOR SIMULAQRON
 #########################
-import logging
-import json
-
 import os
+import json
+import logging
+from enum import Enum
 
 from simulaqron.toolbox import get_simulaqron_path
 
@@ -41,23 +41,10 @@
 config_folder = os.path.join(simulaqron_path, "config")
 
 
-# class _DefaultSettings:
-#     CONF_MAXQUBITS = 20
-#     CONF_MAXREGS = 1000
-#     CONF_WAIT_TIME = 0.5
-#     CONF_RECV_TIMEOUT = 100  # (x 100 ms)
-#     CONF_RECV_EPR_TIMEOUT = 100  # (x 100 ms)
-#     CONF_WAIT_TIME_RECV = 0.1  # (seconds)
-#     CONF_LOGGING_LEVEL_BACKEND = logging.WARNING
-#     CONF_LOGGING_LEVEL_FRONTEND = logging.WARNING
-#     CONF_BACKEND = "stabilizer"
-#     CONF_APP_FILE = os.path.join(config_folder, "appNodes.cfg")
-#     CONF_CQC_FILE = os.path.join(config_folder, "cqcNodes.cfg")
-#     CONF_VNODE_FILE = os.path.join(config_folder, "virtualNodes.cfg")
-#     CONF_NODES_FILE = os.path.join(config_folder, "Nodes.cfg")
-#     CONF_TOPOLOGY_FILE = ""
-#     CONF_NOISY_QUBITS = False
-#     CONF_T1 = 1
+class SimBackend(Enum):
+    STABILIZER = "stabilizer"
+    PROJECTQ = "projectq"
+    QUTIP = "qutip"
 
 
 class Config:
@@ -78,13 +65,8 @@ class Config:
         "recv_timeout": 100,  # (x 100 ms)
         "recv_retry_time": 0.1,  # (seconds)
         "log_level": logging.WARNING,
-        "backend": "stabilizer",
+        "sim_backend": SimBackend.STABILIZER.value,
         "network_config_file": os.path.join(config_folder, "network.json"),
-        "app_file": None,
-        "cqc_file": None,
-        "vnode_file": None,
-        "nodes_file": None,
-        "topology_file": None,
         "noisy_qubits": False,
         "t1": 1.0
     }
@@ -160,12 +142,12 @@ def _read_user(self, _read_user):
 
     @property
     @Decorator.get_setting
-    def backend(self):
+    def sim_backend(self):
         pass
 
-    @backend.setter
+    @sim_backend.setter
     @Decorator.set_setting
-    def backend(self, backend):
+    def sim_backend(self, sim_backend):
         pass
 
     @property
@@ -238,56 +220,6 @@ def network_config_file(self):
     def network_config_file(self, app_file):
         pass
 
-    @property
-    @Decorator.get_setting
-    def topology_file(self):
-        pass
-
-    @topology_file.setter
-    @Decorator.set_setting
-    def topology_file(self, topology_file):
-        pass
-
-    @property
-    @Decorator.get_setting
-    def app_file(self):
-        pass
-
-    @app_file.setter
-    @Decorator.set_setting
-    def app_file(self, app_file):
-        pass
-
-    @property
-    @Decorator.get_setting
-    def cqc_file(self):
-        pass
-
-    @cqc_file.setter
-    @Decorator.set_setting
-    def cqc_file(self, cqc_file):
-        pass
-
-    @property
-    @Decorator.get_setting
-    def vnode_file(self):
-        pass
-
-    @vnode_file.setter
-    @Decorator.set_setting
-    def vnode_file(self, vnode_file):
-        pass
-
-    @property
-    @Decorator.get_setting
-    def nodes_file(self):
-        pass
-
-    @nodes_file.setter
-    @Decorator.set_setting
-    def nodes_file(self, nodes_file):
-        pass
-
     @property
     @Decorator.get_setting
     def noisy_qubits(self):
diff --git a/simulaqron/sim_util.py b/simulaqron/sim_util.py
new file mode 100644
index 0000000..208b9dc
--- /dev/null
+++ b/simulaqron/sim_util.py
@@ -0,0 +1,9 @@
+from netqasm.logging.glob import get_netqasm_logger
+
+logger = get_netqasm_logger("sim_util")
+
+
+def get_qubit_state(qubit, reduced_dm=True):
+    """Currenlty we cannot get the qubit in SimulaQron, just return None"""
+    logger.warning("Cannot get the qubit state in SimulaQron")
+    return None
diff --git a/simulaqron/SimulaQron.py b/simulaqron/simulaqron.py
old mode 100755
new mode 100644
similarity index 95%
rename from simulaqron/SimulaQron.py
rename to simulaqron/simulaqron.py
index 6fc3bf3..24137e9
--- a/simulaqron/SimulaQron.py
+++ b/simulaqron/simulaqron.py
@@ -8,7 +8,7 @@
 
 import simulaqron
 from simulaqron.network import Network
-from simulaqron.settings import simulaqron_settings
+from simulaqron.settings import simulaqron_settings, SimBackend
 from simulaqron.toolbox.manage_nodes import NetworksConfigConstructor
 from simulaqron.toolbox.reset import main as reset_simulaqron
 
@@ -231,10 +231,10 @@ def default():
 
 
 @set.command()
-@click.argument('value', type=click.Choice(["stabilizer", "projectq", "qutip"]))
-def backend(value):
+@click.argument('value', type=click.Choice([b.value for b in SimBackend.value]))
+def sim_backend(value):
     """The backend to use (stabilizer, projectq, qutip)."""
-    simulaqron_settings.backend = value
+    simulaqron_settings.sim_backend = value
 
 
 @set.command()
@@ -314,9 +314,9 @@ def get():
 
 
 @get.command()
-def backend():
+def sim_backend():
     """The backend to use (stabilizer, projectq, qutip)."""
-    print(simulaqron_settings.backend)
+    print(simulaqron_settings.sim_backend)
 
 
 @get.command()
@@ -397,13 +397,13 @@ def nodes():
 @click.option('--hostname', type=str,
               help="The host name of the node, e.g. localhost (default) or 192.168.0.1\n"
                    "If you wish to have different components on different hostname,"
-                   "for example the cqc nodes on one computer and the virtual nodes on another,"
+                   "for example the qnodeos nodes on one computer and the virtual nodes on another,"
                    "you have to manually construct you config file.")
 @click.option('--app-port', type=int,
               help="Port number for the application.\n \
                     If not specified a random unused port between 8000 and 9000 will be used.")
-@click.option('--cqc-port', type=int,
-              help="Port number for the cqc server.\n \
+@click.option('--qnodeos-port', type=int,
+              help="Port number for the qnodeos server.\n \
                     If not specified a random unused port between 8000 and 9000 will be used.")
 @click.option('--vnode-port', type=int,
               help="Port number for the virtual node.\n \
@@ -418,7 +418,7 @@ def nodes():
     help="Force re-write of network_config_file.\n",
     is_flag=True,
 )
-def add(name, network_name=None, hostname=None, app_port=None, cqc_port=None, vnode_port=None, neighbors=None,
+def add(name, network_name=None, hostname=None, app_port=None, qnodeos_port=None, vnode_port=None, neighbors=None,
         force=False):
     """
     Add a node to the network.
@@ -438,8 +438,8 @@ def add(name, network_name=None, hostname=None, app_port=None, cqc_port=None, vn
         neighbors = [neighbor.strip() for neighbor in neighbors]
     networks_config = NetworksConfigConstructor(simulaqron_settings.network_config_file)
     networks_config.add_node(node_name=name, network_name=network_name,
-                             app_hostname=hostname, cqc_hostname=hostname, vnode_hostname=hostname,
-                             app_port=app_port, cqc_port=cqc_port, vnode_port=vnode_port,
+                             app_hostname=hostname, qnodeos_hostname=hostname, vnode_hostname=hostname,
+                             app_port=app_port, qnodeos_port=qnodeos_port, vnode_port=vnode_port,
                              neighbors=neighbors)
     networks_config.write_to_file()
 
diff --git a/simulaqron/start/__init__.py b/simulaqron/start/__init__.py
new file mode 100644
index 0000000..c38427e
--- /dev/null
+++ b/simulaqron/start/__init__.py
@@ -0,0 +1,2 @@
+from .start_vnode import main as start_vnode
+from .start_qnodeos import main as start_qnodeos
diff --git a/simulaqron/start/start_qnodeos.py b/simulaqron/start/start_qnodeos.py
new file mode 100644
index 0000000..ef326a4
--- /dev/null
+++ b/simulaqron/start/start_qnodeos.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+import sys
+import time
+import signal
+from timeit import default_timer as timer
+
+from twisted.internet import reactor
+from twisted.internet.error import ConnectionRefusedError, CannotListenError
+from twisted.spread import pb
+
+from netqasm.logging.glob import get_netqasm_logger, set_log_level
+
+from simulaqron.netqasm_backend.factory import NetQASMFactory
+from simulaqron.netqasm_backend.qnodeos import SubroutineHandler
+from simulaqron.general.host_config import SocketsConfig
+from simulaqron.settings import simulaqron_settings
+
+logger = get_netqasm_logger("start_qnodeos")
+
+_RETRY_TIME = 0.1
+_TIMEOUT = 10
+
+
+def init_register(virtRoot, myName, node):
+    """Retrieves the relevant root objects to talk to such remote connections"""
+    logger.debug("LOCAL %s: All connections set up.", myName)
+    # Set the virtual node
+    node.set_virtual_node(virtRoot)
+    # Start listening to NetQASM messages
+    setup_netqasm_server(myName, node)
+
+
+def connect_to_virtNode(myName, netqasm_factory, virtual_network):
+    """Trys to connect to local virtual node.
+
+    If connection is refused, we try again after a set amount of time
+    (specified in handle_connection_error)
+    """
+    logger.debug("LOCAL %s: Trying to connect to local virtual node.", myName)
+    virtual_node = virtual_network.hostDict[myName]
+    factory = pb.PBClientFactory()
+    # Connect
+    reactor.connectTCP(virtual_node.hostname, virtual_node.port, factory)
+    deferVirtual = factory.getRootObject()
+    # If connection succeeds do:
+    deferVirtual.addCallback(init_register, myName, netqasm_factory)
+    # If connection fails do:
+    deferVirtual.addErrback(handle_connection_error, myName, netqasm_factory, virtual_network)
+
+
+def handle_connection_error(reason, myName, netqasm_factory, virtual_network):
+    """ Handles errors from trying to connect to local virtual node.
+
+    If a ConnectionRefusedError is raised another try will be made after
+    Settings.CONF_WAIT_TIME seconds. Any other error is raised again.
+    """
+    try:
+        reason.raiseException()
+    except ConnectionRefusedError:
+        logger.debug("LOCAL %s: Could not connect, trying again...", myName)
+        reactor.callLater(
+            simulaqron_settings.conn_retry_time,
+            connect_to_virtNode,
+            myName,
+            netqasm_factory,
+            virtual_network,
+        )
+    except Exception as e:
+        logger.error(
+            "LOCAL %s: Critical error when connection to local virtual node: %s",
+            myName,
+            e,
+        )
+        reactor.stop()
+
+
+def setup_netqasm_server(myName, netqasm_factory):
+    """Setup NetQASM server to handle remote on the classical communication network."""
+    t_start = timer()
+    while timer() - t_start < _TIMEOUT:
+        try:
+            logger.debug(
+                "LOCAL %s: Starting local classical communication server.", myName
+            )
+            myHost = netqasm_factory.host
+            myHost.root = netqasm_factory
+            myHost.factory = netqasm_factory
+            reactor.listenTCP(myHost.port, myHost.factory)
+            break
+        except CannotListenError:
+            logger.error(
+                "LOCAL {}: NetQASM server address ({}) is already in use, trying again.".format(
+                    myName, myHost.port
+                )
+            )
+            time.sleep(_RETRY_TIME)
+        except Exception as e:
+            logger.error(
+                "LOCAL {}: Critical error when starting NetQASM server: {}".format(myName, e)
+            )
+            reactor.stop()
+    else:
+        reactor.stop()
+
+
+def sigterm_handler(_signo, _stack_frame):
+    reactor.stop()
+
+
+def main(myName, network_name="default", log_level="WARNING"):
+    """Start the indicated backend NetQASM Server"""
+    set_log_level(log_level)
+    logger.debug(f"Starting QNodeOS at {myName}")
+    signal.signal(signal.SIGTERM, sigterm_handler)
+    signal.signal(signal.SIGINT, sigterm_handler)
+
+    # Since version 3.0.0 a single config file is used
+    network_config_file = simulaqron_settings.network_config_file
+
+    # Read configuration files for the virtual quantum, as well as the classical network
+    virtual_network = SocketsConfig(network_config_file, network_name=network_name, config_type="vnode")
+    qnodeos_network = SocketsConfig(network_config_file, network_name=network_name, config_type="qnodeos")
+
+    # Check if we are in the host-dictionary
+    if myName in qnodeos_network.hostDict:
+        myHost = qnodeos_network.hostDict[myName]
+        logger.debug(f"Setting up QNodeOS protocol factory for {myName}")
+        netqasm_factory = NetQASMFactory(
+            myHost,
+            myName,
+            qnodeos_network,
+            SubroutineHandler,
+            network_name=network_name,
+        )
+    else:
+        logger.error("LOCAL %s: Cannot start classical communication servers.", myName)
+        return
+
+    # Connect to the local virtual node simulating the "local" qubits
+    logger.debug(f"Connect to virtual node {myName}")
+    connect_to_virtNode(myName, netqasm_factory, virtual_network)
+
+    # Run reactor
+    reactor.run()
+    logger.debug(f"Ending QNodeOS at {myName}")
+
+
+if __name__ == '__main__':
+    main(sys.argv[1])
diff --git a/simulaqron/run/startNode.py b/simulaqron/start/start_vnode.py
old mode 100755
new mode 100644
similarity index 77%
rename from simulaqron/run/startNode.py
rename to simulaqron/start/start_vnode.py
index c3e0e6b..9c588be
--- a/simulaqron/run/startNode.py
+++ b/simulaqron/start/start_vnode.py
@@ -29,34 +29,35 @@
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 import sys
-import logging
 import signal
+from functools import partial
 from twisted.internet import reactor
 
-from simulaqron.virtNode.virtual import backEnd
+from netqasm.logging.glob import get_netqasm_logger, set_log_level
+from simulaqron.virtual_node.virtual import Backend
 from simulaqron.settings import simulaqron_settings
 
+logger = get_netqasm_logger("start_vnode")
 
-def sigterm_handler(_signo, _stack_frame):
-    print("Shutting down Node")
+
+def sigterm_handler(name, _signo, _stack_frame):
+    logger.info("Shutting down Node")
     reactor.stop()
 
 
-def main(name, network_name="default"):
-    signal.signal(signal.SIGTERM, sigterm_handler)
-    signal.signal(signal.SIGINT, sigterm_handler)
+def main(name, network_name="default", log_level="WARNING"):
+    set_log_level(log_level)
+    signal.signal(signal.SIGTERM, partial(sigterm_handler, name))
+    signal.signal(signal.SIGINT, partial(sigterm_handler, name))
 
-    logging.basicConfig(
-        format="%(asctime)s:%(levelname)s:%(message)s",
-        level=simulaqron_settings.log_level,
-    )
-    logging.debug("Starting VIRTUAL NODE %s", name)
+    logger.debug("Starting VIRTUAL NODE %s", name)
     if simulaqron_settings.network_config_file is not None:
         virtualFile = simulaqron_settings.network_config_file
     else:
         virtualFile = simulaqron_settings.vnode_file
-    be = backEnd(name, virtualFile, network_name=network_name)
+    be = Backend(name, virtualFile, network_name=network_name)
     be.start(maxQubits=simulaqron_settings.max_qubits, maxRegisters=simulaqron_settings.max_registers)
+    logger.debug("Ending VIRTUAL NODE %s", name)
 
 
 if __name__ == "__main__":
diff --git a/simulaqron/toolbox/manage_nodes.py b/simulaqron/toolbox/manage_nodes.py
index bd7343b..236815b 100644
--- a/simulaqron/toolbox/manage_nodes.py
+++ b/simulaqron/toolbox/manage_nodes.py
@@ -3,16 +3,6 @@
 from contextlib import closing
 import socket
 
-from simulaqron.toolbox import get_simulaqron_path
-from simulaqron.settings import simulaqron_settings
-
-
-config_files = [simulaqron_settings.app_file, simulaqron_settings.cqc_file, simulaqron_settings.vnode_file]
-node_config_file = simulaqron_settings.nodes_file
-simulaqron_path = get_simulaqron_path.main()
-config_folder = "config"
-default_topology_file = os.path.join(simulaqron_path, config_folder, "topology.json")
-
 
 class NetworksConfigConstructor:
     def __init__(self, file_path=None):
@@ -32,8 +22,8 @@ def __init__(self, file_path=None):
             if os.path.exists(self.file_path):
                 self.read_from_file()
 
-    def add_node(self, node_name, network_name="default", app_hostname=None, cqc_hostname=None, vnode_hostname=None,
-                 app_port=None, cqc_port=None, vnode_port=None, neighbors=None):
+    def add_node(self, node_name, network_name="default", app_hostname=None, qnodeos_hostname=None, vnode_hostname=None,
+                 app_port=None, qnodeos_port=None, vnode_port=None, neighbors=None):
         """
         Adds a node with the given name to a network (default: "default").
         If hostnames are None they will default to 'localhost'.
@@ -46,14 +36,14 @@ def add_node(self, node_name, network_name="default", app_hostname=None, cqc_hos
             Name of the network (default: "default")
         :param app_hostname: str or None
             Hostname, e.g. localhost (default) or 192.168.0.1
-        :param cqc_hostname: str or None
+        :param qnodeos_hostname: str or None
             Hostname, e.g. localhost (default) or 192.168.0.1
         :param vnode_hostname: str or None
             Hostname, e.g. localhost (default) or 192.168.0.1
         :param app_port: int or None
             Port number for the application
-        :param cqc_port: int or None
-            Port number for the cqc server
+        :param qnodeos_port: int or None
+            Port number for the qnodeos server
         :param vnode_port: int or None
             Port number for the virtual node
         :param neighbors: (list of str) or None
@@ -63,7 +53,7 @@ def add_node(self, node_name, network_name="default", app_hostname=None, cqc_hos
         """
         if network_name is None:
             network_name = "default"
-        socket_addresses = [(app_hostname, app_port), (cqc_hostname, cqc_port), (vnode_hostname, vnode_port)]
+        socket_addresses = [(app_hostname, app_port), (qnodeos_hostname, qnodeos_port), (vnode_hostname, vnode_port)]
         for i, socket_address in enumerate(socket_addresses):
             hostname, port = socket_address
             if hostname is None:
@@ -80,16 +70,23 @@ def add_node(self, node_name, network_name="default", app_hostname=None, cqc_hos
             socket_addresses[i] = socket_address
 
         app_hostname, app_port = socket_addresses[0]
-        cqc_hostname, cqc_port = socket_addresses[1]
+        qnodeos_hostname, qnodeos_port = socket_addresses[1]
         vnode_hostname, vnode_port = socket_addresses[2]
         if network_name in self.networks:
-            self.networks[network_name].add_node(name=node_name, app_hostname=app_hostname, cqc_hostname=cqc_hostname,
-                                                 vnode_hostname=vnode_hostname, app_port=app_port, cqc_port=cqc_port,
-                                                 vnode_port=vnode_port, neighbors=neighbors)
+            self.networks[network_name].add_node(
+                name=node_name,
+                app_hostname=app_hostname,
+                qnodeos_hostname=qnodeos_hostname,
+                vnode_hostname=vnode_hostname,
+                app_port=app_port,
+                qnodeos_port=qnodeos_port,
+                vnode_port=vnode_port,
+                neighbors=neighbors,
+            )
         else:
             network = _NetworkConfig()
-            network.add_node(name=node_name, app_hostname=app_hostname, cqc_hostname=cqc_hostname,
-                             vnode_hostname=vnode_hostname, app_port=app_port, cqc_port=cqc_port,
+            network.add_node(name=node_name, app_hostname=app_hostname, qnodeos_hostname=qnodeos_hostname,
+                             vnode_hostname=vnode_hostname, app_port=app_port, qnodeos_port=qnodeos_port,
                              vnode_port=vnode_port, neighbors=neighbors)
             self.networks[network_name] = network
 
@@ -234,14 +231,18 @@ def read_from_file(self, file_path=None):
 
             for node_name, node_dict in nodes_dict.items():
                 app_hostname, app_port = node_dict["app_socket"]
-                cqc_hostname, cqc_port = node_dict["cqc_socket"]
+                qnodeos_hostname, qnodeos_port = node_dict["qnodeos_socket"]
                 vnode_hostname, vnode_port = node_dict["vnode_socket"]
-                socket_addresses = [(app_hostname, app_port), (cqc_hostname, cqc_port), (vnode_hostname, vnode_port)]
+                socket_addresses = [
+                    (app_hostname, app_port),
+                    (qnodeos_hostname, qnodeos_port),
+                    (vnode_hostname, vnode_port),
+                ]
                 for socket_address in socket_addresses:
                     if socket_address not in self.used_sockets:
                         self.used_sockets.append(socket_address)
-                node = _NodeConfig(name=node_name, app_hostname=app_hostname, cqc_hostname=cqc_hostname,
-                                   vnode_hostname=vnode_hostname, app_port=app_port, cqc_port=cqc_port,
+                node = _NodeConfig(name=node_name, app_hostname=app_hostname, qnodeos_hostname=qnodeos_hostname,
+                                   vnode_hostname=vnode_hostname, app_port=app_port, qnodeos_port=qnodeos_port,
                                    vnode_port=vnode_port)
                 network.nodes[node_name] = node
             self.networks[network_name] = network
@@ -296,7 +297,17 @@ def __init__(self):
         self.topology = None
         self.nodes = {}
 
-    def add_node(self, name, app_hostname, cqc_hostname, vnode_hostname, app_port, cqc_port, vnode_port, neighbors):
+    def add_node(
+        self,
+        name,
+        app_hostname,
+        qnodeos_hostname,
+        vnode_hostname,
+        app_port,
+        qnodeos_port,
+        vnode_port,
+        neighbors,
+    ):
         """
         Adds a node with the given name to a network (default: "default").
         If hostnames are None they will default to 'localhost'.
@@ -307,14 +318,14 @@ def add_node(self, name, app_hostname, cqc_hostname, vnode_hostname, app_port, c
             Name of the node, e.g. Alice
         :param app_hostname: str or None
             Hostname, e.g. localhost (default) or 192.168.0.1
-        :param cqc_hostname: str or None
+        :param qnodeos_hostname: str or None
             Hostname, e.g. localhost (default) or 192.168.0.1
         :param vnode_hostname: str or None
             Hostname, e.g. localhost (default) or 192.168.0.1
         :param app_port: int or None
             Port number for the application
-        :param cqc_port: int or None
-            Port number for the cqc server
+        :param qnodeos_port: int or None
+            Port number for the qnodeos server
         :param vnode_port: int or None
             Port number for the virtual node
         :param neighbors: (list of str) or None
@@ -332,9 +343,15 @@ def add_node(self, name, app_hostname, cqc_hostname, vnode_hostname, app_port, c
 
             self.topology[name] = neighbors
 
-        self.nodes[name] = _NodeConfig(name=name, app_hostname=app_hostname, cqc_hostname=cqc_hostname,
-                                       vnode_hostname=vnode_hostname, app_port=app_port, cqc_port=cqc_port,
-                                       vnode_port=vnode_port)
+        self.nodes[name] = _NodeConfig(
+            name=name,
+            app_hostname=app_hostname,
+            qnodeos_hostname=qnodeos_hostname,
+            vnode_hostname=vnode_hostname,
+            app_port=app_port,
+            qnodeos_port=qnodeos_port,
+            vnode_port=vnode_port,
+        )
 
     def to_dict(self):
         """
@@ -346,16 +363,16 @@ def to_dict(self):
 
 
 class _NodeConfig:
-    def __init__(self, name, app_hostname, cqc_hostname, vnode_hostname, app_port, cqc_port, vnode_port):
+    def __init__(self, name, app_hostname, qnodeos_hostname, vnode_hostname, app_port, qnodeos_port, vnode_port):
         """
         Used by _NetworkConfig to keep track of the config of a single node.
         """
         self.name = name
         self.app_hostname = app_hostname
-        self.cqc_hostname = cqc_hostname
+        self.qnodeos_hostname = qnodeos_hostname
         self.vnode_hostname = vnode_hostname
         self.app_port = app_port
-        self.cqc_port = cqc_port
+        self.qnodeos_port = qnodeos_port
         self.vnode_port = vnode_port
 
     def to_dict(self):
@@ -365,6 +382,6 @@ def to_dict(self):
         """
         return {
             "app_socket": [self.app_hostname, self.app_port],
-            "cqc_socket": [self.cqc_hostname, self.cqc_port],
+            "qnodeos_socket": [self.qnodeos_hostname, self.qnodeos_port],
             "vnode_socket": [self.vnode_hostname, self.vnode_port]
         }
diff --git a/simulaqron/toolbox/reset.py b/simulaqron/toolbox/reset.py
index a58acb2..6efda1d 100644
--- a/simulaqron/toolbox/reset.py
+++ b/simulaqron/toolbox/reset.py
@@ -1,13 +1,24 @@
+import os
+
 from simulaqron.settings import simulaqron_settings
 from simulaqron.toolbox.manage_nodes import NetworksConfigConstructor
 
 
-def main():
-    simulaqron_settings.default_settings()
+def check_config_files():
+    if not os.path.exists(simulaqron_settings.network_config_file):
+        _create_default_network_config()
+
+
+def _create_default_network_config():
     networks_config = NetworksConfigConstructor()
     networks_config.reset()
     networks_config.write_to_file(simulaqron_settings.network_config_file)
 
 
+def main():
+    simulaqron_settings.default_settings()
+    _create_default_network_config()
+
+
 if __name__ == '__main__':
     main()
diff --git a/simulaqron/toolbox/stabilizerStates.py b/simulaqron/toolbox/stabilizer_states.py
similarity index 100%
rename from simulaqron/toolbox/stabilizerStates.py
rename to simulaqron/toolbox/stabilizer_states.py
diff --git a/simulaqron/virtNode/__init__.py b/simulaqron/virtNode/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/virtNode/virtual.py b/simulaqron/virtNode/virtual.py
deleted file mode 100644
index 4482182..0000000
--- a/simulaqron/virtNode/virtual.py
+++ /dev/null
@@ -1,2081 +0,0 @@
-#
-# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import logging
-import random
-
-from collections import deque
-
-from twisted.spread import pb
-from twisted.internet import reactor
-from twisted.internet.defer import inlineCallbacks, DeferredLock, Deferred, DeferredList
-from twisted.internet.task import deferLater
-from twisted.internet.error import ConnectionRefusedError, CannotListenError
-from twisted.spread.pb import RemoteError
-
-from simulaqron.virtNode.basics import quantumError, noQubitError, virtNetError
-from simulaqron.virtNode.quantum import simulatedQubit
-from simulaqron.general.hostConfig import socketsConfig
-from simulaqron.settings import simulaqron_settings
-
-if simulaqron_settings.backend == "qutip":
-    from simulaqron.virtNode.qutipSimulator import qutipEngine
-elif simulaqron_settings.backend == "projectq":
-    from simulaqron.virtNode.projectQSimulator import projectQEngine
-elif simulaqron_settings.backend == "stabilizer":
-    from simulaqron.virtNode.stabilizerSimulator import stabilizerEngine
-else:
-    raise quantumError("Unknown backend {}".format(simulaqron_settings.backend))
-
-
-######
-#
-# backEnd - starts the local virtual node and connects to the other virtual nodes
-# forming the quantum network
-#
-class backEnd(object):
-    def __init__(self, name, virtualFile, network_name="default"):
-        """
-        Initialize. This will read the configuration file and populate the name,hostname,port information with the
-        information found in the configuration file for the given name.
-        """
-
-        # Read the configuration file
-        try:
-            self.config = socketsConfig(virtualFile, network_name=network_name, config_type="vnode")
-            self.myID = self.config.hostDict[name]
-        except KeyError as e:
-            logging.error("LOCAL {}: No such name in the configuration file {}: {}".format(name, virtualFile, e))
-            raise e
-        except Exception as e:
-            logging.error("LOCAL {}: Error reading the configuration file {}: {}".format(name, virtualFile, e))
-            raise e
-
-    def start(self, maxQubits=simulaqron_settings.max_qubits, maxRegisters=simulaqron_settings.max_registers):
-        """
-        Start listening to requests from other nodes.
-
-        Arguments
-        maxQubits	maximum qubits in the default register
-        """
-
-        try:
-            logging.debug("VIRTUAL NODE %s: Starting on port %d", self.myID.name, self.myID.port)
-            node = virtualNode(self.myID, self.config, maxQubits=maxQubits, maxRegisters=maxRegisters)
-            reactor.listenTCP(self.myID.port, pb.PBServerFactory(node))
-
-            logging.debug("VIRTUAL NODE %s: running reactor.", self.myID.name)
-            reactor.run()
-        except CannotListenError:
-            logging.error("LOCAL {}: CQC server address ({}) is already in use.".format(self.myID.name, self.myID.port))
-            return
-        except Exception as e:
-            logging.error(
-                "LOCAL {}: Critical error when starting local virtual node server: {}".format(self.myID.name, e)
-            )
-            return
-
-
-#######
-#
-# virtualNode - this is the virtual quantum node. It keeps track of registers simulated here, qubits
-# virtually available at this node, etc
-#
-
-
-class virtualNode(pb.Root):
-    def __init__(self, ID, config, maxQubits=simulaqron_settings.max_qubits,
-                 maxRegisters=simulaqron_settings.max_registers):
-        """
-        Initialize storing also our own name, hostname and port.
-
-        Arguments:
-        ID		host identifier of this node
-        maxQubits	maximum number of qubits to use in the default engine (default 10)
-        maxRegister	maximum number of registers
-        """
-
-        try:
-
-            # Store our own host identifiers and configuration
-            self.myID = ID
-            self.myID.root = self
-            self.config = config
-
-            # Set max nr of registers and virtual qubits
-            self.maxRegs = maxRegisters
-            self.maxQubits = maxQubits
-
-            # List of connections
-            self.conn = {}
-
-            # Number of registers _created_ at this node
-            # this may not equal the numbers of registers virtually carried
-            self.numRegs = 0
-
-            # Counter for used register numbers
-            self._next_reg_num = 0
-
-            # Set up the dictionary of registers
-            self.registers = {}
-
-            # Initialize the list of qubits at this node
-            self.virtQubits = []
-            self.simQubits = []
-
-            # Set up connections to the neighouring nodes in the network
-            self.connectNet()
-
-            # Global lock: needs to be acquire whenever we want to manipulate more than one
-            # qubit object
-            self._lock = DeferredLock()
-
-            # Time until retry
-            self._delay = 1
-
-            # Maximum number of attempts at getting locks
-            self.maxAttempts = 300
-
-            # List of qubit received to be polled by CQC
-            self.cqcRecv = {}
-
-            # List of halves of epr-pairs received to be polled by CQC
-            self.cqcRecvEpr = {}
-
-        except Exception as e:
-            logging.error("VIRTUAL NODE {}: Critical error when initializing virtNode: {}".format(ID.name, e))
-            raise e
-
-    def reraise_remote_error(self, remote_err):
-        """
-        This is a function re-raises the error thrown remotely
-        :param remote_err: :obj:`twisted.spread.pb.RemoteError`
-        :return: class
-        """
-        # Get name of remote error
-        error_name = remote_err.remoteType.split(b".")[-1].decode()
-
-        # Get class of remote error
-        error_class = eval(error_name)
-
-        raise error_class(str(remote_err))
-
-    def connectNet(self):
-        """
-        Initialize the connections to the other virtual nodes in the network according to the available
-        configuration.
-        """
-
-        try:
-            for key in self.config.hostDict:
-                node = self.config.hostDict[key]
-                if node.name != self.myID.name:
-                    self.connect_to_node(node)
-                else:
-                    self.conn[node.name] = node
-        except Exception as e:
-            logging.error(
-                "VIRTUAL NODE {}: Critical error when connection network of virtual nodes: {}".format(self.myID.name, e)
-            )
-            raise e
-
-    def remote_check_connections(self):
-        """
-        Checks if all connections are up. (Just checks if the number of
-        connections equal the number of nodes in config-file)
-        """
-        return len(self.conn) == len(self.config.hostDict)
-
-    @inlineCallbacks
-    def get_connection(self, name):
-        """
-        Returns the connection specified by 'name'. If no such connection is
-        up yet but name is in the configuration file, wait and try again.
-        """
-        if name in self.conn:
-            return self.conn[name]
-        else:
-            try:
-                logging.debug(
-                    "VIRTUAL NODE {}: Connection to {} not up yet, need to wait...".format(self.myID.name, name)
-                )
-                conn_to_return = yield deferLater(reactor, simulaqron_settings.conn_retry_time, self.get_connection,
-                                                  name)
-                return conn_to_return
-            except Exception as e:
-                raise e
-
-    def connect_to_node(self, node):
-        """
-        Connects to other node. If node not up yet, waits for CONF_WAIT_TIME seconds.
-        """
-        logging.debug("VIRTUAL NODE {}: Trying to connect to node {}.".format(self.myID.name, node.name))
-        node.factory = pb.PBClientFactory()
-        reactor.connectTCP(node.hostname, node.port, node.factory)
-        defer = node.factory.getRootObject()
-        defer.addCallback(self.handle_connection, node)
-        defer.addErrback(self.handle_connection_error, node)
-
-    def handle_connection(self, obj, node):
-        """
-        Callback obtaining twisted root object when connection to the node given by the node details 'node'.
-        """
-        try:
-            logging.debug("VIRTUAL NODE %s: New connection to %s.", self.myID.name, node.name)
-            # Retrieve the root object: virtualNode on the remote
-            node.root = obj
-
-            # Add this node to the local connections
-            self.conn[node.name] = node
-        except Exception as e:
-            logging.error(
-                "VIRTUAL NODE {}: Critical error when handling connection to node {}: {}".format(
-                    self.myID.name, node.name, e
-                )
-            )
-            raise e
-
-    def handle_connection_error(self, reason, node):
-        """
-        Handles errors from trying to connect to other node.
-        If a ConnectionRefusedError is raised another try will be made after CONF_WAIT_TIME seconds.
-        CONF_WAIT_TIME is set in 'settings.py'.
-        Any other error is raised again.
-        """
-
-        try:
-            reason.raiseException()
-        except ConnectionRefusedError:
-            logging.debug("VIRTUAL NODE {}: Could not connect to {}, trying again...".format(self.myID.name, node.name))
-            reactor.callLater(simulaqron_settings.conn_retry_time, self.connect_to_node, node)
-        except Exception as e:
-            logging.error(
-                "VIRTUAL NODE {}: Critical error when connection to local virtual node: {}".format(self.myID.name, e)
-            )
-            reactor.stop()
-
-    def get_virtual_id(self):
-        """
-        This is a crude and horrible cludge to generate unique IDs for virtual qubits.
-        """
-
-        # Loop through the firt k numbers where k is the number of virtual qubits + 1
-        # Note that this is guaranteed to find a an index which is not yet used
-        for j in range(len(self.virtQubits) + 1):
-            used = 0
-            for q in self.virtQubits:
-                if q.num == j:
-                    used = 1
-            if used == 0:
-                return j
-
-    def get_sim_id(self):
-        """
-        Similarly, this is a crude and horrible cludge to generate unique IDs for simulated qubits.
-        """
-
-        # Loop through the firt k numbers where k is the number of virtual qubits + 1
-        # Note that this is guaranteed to find a an index which is not yet used
-        for j in range(len(self.simQubits) + 1):
-            used = 0
-            for q in self.simQubits:
-                if q.simNum == j:
-                    used = 1
-            if used == 0:
-                return j
-
-    def _q_num_to_obj(self, num):
-        """
-        Given the simulation number of a qubit simulated here, return the corresponding object.
-        """
-        for q in self.simQubits:
-            if q.simNum == num:
-                return q
-        return None
-
-    def remote_isLocked(self):
-        return self._lock.locked
-
-    @inlineCallbacks
-    def _get_global_lock(self):
-        logging.debug("VIRTUAL NODE %s: Local GETTING LOCK", self.myID.name)
-        try:
-            yield self._lock.acquire()
-        except Exception as e:
-            raise e
-        logging.debug("VIRTUAL NODE %s: Local GOT LOCK", self.myID.name)
-
-    @inlineCallbacks
-    def remote_get_global_lock(self):
-        logging.debug("VIRTUAL NODE %s: Remote GETTING LOCK", self.myID.name)
-        try:
-            yield self._lock.acquire()
-        except Exception as e:
-            raise e
-        logging.debug("VIRTUAL NODE %s: Remote GOT LOCK", self.myID.name)
-
-    @inlineCallbacks
-    def _release_global_lock(self):
-        logging.debug("VIRTUAL NODE %s: Local RELEASE LOCK", self.myID.name)
-        if self._lock.locked:
-            try:
-                yield self._lock.release()
-            except Exception as e:
-                raise e
-
-    @inlineCallbacks
-    def remote_release_global_lock(self):
-        logging.debug("VIRTUAL NODE %s: Remote RELEASE LOCK", self.myID.name)
-        if self._lock.locked:
-            try:
-                yield self._lock.release()
-            except Exception as e:
-                raise e
-
-    @inlineCallbacks
-    def _lock_reg_qubits(self, qubit):
-        """
-        Acquire the lock on all qubits in the same register as the local sim qubit qubit.
-        """
-        for q in self.simQubits:
-            if q.register == qubit.register:
-                try:
-                    yield q.lock()
-                except Exception as err:
-                    raise err
-
-    @inlineCallbacks
-    def remote_lock_reg_qubits(self, qubitNum):
-        """
-        Acquire the lock on all qubits in the same register as qubitNum.
-        """
-
-        try:
-            yield self._lock_reg_qubits(self._q_num_to_obj(qubitNum))
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def _unlock_reg_qubits(self, qubit):
-        """
-        Release the lock on all qubits in the same register as qubit.
-        """
-        for q in self.simQubits:
-            if q.register == qubit.register:
-                if q._lock.locked:
-                    try:
-                        yield q.unlock()
-                    except Exception as err:
-                        raise err
-
-    @inlineCallbacks
-    def remote_unlock_reg_qubits(self, qubitNum):
-        """
-        Release the lock on all qubits in the same register as qubitNum.
-        """
-
-        try:
-            yield self._unlock_reg_qubits(self._q_num_to_obj(qubitNum))
-        except Exception as err:
-            raise err
-
-    def remote_add_register(self, maxQubits=10):
-        """
-        Adds a new register to the node..
-
-        Arguments:
-        maxQubits	maximum number of qubits to use in the default engine
-        """
-        # TODO We have to methods that do the same thing, should deprecate one of them
-        return self.remote_new_register(maxQubits=maxQubits)
-
-    def get_new_reg_num(self):
-        """
-        Returns an unused register number.
-        """
-        reg_num = self._next_reg_num
-        self._next_reg_num += 1
-        return reg_num
-
-    def remote_new_register(self, maxQubits=10):
-        """
-        Initialize a local register. Right now, this simple creates a register according to the simple engine backend
-        using qubit.
-
-        Arguments:
-        maxQubits	maximum number of qubits to use in the default engine (default 10)
-        """
-
-        try:
-            # Make sure that reg numbers are assigned correctly
-            self._get_global_lock()
-
-            if self.numRegs >= self.maxRegs:
-                logging.error("%s: Maximum number of registers reached.", self.myID.name)
-                raise quantumError("Maximum number of registers reached.")
-
-            self.numRegs = self.numRegs + 1
-            regNum = self.get_new_reg_num()
-            if simulaqron_settings.backend == "qutip":
-                newReg = qutipEngine(self.myID, regNum, maxQubits)
-            elif simulaqron_settings.backend == "projectq":
-                newReg = projectQEngine(self.myID, regNum, maxQubits)
-            elif simulaqron_settings.backend == "stabilizer":
-                newReg = stabilizerEngine(self.myID, regNum, maxQubits)
-            else:
-                raise quantumError("Unknown backend {}".format(simulaqron_settings.backend))
-
-            self.registers[regNum] = newReg
-
-            logging.debug("VIRTUAL NODE %s: Initializing new simulated register.", self.myID.name)
-        except Exception as e:
-            logging.error("VIRTUAL NODE {}: Critical error when getting new register: {}".format(self.myID.name, e))
-            raise e
-        finally:
-            self._release_global_lock()
-        return newReg
-
-    def remote_delete_register(self, reg):
-        """
-        Removes the register from the node.
-        Happens if the last qubit in the register is measured out.
-        """
-
-        # Get register number
-        regnum = reg.num
-
-        # Remove register
-        self.registers.pop(regnum)
-        self.numRegs -= 1
-
-    @inlineCallbacks
-    def remote_new_qubit(self, ignore_max_qubits=False):
-        """
-        Create a new qubit in the default local register.
-
-        :param ignore_max_qubits: bool
-            Used to ignore the check if max virtual qubits is reached. This is used when creating EPR pairs
-            to be able to temporarily create a qubit.
-        """
-        logging.debug("VIRTUAL NODE %s: Request to create new qubit.", self.myID.name)
-
-        try:
-            # Get a lock to assure IDs are assigned correctly and maxQubits is consitently checked
-            try:
-                yield self._get_global_lock()
-            except Exception as err:
-                raise err
-
-            if (len(self.virtQubits) >= self.maxQubits) and (not ignore_max_qubits):
-                logging.error("VIRTUAL NODE %s: Maximum number of virtual qubits reached.", self.myID.name)
-                raise noQubitError("Max virtual qubits reached")
-            else:
-                # Qubit in the simulation backend, initialized to |0>
-                simNum = self.get_sim_id()
-
-                # Create a new register
-                newReg = self.remote_add_register()
-
-                # simQubit = simulatedQubit(self.myID, self.defaultReg, simNum)
-                simQubit = simulatedQubit(self.myID, newReg, simNum)
-                try:
-                    simQubit.make_fresh()
-                except noQubitError as err:
-                    logging.error("VIRTUAL NODE %s: Max qubits for register reached.", self.myID.name)
-                    raise err
-
-                self.simQubits.append(simQubit)
-
-                # Virtual qubit
-                newNum = self.get_virtual_id()
-                newQubit = virtualQubit(self.myID, self.myID, simQubit, newNum)
-                self.virtQubits.append(newQubit)
-        finally:
-            self._release_global_lock()
-
-        return newQubit
-
-    @inlineCallbacks
-    def remote_new_qubit_inreg(self, reg):
-        """
-        Create a new qubit in the specified register reg.
-        """
-
-        # Only allow if the register is local
-        if reg.simNode != self.myID:
-            raise quantumError("Can only create qubits registers simulated locally by this node.")
-
-        try:
-            # Get a lock to assure IDs are assigned correctly and maxQubits is consitently checked
-            try:
-                yield self._get_global_lock()
-            except Exception as err:
-                raise err
-
-            if len(self.virtQubits) >= self.maxQubits:
-                logging.error("VIRTUAL NODE %s: Maximum number of virtual qubits reached.", self.myID.name)
-                raise noQubitError("Max virtual qubits reached")
-            else:
-                # Qubit in the local simulation backend, initialized to |0>
-                simNum = self.get_sim_id()
-                simQubit = simulatedQubit(self.myID, reg, simNum)
-                try:
-                    simQubit.make_fresh()
-                except noQubitError as err:
-                    logging.error("VIRTUAL NODE %s: Max qubits for register reached.", self.myID.name)
-                    raise err
-                self.simQubits.append(simQubit)
-
-                # Virtual qubit
-                newNum = self.get_virtual_id()
-                newQubit = virtualQubit(self.myID, self.myID, simQubit, newNum)
-                self.virtQubits.append(newQubit)
-        finally:
-            self._release_global_lock()
-
-        return newQubit
-
-    @inlineCallbacks
-    def remote_cqc_send_qubit(self, num, targetName, app_id, remote_app_id):
-        """
-        Send interface for CQC to add the qubit to the remote nodes received list for an application.
-
-        Arguments:
-        num		number of virtual qubit to send
-        targetName	name of the node to send to
-        app_id		application asking to have this qubit delivered
-        remote_app_id	application ID to deliver the qubit to
-        """
-        logging.debug("VIRTUAL NODE %s: request to send qubit %d to %s", self.myID.name, num, targetName)
-
-        virtQubit = self.remote_get_virtual_ref(num)
-
-        try:
-            newVirtNum = yield self.remote_send_qubit(virtQubit, targetName)
-        except Exception as err:
-            raise err
-
-        # Lookup host ID of node
-        try:
-            if not (targetName in self.config.hostDict):
-                raise virtNetError(
-                    "Trying to get conncetion to virtual node {}, but this is not in configuration file".format(
-                        targetName
-                    )
-                )
-            remoteNode = yield self.get_connection(targetName)
-        except Exception as err:
-            raise err
-
-        # Ask to add to list
-        try:
-            yield remoteNode.root.callRemote("cqc_add_recv_list", self.myID.name, app_id, remote_app_id, newVirtNum)
-        except RemoteError as remote_err:
-            self.reraise_remote_error(remote_err)
-        except Exception as err:
-            raise err
-
-    def remote_cqc_add_recv_list(self, fromName, from_app_id, to_app_id, new_virt_num):
-        """
-        Add an item to the received list for use in CQC.
-        """
-
-        if not (to_app_id in self.cqcRecv):
-            self.cqcRecv[to_app_id] = deque([])
-
-        self.cqcRecv[to_app_id].append(QubitCQC(fromName, self.myID.name, from_app_id, to_app_id, new_virt_num))
-        logging.debug("VIRTUAL NODE %s: Added a qubit for app id %d to recv list", self.myID.name, to_app_id)
-
-    def remote_cqc_get_recv(self, to_app_id):
-        """
-        Retrieve the next qubit with the given app ID form the received list.
-        """
-
-        logging.debug(
-            "VIRTUAL NODE %s: Trying to retrieve qubit for app id %d from recv list", self.myID.name, to_app_id
-        )
-        # Get the list corresponding to the specified application ID
-        if not (to_app_id in self.cqcRecv):
-            return None
-
-        qQueue = self.cqcRecv[to_app_id]
-        if not qQueue:
-            return None
-
-        # Retrieve the first element on that list (first in, first out)
-        qc = qQueue.popleft()
-        if not qc:
-            return None
-
-        logging.debug("VIRTUAL NODE %s: Returning qubit for app id %d from recv list", self.myID.name, to_app_id)
-        return self.remote_get_virtual_ref(qc.virt_num)
-
-    @inlineCallbacks
-    def remote_cqc_send_epr_half(self, num, targetName, app_id, remote_app_id, rawEntInfo):
-        """
-        Send interface for CQC to add the qubit to the remote nodes received list for an application.
-
-        Arguments:
-        num		number of virtual qubit to send
-        targetName	name of the node to send to
-        app_id		application asking to have this qubit delivered
-        remote_app_id	application ID to deliver the qubit to
-        entInfo		entanglement information
-        """
-        qubit = self.remote_get_virtual_ref(num)
-
-        try:
-            newVirtNum = yield self.remote_send_qubit(qubit, targetName)
-        except Exception as err:
-            raise err
-
-        # Lookup host ID of node
-        try:
-            if not (targetName in self.config.hostDict):
-                raise virtNetError(
-                    "Trying to get conncetion to virtual node {}, but this is not in configuration file".format(
-                        targetName
-                    )
-                )
-            remoteNode = yield self.get_connection(targetName)
-        except Exception as e:
-            raise e
-
-        # Ask to add to list
-        try:
-            yield remoteNode.root.callRemote(
-                "cqc_add_epr_list", self.myID.name, app_id, remote_app_id, newVirtNum, rawEntInfo
-            )
-        except RemoteError as remote_err:
-            self.reraise_remote_error(remote_err)
-        except Exception as err:
-            raise err
-
-    def remote_cqc_add_epr_list(self, fromName, from_app_id, to_app_id, new_virt_num, rawEntInfo):
-        """
-        Add an item to the epr list for use in CQC.
-        """
-
-        if not (to_app_id in self.cqcRecvEpr):
-            self.cqcRecvEpr[to_app_id] = deque([])
-
-        self.cqcRecvEpr[to_app_id].append(
-            QubitCQC(fromName, self.myID.name, from_app_id, to_app_id, new_virt_num, rawEntInfo=rawEntInfo)
-        )
-        logging.debug("VIRTUAL NODE %s: Added a qubit for app id %d to epr list", self.myID.name, to_app_id)
-
-    def remote_cqc_get_epr_recv(self, to_app_id):
-        """
-        Retrieve the next qubit (half of an EPR-pair) with the given app ID from the received list.
-        """
-
-        try:
-            logging.debug(
-                "VIRTUAL NODE %s: Trying to retrieve qubit for app id %d from epr list", self.myID.name, to_app_id
-            )
-            # Get the list corresponding to the specified application ID
-            if not (to_app_id in self.cqcRecvEpr):
-                return None
-
-            qQueue = self.cqcRecvEpr[to_app_id]
-            if not qQueue:
-                return None
-
-            # Retrieve the first element on that list (first in, first out)
-            qc = qQueue.popleft()
-            if not qc:
-                return None
-
-            logging.debug("VIRTUAL NODE %s: Returning qubit for app id %d from epr list", self.myID.name, to_app_id)
-            return self.remote_get_virtual_ref(qc.virt_num), qc.rawEntInfo
-
-        except Exception as e:
-            raise e
-
-    @inlineCallbacks
-    def remote_send_qubit(self, qubit, targetName):
-        """
-        Sends the qubit to the specified target node. This creates a new virtual qubit object at the remote node
-        with the right qubit and backend details.
-
-        Arguments
-        qubit		virtual qubit to be sent
-        targetName	target ndoe to place qubit at (host object)
-        """
-        logging.debug("VIRTUAL NODE %s: Request to send qubit sim Num %d to %s.", self.myID.name, qubit.num, targetName)
-        if qubit.active != 1:
-            logging.debug("VIRTUAL NODE %s: Attempt to manipulate qubit no longer at this node.", self.myID.name)
-            return
-
-        # Lookup host id of node
-        try:
-            if not (targetName in self.config.hostDict):
-                raise virtNetError(
-                    "Trying to get conncetion to virtual node {}, but this is not in configuration file".format(
-                        targetName
-                    )
-                )
-            remoteNode = yield self.get_connection(targetName)
-        except Exception as e:
-            raise e
-
-        try:
-            # Get lock to prevent access to qubits between sending and manipulating local list
-            self._get_global_lock()
-
-            # Check whether we are just the virtual, or also the simulating node
-            if qubit.virtNode == qubit.simNode:
-                logging.debug("VIRTUAL NODE %s: Sending qubit simulated locally", self.myID.name)
-                # We are both the virtual as well as the simulating node
-                # Pass a reference to our locally simulated qubit object to the remote node
-                try:
-                    newNum = yield remoteNode.root.callRemote("add_qubit", self.myID.name, qubit.simQubit)
-                except RemoteError as remote_err:
-                    self.reraise_remote_error(remote_err)
-                except Exception as err:
-                    raise err
-            else:
-                logging.debug(
-                    "VIRTUAL NODE %s: Sending qubit simulated remotely at %s", self.myID.name, qubit.simNode.name
-                )
-                # We are only the virtual node, not the simulating one. In this case, we need to ask
-                # the actual simulating node to do the transfer for us. Due to the pecularities of Twisted PB
-                # we need to do this by the simulated qubit number
-                try:
-                    simQubitNum = yield qubit.simQubit.callRemote("get_sim_number")
-                except RemoteError as remote_err:
-                    self.reraise_remote_error(remote_err)
-                except Exception as err:
-                    raise err
-                try:
-                    newNum = yield qubit.simNode.root.callRemote("transfer_qubit", simQubitNum, targetName)
-                except RemoteError as remote_err:
-                    self.reraise_remote_error(remote_err)
-                except Exception as err:
-                    raise err
-
-            # We gave it away so mark as inactive
-            qubit.active = 0
-
-            # Remove the qubit from the local virtual list. Note it remains in the simulated
-            # list, since we continue to simulate this qubit if we did so before.
-            self.virtQubits.remove(qubit)
-        except Exception as err:
-            raise err
-        finally:
-            self._release_global_lock()
-
-        return newNum
-
-    @inlineCallbacks
-    def remote_transfer_qubit(self, simQubitNum, targetName):
-        """
-        Transfer the qubit to the destination node if we are the simulating node. The reason why we cannot
-        do this directly is that Twisted PB does not allow objects to be passed between connecting nodes.
-        Only between the creator of the object and its immediate connections.
-
-        Arguments
-        simQubitNum	simulated qubit number to be sent
-        targetName	target node to place qubit at (host object)
-        """
-        logging.debug("VIRTUAL NODE %s: Request to transfer qubit to %s.", self.myID.name, targetName)
-
-        # Convert the number into the right local object
-        simQubit = self._q_num_to_obj(simQubitNum)
-
-        # Lookup host id of node
-        try:
-            if not (targetName in self.config.hostDict):
-                raise virtNetError(
-                    "Trying to get conncetion to virtual node {}, but this is not in configuration file".format(
-                        targetName
-                    )
-                )
-            remoteNode = yield self.get_connection(targetName)
-        except Exception as e:
-            raise e
-
-        # Check if we are both the destination node and simulating node
-        if self.myID.name == targetName:
-            try:
-                newNum = yield remoteNode.root.remote_add_qubit(self.myID.name, simQubit)
-            except Exception as err:
-                raise err
-        else:
-            try:
-                newNum = yield remoteNode.root.callRemote("add_qubit", self.myID.name, simQubit)
-            except RemoteError as remote_err:
-                self.reraise_remote_error(remote_err)
-            except Exception as err:
-                raise err
-
-        return newNum
-
-    @inlineCallbacks
-    def remote_add_qubit(self, name, simQubit):
-        """
-        Add a qubit to the local virtual node.
-
-        Arguments
-        name		name of the node simulating this qubit
-        simQubit 	simulated qubit reference in the backend we're adding
-        """
-
-        logging.debug("VIRTUAL NODE %s: Request to add qubit from %s.", self.myID.name, name)
-
-        # Get the details of the remote node
-        try:
-            if not (name in self.config.hostDict):
-                raise virtNetError(
-                    "Trying to get conncetion to virtual node {}, but this is not in configuration file".format(name)
-                )
-            nb = yield self.get_connection(name)
-        except Exception as e:
-            raise e
-
-        try:
-            # Get a lock to make sure IDs are assigned correctly
-            self._get_global_lock()
-
-            if len(self.virtQubits) >= self.maxQubits:
-                raise noQubitError("Max virtual qubits reached")
-
-            # Generate a new virtual qubit object for the qubit now at this node
-            newNum = self.get_virtual_id()
-            newQubit = virtualQubit(self.myID, nb, simQubit, newNum)
-
-            # Add to local list
-            self.virtQubits.append(newQubit)
-        except Exception as err:
-            raise err
-        finally:
-            self._release_global_lock()
-
-        return newNum
-
-    def remote_get_virtual_ref(self, num):
-        """
-        Return a virual qubit object for the given number.
-
-        Arguments
-        num		number of the virtual qubit
-        """
-
-        for q in self.virtQubits:
-            if q.num == num:
-                return q
-
-        return None
-
-    def remote_remove_sim_qubit_num(self, delNum):
-        """
-        Removes the simulated qubit delQubit from the node and also from the underlying engine. Relies on this qubit
-        having been locked.
-
-        Arguments
-        delNum		simID of the simulated qubit to delete
-        """
-
-        self._remove_sim_qubit(self._q_num_to_obj(delNum))
-
-    @inlineCallbacks
-    def _remove_sim_qubit(self, delQubit):
-        """
-        Removes the simulated qubit object.
-
-        Arguments
-        delQubit	simulated qubit object to delete
-        """
-
-        # Caution: Only qubits simulated at this node can be removed
-        if delQubit not in self.simQubits:
-            logging.error("VIRTUAL NODE %s: Attempt to delete qubit not simulated at this node.", self.myID.name)
-            raise quantumError("%s: Cannot delete qubits we don't simulate.")
-
-        #
-        delNum = delQubit.num
-        delRegister = delQubit.register
-
-        try:
-            # We need to manipulate multiple qubits, get global lock
-            yield self._get_global_lock()
-
-            # Lock all relevant qubits first
-            for q in self.simQubits:
-                if q.register == delRegister:
-                    yield q.lock()
-
-            # First we remove the physical qubit from the register
-            delRegister.remove_qubit(delNum)
-
-            # Check if this was the last qubit
-            if delRegister.activeQubits == 0:
-                self.remote_delete_register(delRegister)
-            else:
-                # When removing a qubit, we need to update the positions of the qubits in
-                # the underlying physical register
-                # in all relevant qubit objects.
-                for q in self.simQubits:
-                    # If they are in the same engine, and update is required
-                    if q.register == delRegister:
-                        if q.num > delNum:
-                            q.num = q.num - 1
-
-            # Remove the qubit form the list of simulated qubits
-            self.simQubits.remove(delQubit)
-
-        except Exception as e:
-            logging.error("VIRTUAL NODE {}: Cannot remove sim qubit - {}".format(self.myID.name, e))
-        finally:
-            # Release all relevant qubits again
-            for q in self.simQubits:
-                if q.register == delRegister:
-                    q.unlock()
-
-            # Release the global multi qubit lock
-            self._release_global_lock()
-
-    def remote_merge_regs(self, num1, num2):
-        """
-        Merges the two local quantum registers. Note that these register may simulate virtual qubits across different
-        network nodes. This will ignore maxQubits and simply create one large register allowing twice maxQubits qubits.
-
-        Arguments
-        num1 		number of the first qubit
-        num2		number of the second qubit
-        """
-
-        # Lookup the qubit objects corresponding to these numbers
-        for q in self.simQubits:
-            if q.simNum == num1:
-                q1 = q
-            elif q.simNum == num2:
-                q2 = q
-
-        self.local_merge_regs(q1, q2)
-
-    def local_merge_regs(self, qubit1, qubit2):
-        """
-        Merges the two local quantum registers. Note that these register may simulate virtual qubits across different
-        network nodes. This will ignore maxQubits and simply create one large register allowing twice maxQubits qubits.
-
-        Arguments
-        qubit1		qubit1 in reg1, called from remote having access to only qubits
-        qubit2		qubit2 in reg2
-        """
-        logging.debug(
-            "VIRTUAL NODE %s: Request to merge local register for qubits simNum %d and simNum %d.",
-            self.myID.name,
-            qubit1.simNum,
-            qubit2.simNum,
-        )
-
-        # This should only be called if locks are acquired
-        assert qubit1._lock.locked
-        assert qubit2._lock.locked
-        assert self._lock.locked
-
-        logging.debug("VIRTUAL NODE %s: Request to merge LOCKS PRESENT", self.myID.name)
-
-        reg1 = qubit1.register
-        reg2 = qubit2.register
-
-        # Check if there's anything to do at all
-        if reg1 == reg2:
-            logging.debug("VIRTUAL NODE %s: Request to merge local register: not required", self.myID.name)
-            return
-
-        logging.debug("VIRTUAL NODE %s: Request to merge local register: need merge", self.myID.name)
-
-        # Allow reg 1 to absorb reg 2
-        reg1.maxQubits = reg1.maxQubits + reg2.activeQubits
-
-        # For relabelling qubit numbers get the offset
-        offset = reg1.activeQubits
-
-        # Add reg2 to reg1
-        reg1.absorb(reg2)
-
-        # Update the simulated qubit numbering and register
-        for q in self.simQubits:
-            if q.register == reg2:
-                logging.debug("VIRTUAL NODE %s: Updating register %d to %d.", self.myID.name, q.num, q.num + offset)
-                q.register = reg1
-                q.num = q.num + offset
-
-        # reg2.reset()
-        self.remote_delete_register(reg2)
-
-    @inlineCallbacks
-    def remote_merge_from(self, simNodeName, simQubitNum, localReg):
-        """
-        Bring a remote register to this node.
-
-        Arguments
-        simNodeName	name of the node who simulates right now
-        simQubitNum	simulation number of qubit whose register we will merge
-        localReg	local register to merge with
-        """
-
-        logging.debug("VIRTUAL NODE %s: Merging from %s", self.myID.name, simNodeName)
-
-        # This should only be called if lock is acquired
-        assert self._lock.locked
-
-        logging.debug("VIRTUAL NODE %s: Merging from %s LOCKS PRESENT", self.myID.name, simNodeName)
-
-        # Lookup the local connection for this simulating node
-        try:
-            if not (simNodeName in self.config.hostDict):
-                raise virtNetError(
-                    "Trying to get conncetion to virtual node {}, but this is not in configuration file".format(
-                        simNodeName
-                    )
-                )
-            simNode = yield self.get_connection(simNodeName)
-        except Exception as e:
-            raise e
-
-        # Fetch the details of the remote register and qubit, and remove sim qubits at node
-        try:
-            (R, I, activeQ, oldRegNum, oldQubitNum) = yield simNode.root.callRemote("get_register_del", simQubitNum)
-        except RemoteError as remote_err:
-            self.reraise_remote_error(remote_err)
-        except Exception as err:
-            raise err
-
-        # Get numbering offset from previous register: append at end
-        offset = localReg.activeQubits
-
-        # Allow localReg to absorb the remote register
-        localReg.maxQubits = localReg.maxQubits + activeQ
-        localReg.absorb_parts(R, I, activeQ)
-
-        # Collect mappings between numbers and objects for updating the virtual qubits
-        newD = {}
-
-        # Make new qubit objects
-        for k in range(activeQ):
-            simNum = self.get_sim_id()
-            newQubit = simulatedQubit(self.myID, localReg, simNum, offset + k)
-            self.simQubits.append(newQubit)
-            newD[k] = newQubit
-
-        # Issue an update call to all nodes to update their virtual qubits if necessary
-        # for name in self.conn:
-        for name in self.config.hostDict:
-            if name != self.myID.name:
-                try:
-                    nb = yield self.get_connection(name)
-                except Exception as err:
-                    raise err
-                try:
-                    yield nb.root.callRemote("update_virtual_merge", self.myID.name, simNodeName, oldRegNum, newD)
-                except RemoteError as remote_err:
-                    self.reraise_remote_error(remote_err)
-                except Exception as err:
-                    raise err
-
-        # Locally, we might also already have virtual qubits which were in the remote simulated
-        # register. Update them as well
-        logging.debug("VIRTUAL NODE %s: Updating local virtual qubits.", self.myID.name)
-        try:
-            yield self.remote_update_virtual_merge(self.myID.name, simNodeName, oldRegNum, newD)
-        except Exception as err:
-            raise err
-
-        # Return the qubit object corresponding to the new physical qubit
-        return newD[oldQubitNum]
-
-    @inlineCallbacks
-    def remote_update_virtual_merge(self, newSimNodeName, oldSimNodeName, oldRegNum, newD):
-        """
-        Update the virtual qubits to the new simulating node, if applicable. This is extremely
-        inefficient due to not keeping register information in virtualQubit.
-
-        Arguments
-        newSimNodeName	new node simulating this qubit
-        oldSimNodeName	old node simulating the qubit
-        oldReg		old register
-        newD		dictionary mapping qubit numbers to qubit objects at the new simulating node
-        """
-
-        logging.debug("VIRTUAL NODE %s: Request to update local virtual qubits.", self.myID.name)
-
-        # If this is a third node (not involved in the two qubit gate, but carrying virtual qubits
-        # which were in the simulated register), then they will now be updated. We remark that this function
-        # can only be called from the _simulating node_ now handing over simulation to someone else. Both the simulating
-        # node and the new simulating node are globally locked so there should be no conflicts here in updating:
-        # a third node that may wish to do a 2 qubit gate between the qubits to be updated needs to wait.
-
-        # Lookup the local connections for the given node names
-        try:
-            if not (newSimNodeName in self.config.hostDict):
-                raise virtNetError(
-                    "Trying to get conncetion to virtual node {}, but this is not in configuration file".format(
-                        newSimNodeName
-                    )
-                )
-            if not (oldSimNodeName in self.config.hostDict):
-                raise virtNetError(
-                    "Trying to get conncetion to virtual node {}, but this is not in configuration file".format(
-                        oldSimNodeName
-                    )
-                )
-            newSimNode = yield self.get_connection(newSimNodeName)
-            oldSimNode = yield self.get_connection(oldSimNodeName)
-        except Exception as e:
-            raise e
-
-        for q in self.virtQubits:
-            if q.virtNode == q.simNode and q.simNode == oldSimNode:
-                logging.debug("VIRTUAL NODE %s: Simulating node update.", self.myID.name)
-                # We previously simulated this qubit ourselves
-                givenReg = q.simQubit.register.num
-                givenNum = q.simQubit.num
-            elif q.simNode == oldSimNode:
-                logging.debug("VIRTUAL NODE %s: Previously remote simulator node update.", self.myID.name)
-                # We had the virtual qubit but it was simulated elsewhere
-                try:
-                    (givenNum, givenReg) = yield q.simQubit.callRemote("get_numbers")
-                except RemoteError as remote_err:
-                    self.reraise_remote_error(remote_err)
-                except Exception as err:
-                    raise err
-
-            # Check if this qubit needs updating
-            if q.simNode == oldSimNode and givenReg == oldRegNum:
-                logging.debug(
-                    "VIRTUAL NODE %s: Updating virtual qubit %d, previously %s now %s",
-                    self.myID.name,
-                    q.num,
-                    oldSimNode.name,
-                    newSimNode.name,
-                )
-                q.simNode = newSimNode
-                q.simQubit = newD[givenNum]
-
-    @inlineCallbacks
-    def remote_get_register_RI(self, qubit):
-        """
-        Return the real and imaginary part of the (possibly remote) simulated register which
-        contains this virtual qubit.
-        """
-        if isinstance(qubit, virtualQubit):
-            realM, imagM = yield qubit.remote_get_register_RI()
-        else:
-            realM, imagM = yield qubit.callRemote("get_register_RI")
-        return realM, imagM
-
-    def remote_get_register(self, qubit):
-        """
-        Return the value of of a locally simulated register which contains this virtual qubit.
-        """
-
-        (realM, imagM) = qubit.simQubit.register.get_register_RI()
-        activeQ = qubit.simQubit.register.activeQubits
-        oldRegNum = qubit.simQubit.register.num
-        oldQubitNum = qubit.simQubit.num
-
-        return (realM, imagM, activeQ, oldRegNum, oldQubitNum)
-
-    def remote_get_register_del(self, qubitNum):
-        """
-        Return the value of of a locally simulated register, and remove the simulated qubits from this node.
-
-        Caution: virtual qubits not updated.
-        """
-
-        assert self._lock.locked
-
-        # Locate the qubit object for this ID
-        gotQ = None
-        for q in self.simQubits:
-            if q.simNum == qubitNum:
-                gotQ = q
-
-        # If nothing is found, return
-        if gotQ is None:
-            logging.debug("VIRTUAL NODE %s: No simulated qubit with ID %d.", qubitNum)
-            return ([], [], 0, 0, 0)
-
-        (realM, imagM) = gotQ.register.get_register_RI()
-        activeQ = gotQ.register.activeQubits
-        oldRegNum = gotQ.register.num
-        oldQubitNum = gotQ.num
-        delRegister = gotQ.register
-
-        # Remove all simulated qubits and the register
-        # Need to iterate of simQubits in reverse, otherwise wrong elements are removed
-        for q in reversed(self.simQubits):
-            if q.register.num == oldRegNum:
-                self.simQubits.remove(q)
-                # gotQ.register.activeQubits -= 1
-
-        self.remote_delete_register(delRegister)
-
-        return (realM, imagM, activeQ, oldRegNum, oldQubitNum)
-
-    @inlineCallbacks
-    def remote_get_multiple_qubits(self, qList):
-        """
-        Return the state of multiple qubits virtually located at this node. This will fail if the qubits
-        are not in the same register or thus also simulating node.
-
-        Arguments
-        qList		list of virtual qubits of which to retrieve the state
-        """
-
-        localSim = False
-        remoteSim = False
-
-        # Check whether we are the simulating node.
-        for q in qList:
-            if q.simNode == q.virtNode:
-                localSim = True
-            elif q.simNode != q.virtNode:
-                remoteSim = True
-
-        # Check whether two nodes are the simulator, for now we simply fail in this case
-        if localSim and remoteSim:
-            logging.error(
-                "VIRTUAL NODE %s: Getting multiple qubits from multiple simulators is currently not supported.",
-                self.myID.name,
-            )
-            return ([0], [0])
-
-        if localSim:
-            # Qubits are local, simply retrieve from the simulation
-            nums = []
-            for q in qList:
-                nums.append(q.simQubit.simNum)
-            logging.debug("VIRTUAL NODE %s: Looking for simulated qubits. %s", self.myID.name, nums)
-            (R, I) = self.remote_get_state(nums)
-        else:
-            # Qubits are located elsewhere.
-            nums = []
-            for q in qList:
-                try:
-                    (num, name) = yield q.simQubit.callRemote("get_details")
-                except RemoteError as remote_err:
-                    self.reraise_remote_error(remote_err)
-                except Exception as err:
-                    raise err
-                nums.append(num)
-            try:
-                (R, I) = yield qList[0].simNode.root.callRemote("get_state", nums)
-            except RemoteError as remote_err:
-                self.reraise_remote_error(remote_err)
-            except Exception as err:
-                raise err
-
-        return (R, I)
-
-    def remote_get_state(self, simNumList):
-        """
-        Return the state of multiple qubits corresponding to the IDs in simNumList.
-        """
-
-        # Convert simulation numbers to register and real number in register
-        traceList = []
-        foundOne = False
-        prev = None
-        for n in simNumList:
-            for q in self.simQubits:
-                if q.simNum == n:
-                    if foundOne is True and prev.register != q.register:
-                        logging.error(
-                            "VIRTUAL NODE %s: Getting multiple qubits from different registers not supported.",
-                            self.myID.name,
-                        )
-                        return ([], [])
-                    prev = q
-                    foundOne = True
-                    traceList.append(q.num)
-        if not foundOne:
-            logging.error("VIRTUAL NODE %s: No such qubits found.", self.myID.name)
-            return
-
-        traceList.sort()
-        (realM, imagM) = prev.register.get_qubits_RI(traceList)
-
-        return (realM, imagM)
-
-
-#######
-#
-# virtualQubit - a qubit that is virtually carried at this node. It may be simulated elsewhere
-# but in the simulation it is located at this particular virtualNode.
-#
-# This is given out as a reference object to users who ask for a "local" qubit
-#
-#
-
-
-class virtualQubit(pb.Referenceable):
-    def __init__(self, virtNode, simNode, simQubit, num):
-        """
-        Creates a virtual qubit object simulated in the specified simulation register backend
-
-        Arguments
-        virtNode	node where this qubit is virtually located
-        simNode		node where this qubit is simulated
-        simQubit	reference to the underlying qubit object (may be remote)
-        num		number ID among the virtual qubits
-        """
-
-        # Node where this qubit is virtually located
-        self.virtNode = virtNode
-
-        # Node where this qubit is being simulated
-        self.simNode = simNode
-
-        # Underlying qubit object for simulation
-        self.simQubit = simQubit
-
-        # Qubit active at this node. The client may retain a reference to this object,
-        # which will cause python to keep it, while it has actually be transferred to
-        # another node. We do not allow operations on a qubit that is now virtually elsewhere.
-        self.active = 1
-
-        # Our number at this virtual node. Note that this has nothing to do
-        # with the number of the qubits in the register
-        self.num = num
-
-    @inlineCallbacks
-    def _single_gate(self, name, *args):
-        """
-        Apply the single gate function to the underlying qubit. This is an internal method used by all the other
-        single qubit calls, which will perform the correct local or remote method calls as applicable after
-        performing the necessary locking.
-
-        Arguments
-        name		name of the method corresponding to the name. For example: name = apply_X
-        param		parameters for gates such as rotations (axis,angle)
-        """
-
-        if self.active != 1:
-            logging.error("VIRTUAL NODE %s: Attempt to manipulate qubits no longer at this node.", self.virtNode.name)
-            return False
-
-        # Construct the name of the method to call if the qubit is locally simulated
-        # in which case we (ironically) need to append the prefix remote which is automatically
-        # added if the method is called from remote by Twisted
-        localName = "".join(["remote_", name])
-
-        # Check whether the qubit is local or remote. Due to remote register merges, this may change
-        # while we try and get a lock. For this reason, we have to wait until we have a lock on an _active_
-        # qubit before proceeding. If it is no longer active when we get the lock, then it has been
-        # moved elsewhere in the meantime and we need to wait for the remote message to update the virtual
-        # qubit object in the background.
-        waiting = True
-        outcome = False
-        while waiting:
-            if self.virtNode == self.simNode:
-                if not self.simQubit.isLocked():
-                    try:
-                        yield self.simQubit.lock()
-                        if self.simQubit.active:
-                            getattr(self.simQubit, localName)(*args)
-                            waiting = False
-                            outcome = True
-                    except Exception as e:
-                        logging.error("VIRTUAL NODE {}: Cannot apply {} - {}".format(self.virtNode.name, e, name))
-                        waiting = False
-                    finally:
-                        self.simQubit.unlock()
-            else:
-                try:
-                    isLocked = yield self.simQubit.callRemote("isLocked")
-                except RemoteError as remote_err:
-                    self.virtNode.root.reraise_remote_error(remote_err)
-                except Exception as err:
-                    raise err
-                if not isLocked:
-                    try:
-                        yield self.simQubit.callRemote("lock")
-                        active = yield self.simQubit.callRemote("isActive")
-                        if active:
-                            logging.debug(
-                                "VIRTUAL NODE %s: Calling %s remotely to apply %s.",
-                                self.virtNode.name,
-                                self.simNode.name,
-                                name,
-                            )
-                            yield self.simQubit.callRemote(name, *args)
-                            waiting = False
-                            outcome = True
-                    except Exception as e:
-                        logging.error("VIRTUAL NODE %s: Cannot apply %s - %s", e, name)
-                        waiting = False
-                    finally:
-                        yield self.simQubit.callRemote("unlock")
-
-            # If we did not get a lock on an active qubit, wait for update and try again
-            if waiting:
-                try:
-                    yield deferLater(reactor, self.virtNode.root._delay, lambda: None)
-                except Exception as err:
-                    raise err
-
-        return outcome
-
-    @inlineCallbacks
-    def remote_apply_X(self):
-        """
-        Apply X gate to itself by passing it onto the underlying register.
-        """
-        try:
-            success = yield self._single_gate("apply_X")
-            return success
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def remote_apply_Y(self):
-        """
-        Apply Y gate.
-        """
-        try:
-            success = yield self._single_gate("apply_Y")
-            return success
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def remote_apply_Z(self):
-        """
-        Apply Z gate.
-        """
-        try:
-            success = yield self._single_gate("apply_Z")
-            return success
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def remote_apply_H(self):
-        """
-        Apply H gate.
-        """
-        try:
-            success = yield self._single_gate("apply_H")
-            return success
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def remote_apply_K(self):
-        """
-        Apply K gate - taking computational basis to Y eigenbasis.
-        """
-        try:
-            success = yield self._single_gate("apply_K")
-            return success
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def remote_apply_T(self):
-        """
-        Apply T gate.
-        """
-        try:
-            success = yield self._single_gate("apply_T")
-            return success
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def remote_apply_rotation(self, n, a):
-        """
-        Apply rotation around axis n with angle a.
-        Arguments:
-        n	A tuple of three numbers specifying the rotation axis, e.g n=(1,0,0)
-        a	The rotation angle in radians.
-        """
-        try:
-            success = yield self._single_gate("apply_rotation", n, a)
-            return success
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def remote_measure(self, inplace=False):
-        """
-        Measure the qubit in the standard basis. If inplace=False, this does delete the qubit from the simulation.
-
-        Returns the measurement outcome.
-        """
-
-        if self.active != 1:
-            logging.error("VIRTUAL NODE %s: Attempt to manipulate qubits no longer at this node.", self.virtNode.name)
-            return
-
-        # Check whether the qubit is local or remote. Due to remote register merges, this may change
-        # while we try and get a lock. For this reason, we have to wait until we have a lock on an _active_
-        # qubit before proceeding.
-        waiting = True
-        outcome = None
-        while waiting:
-            if self.virtNode == self.simNode:
-                if not self.simQubit.isLocked():
-                    try:
-                        yield self.simQubit.lock()
-                        if self.simQubit.active:
-                            logging.debug("VIRTUAL NODE %s: Measuring local qubit", self.virtNode.name)
-                            outcome = self.simQubit.remote_measure_inplace()
-                            if not inplace:
-                                self.virtNode.root._remove_sim_qubit(self.simQubit)
-
-                                # Delete from virtual qubits
-                                self.virtNode.root.virtQubits.remove(self)
-                            waiting = False
-                    except Exception as e:
-                        logging.error(
-                            "VIRTUAL NODE {}: Cannot remove local qubit. Error: {}".format(self.virtNode.name, e)
-                        )
-                        waiting = False
-                    finally:
-                        self.simQubit.unlock()
-            else:
-                try:
-                    isLocked = yield self.simQubit.callRemote("isLocked")
-                except RemoteError as remote_err:
-                    self.virtNode.root.reraise_remote_error(remote_err)
-                except Exception as err:
-                    raise err
-                if not isLocked:
-                    try:
-                        yield self.simQubit.callRemote("lock")
-                        active = yield self.simQubit.callRemote("isActive")
-                        if active:
-                            logging.debug(
-                                "VIRTUAL NODE %s: Measuring remote qubit at %s.", self.virtNode.name, self.simNode.name
-                            )
-                            outcome = yield self.simQubit.callRemote("measure_inplace")
-                            if not inplace:
-                                num = yield self.simQubit.callRemote("get_sim_number")
-                                yield self.simNode.root.callRemote("remove_sim_qubit_num", num)
-
-                                # Delete from virtual qubits
-                                self.virtNode.root.virtQubits.remove(self)
-                            waiting = False
-                    except Exception as e:
-                        logging.error(
-                            "VIRTUAL NODE {}: Cannot remove remote qubit. Error: {}".format(self.virtNode.name, e)
-                        )
-                        waiting = False
-                    finally:
-                        yield self.simQubit.callRemote("unlock")
-
-            # If we did not get a lock on an active qubit, wait for update and try again
-            if waiting:
-                try:
-                    yield deferLater(reactor, self.virtNode.root._delay, lambda: None)
-                except Exception as err:
-                    raise err
-
-        return outcome
-
-    def _lock_nodes(self, target):
-        """
-        Wrapper to acquire the global register lock on both nodes that involve the qubits, and local node.
-
-        Arguments
-        target		virtual qubit of the target qubit
-
-        """
-        lockedLocal = False
-        lockedRemoteTarget = False
-
-        # Lock qubits nodes
-        if self.simNode == self.virtNode:
-            # first qubit is locally simulated
-            def1 = self.simNode.root._get_global_lock()
-            lockedLocal = True
-        else:
-            # first qubit is remote
-            def1 = self.simNode.root.callRemote("get_global_lock")
-
-        # If target is a different node
-        if target.simNode != self.simNode:
-            if target.simNode == target.virtNode:
-                # target qubit is local
-                def2 = target.simNode.root._get_global_lock()
-                lockedLocal = True
-            else:
-                # target qubit is remote
-                def2 = target.simNode.root.callRemote("get_global_lock")
-                lockedRemoteTarget = True
-
-        if not lockedLocal:
-            def0 = self.virtNode.root._get_global_lock()
-
-            if lockedRemoteTarget:
-                return DeferredList([def0, def1, def2], fireOnOneCallback=False, consumeErrors=True)
-            else:
-                return DeferredList([def0, def1], fireOnOneCallback=False, consumeErrors=True)
-        else:
-            if lockedRemoteTarget:
-                return DeferredList([def1, def2], fireOnOneCallback=False, consumeErrors=True)
-            else:
-                return DeferredList([def1], fireOnOneCallback=False, consumeErrors=True)
-
-    @inlineCallbacks
-    def _unlock_nodes(self, q1simNode, q1virtNode, q2simNode, q2virtNode):
-        """
-        Wrapper to acquire the global register lock on both nodes that involve the qubits. This takes different
-        arguments as lock nodes since we wish to call it with the _original_ simulated and target nodes from
-        which we got the lock - not the updated ones.
-
-        Arguments
-        q1simNode	original simulating node of the first qubit
-        q1virtNode	original virtual node of the first qubit
-        q2simNode	original simulating node of the second qubit
-        q2virtNode	original virtual node of the second qubit
-
-        """
-
-        try:
-            # Release qubit node locks
-            if q1simNode == q1virtNode:
-                # first qubit was locally simulated
-                yield self.simNode.root._release_global_lock()
-            else:
-                # first qubit was remote
-                yield q1simNode.root.callRemote("release_global_lock")
-
-            # If target was a different node
-            if q1simNode != q2simNode:
-                if q2simNode == q2virtNode:
-                    # target qubit was local
-                    yield q2simNode.root._release_global_lock()
-                else:
-                    # target qubit was remote
-                    yield q2simNode.root.callRemote("release_global_lock")
-
-            # Release local node (may be the same as above)
-            self.virtNode.root._release_global_lock()
-        except RemoteError as remote_err:
-            self.virtNode.root.reraise_remote_error(remote_err)
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def _lock_inreg(self, qubit):
-        """
-        Lock all qubits in the same register as the virtual qubit qubit.
-        """
-
-        try:
-            if qubit.simNode == qubit.virtNode:
-                yield qubit.simNode.root._lock_reg_qubits(qubit.simQubit)
-            else:
-                simNum = yield qubit.simQubit.callRemote("get_sim_number")
-                yield qubit.simNode.root.callRemote("lock_reg_qubits", simNum)
-        except RemoteError as remote_err:
-            self.virtNode.root.reraise_remote_error(remote_err)
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def _unlock_inreg(self, qubit):
-        """
-        Lock all qubits in the same register as the virtual qubit qubit.
-        """
-
-        try:
-            if qubit.simNode == qubit.virtNode:
-                yield qubit.simNode.root._unlock_reg_qubits(qubit.simQubit)
-            else:
-                simNum = yield qubit.simQubit.callRemote("get_sim_number")
-                yield qubit.simNode.root.callRemote("unlock_reg_qubits", simNum)
-        except RemoteError as remote_err:
-            self.virtNode.root.reraise_remote_error(remote_err)
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def remote_cnot_onto(self, target):
-        """
-        Performs a CNOT operation with this qubit as control, and the other qubit as target.
-
-        Arguments
-        target		the virtual qubit to use as the target of the CNOT
-        """
-
-        try:
-            success = yield self._two_qubit_gate(target, "cnot_onto")
-            return success
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def remote_cphase_onto(self, target):
-        """
-        Performs a CPHASE operation with this qubit as control, and the other qubit as target.
-
-        Arguments
-        target		the virtual qubit to use as the target of the CPHASE
-        """
-
-        try:
-            success = yield self._two_qubit_gate(target, "cphase_onto")
-            return success
-        except Exception as err:
-            raise err
-
-    @inlineCallbacks
-    def _two_qubit_gate(self, target, name):
-        """
-        Perform a two qubit gate including all the required locking.
-
-        Arguments
-        target		second virtual qubit (beyond self which is the first)
-        name		name of the gate to perform
-        """
-
-        if self.active != 1 or target.active != 1:
-            logging.error("VIRTUAL NODE %s: Attempt to manipulate qubits no longer at this node.", self.virtNode.name)
-            return
-
-        localName = "".join(["remote_", name])
-        logging.debug(
-            "VIRTUAL NODE %s: Doing 2 qubit gate name %s and local call %s", self.virtNode.name, name, localName
-        )
-
-        # Before we proceed, we need to acquire the gobal locks of the nodes holding the
-        # registers of both qubits. We wrap this in a timeout with random repeat since there is
-        # otherwise the possibility of a deadlock if two nodes compete for the _two_ locks
-        waiting = True
-        attempts = 0
-        try:
-            while waiting and attempts <= self.virtNode.root.maxAttempts:
-
-                # Set up the timeout at a random time between 1s and 4s later
-                timeoutD = Deferred()
-                timeup = reactor.callLater(random.uniform(1, 4), timeoutD.callback, None)
-
-                # Check if self simNode is locked
-                if self.simNode == self.virtNode:
-                    self_isLocked = self.simNode.root._lock.locked
-                else:
-                    self_isLocked = yield self.simNode.root.callRemote("isLocked")
-                # Check if other simNode is locked
-                if target.simNode == target.virtNode:
-                    other_isLocked = target.simNode.root._lock.locked
-                else:
-                    other_isLocked = yield target.simNode.root.callRemote("isLocked")
-
-                if self_isLocked:
-                    logging.debug(
-                        "VIRTUAL NODE {}: This SimNode {} already locked. Need to wait.".format(
-                            self.virtNode.name, self.simNode.name
-                        )
-                    )
-                    yield timeoutD
-                    attempts += 1
-                elif other_isLocked:
-                    logging.debug(
-                        "VIRTUAL NODE {}: Other SimNode {} already locked. Need to wait.".format(
-                            self.virtNode.name, target.simNode.name
-                        )
-                    )
-                    yield timeoutD
-                    attempts += 1
-
-                else:
-
-                    # Set up the lock acquisition
-                    lockD = self._lock_nodes(target)
-
-                    try:
-                        # Yield on both of them
-                        gotLock, timeoutRes = yield DeferredList(
-                            [lockD, timeoutD], fireOnOneCallback=True, fireOnOneErrback=True, consumeErrors=True
-                        )
-                    except Exception as e:
-                        logging.debug("VIRTUAL NODE %s: Cannot get lock %s", self.virtNode.name, e)
-                        yield self._unlock_nodes(self.simNode, self.virtNode, target.simNode, target.virtNode)
-                        timeup.cancel()
-                        return
-                    else:
-                        if timeoutD.called:
-                            logging.debug("VIRTUAL NODE %s: Timing out getting locks.", self.virtNode.name)
-                            lockD.cancel()
-                            yield self._unlock_nodes(self.simNode, self.virtNode, target.simNode, target.virtNode)
-                            attempts = attempts + 1
-                        elif lockD.called:
-                            waiting = False
-                            timeup.cancel()
-        except RemoteError as remote_err:
-            self.virtNode.root.reraise_remote_error(remote_err)
-        except Exception as err:
-            raise err
-
-        # We have now acquired the two relevant global node locks. If more than one qubit is locked, all code
-        # will first acquire the global lock, so this should be safe from deadlocks now, so we will not timeout
-
-        try:
-            yield self._lock_inreg(self)
-            yield self._lock_inreg(target)
-        except Exception as err:
-            raise err
-
-        # When merging registers, we may need to update the virtual qubits. Remember the original ones so we can
-        # send appropriate unlocks below. (note this assignment must be done after the locks are acquired)
-        q1simNode = self.simNode
-        q1virtNode = self.virtNode
-        q2simNode = target.simNode
-        q2virtNode = target.virtNode
-
-        # Todo a 2 qubit gate, both qubits must be in the same simulated register. We will merge
-        # registers if this is not already the case.
-        try:
-            if self.simNode == target.simNode:
-                # Both qubits are simulated at the same node
-
-                if self.simNode == self.virtNode:
-                    # Both qubits are both locally simulated, check whether they are in the same register
-
-                    if self.simQubit.register == target.simQubit.register:
-                        # They are even in the same register, just do the gate
-                        getattr(self.simQubit, localName)(target.simQubit.num)
-                    else:
-                        logging.debug("VIRTUAL NODE %s: 2qubit command demands register merge.", self.virtNode.name)
-                        # Both are local but not in the same register
-                        self.simNode.root.local_merge_regs(self.simQubit, target.simQubit)
-
-                        # After the merge, just do the gate
-                        getattr(self.simQubit, localName)(target.simQubit.num)
-                else:
-                    # Both are remotely simulated
-                    logging.debug("VIRTUAL NODE %s: 2qubit command demands remote register merge.", self.virtNode.name)
-
-                    # Fetch the details of the two simulated qubits from remote
-                    (fNum, fNode) = yield self.simQubit.callRemote("get_details")
-                    (tNum, tNode) = yield target.simQubit.callRemote("get_details")
-
-                    # Sanity check: we really have the right simulating node
-                    if fNode != self.simNode.name or tNode != target.simNode.name:
-                        logging.error("VIRTUAL NODE %s: Inconsistent simulation. Cannot merge.", self.myID.name)
-                        raise quantumError("Inconsistent simulation")
-
-                    # Merge the remote register according to the simulation IDs of the qubits
-                    yield self.simNode.root.callRemote("merge_regs", fNum, tNum)
-
-                    # Get the number of the target in the new register
-                    targetNum = yield target.simQubit.callRemote("get_number")
-
-                    # Execute the 2 qubit gate
-                    yield self.simQubit.callRemote(name, targetNum)
-                    logging.debug(
-                        "VIRTUAL NODE %s: Remote 2qubit command to %s.", self.virtNode.name, target.simNode.name
-                    )
-            else:
-                # They are simulated at two different nodes
-
-                if self.simNode == self.virtNode:
-
-                    # We are the locally simulating node of the first qubit, merge all to us
-                    logging.debug(
-                        "VIRTUAL NODE %s: 2qubit command demands merge from remote target sim %s to us.",
-                        self.simNode.name,
-                        target.simNode.name,
-                    )
-                    (fNum, fNode) = yield target.simQubit.callRemote("get_details")
-                    if fNode != target.simNode.name:
-                        logging.error("VIRTUAL NODE %s: Inconsistent simulation. Cannot merge.", self.myID.name)
-                        raise quantumError("Inconsistent simulation.")
-                    target.simQubit = yield self.simNode.root.remote_merge_from(
-                        target.simNode.name, fNum, self.simQubit.register
-                    )
-
-                    # Get the number of the target in the new register
-                    targetNum = target.simQubit.num
-
-                    # Execute the 2 qubit gate
-                    getattr(self.simQubit, localName)(targetNum)
-
-                elif target.simNode == target.virtNode:
-
-                    # We are the locally simulating node of the target qubit, merge all to us
-                    logging.debug(
-                        "VIRTUAL NODE %s: 2qubit command demands merge from remote sim %s to us.",
-                        target.simNode.name,
-                        self.simNode.name,
-                    )
-                    (fNum, fNode) = yield self.simQubit.callRemote("get_details")
-                    if fNode != self.simNode.name:
-                        logging.error("VIRTUAL NODE %s: Inconsistent simulation. Cannot merge.", self.myID.name)
-                        raise quantumError("Inconsistent simulation.")
-                    self.simQubit = yield target.simNode.root.remote_merge_from(
-                        self.simNode.name, fNum, target.simQubit.register
-                    )
-
-                    # Get the number of the target in the new register
-                    targetNum = target.simQubit.num
-
-                    # Execute the 2 qubit gate
-                    getattr(self.simQubit, localName)(targetNum)
-
-                else:
-                    # Both qubits are remotely simulated - we will pull both registers to become one local register
-                    logging.debug(
-                        "VIRTUAL NODE %s: 2qubit command demands total remote merge from %s and %s.",
-                        self.virtNode.name,
-                        target.simNode.name,
-                        self.simNode.name,
-                    )
-
-                    # Create a new local register
-                    newLocalReg = self.virtNode.root.remote_add_register()
-
-                    # Fetch the detail of the two registers from remote
-                    (fNum, fNode) = yield self.simQubit.callRemote("get_details")
-                    if fNode != self.simNode.name:
-                        logging.error("VIRTUAL NODE %s: Inconsistent simulation. Cannot merge.", self.myID.name)
-                        raise quantumError("Inconsistent simulation.")
-                    (tNum, tNode) = yield target.simQubit.callRemote("get_details")
-                    if tNode != target.simNode.name:
-                        logging.error("VIRTUAL NODE %s: Inconsistent simulation. Cannot merge.", self.myID.name)
-                        raise quantumError("Inconsistent simulation.")
-
-                    # Pull the remote registers to this node
-                    self.simQubit = yield self.virtNode.root.remote_merge_from(self.simNode.name, fNum, newLocalReg)
-                    target.simQubit = yield target.virtNode.root.remote_merge_from(
-                        target.simNode.name, tNum, newLocalReg
-                    )
-                    # Get the number of the target in the new register
-                    targetNum = target.simQubit.num
-
-                    # Finally, execute the two qubit gate
-                    logging.debug("RUN GATE")
-                    getattr(self.simQubit, localName)(targetNum)
-        except RemoteError as remote_err:
-            self.virtNode.root.reraise_remote_error(remote_err)
-        except Exception as e:
-            logging.error("VIRTUAL NODE %s: Cannot perform two qubit gate %s:", self.virtNode.name, e)
-            raise e
-
-        finally:
-            # We need to release all the locks, no matter what happened
-            yield self._unlock_inreg(self)
-            yield self._unlock_inreg(target)
-            yield self._unlock_nodes(q1simNode, q1virtNode, q2simNode, q2virtNode)
-
-        return True
-
-    @inlineCallbacks
-    def remote_get_number(self):
-        """
-        Returns the number of this qubit in whatever local register it is in. Not useful for the client,
-        but convenient for debugging.
-        """
-
-        if self.active != 1:
-            logging.error("VIRTUAL NODE %s: Attempt to manipulate qubits no longer at this node.", self.virtNode.name)
-
-        if self.virtNode == self.simNode:
-            num = self.simQubit.num
-        else:
-            try:
-                num = yield self.simQubit.callRemote("get_number")
-            except ConnectionError:
-                logging.error("VIRTUAL NODE %s: Connection failed: cannot get qubit number.")
-                return
-
-        return num
-
-    def remote_get_virt_num(self):
-        """
-        Returns the number of the virtual qubit.
-        """
-        return self.num
-
-    def remote_get_virtNode(self):
-        """
-        Returns the virtNode of this virtual qubit
-        """
-        return self.virtNode.name
-
-    def remote_get_simNode(self):
-        """
-        Returns the simNode of this virtual qubit
-        """
-        return self.simNode.name
-
-    @inlineCallbacks
-    def remote_get_qubit(self):
-        """
-        Returns the state of this qubit in real and imaginary parts separated. This is required
-        single Twisted cannot natively transfer complex valued objects.
-        """
-
-        if self.active != 1:
-            logging.error("VIRTUAL NODE %s: Attempt to manipulate qubits no longer at this node.", self.virtNode.name)
-
-        if self.virtNode == self.simNode:
-            (R, I) = self.simQubit.remote_get_qubit()
-        else:
-            try:
-                try:
-                    (R, I) = yield self.simQubit.callRemote("get_qubit")
-                except RemoteError as remote_err:
-                    self.virtNode.root.reraise_remote_error(remote_err)
-            except ConnectionError:
-                logging.error("VIRTUAL NODE %s: Connection failed: cannot get qubit number.")
-            except Exception as err:
-                raise err
-
-        return (R, I)
-
-    @inlineCallbacks
-    def remote_get_register_RI(self):
-        if self.simNode == self.virtNode:
-            realM, imagM = self.simQubit.register.get_register_RI()
-        else:
-            realM, imagM = yield self.simQubit.callRemote("get_register_RI")
-        return realM, imagM
-
-
-############################################
-#
-# Keeping track of received qubits for CQC
-
-
-class QubitCQC:
-    def __init__(self, fromName, toName, from_app_id, to_app_id, new_virt_num, rawEntInfo=None):
-        self.fromName = fromName
-        self.toName = toName
-        self.from_app_id = from_app_id
-        self.to_app_id = to_app_id
-        self.virt_num = new_virt_num
-        self.rawEntInfo = rawEntInfo
diff --git a/simulaqron/tests/__init__.py b/simulaqron/virtual_node/__init__.py
similarity index 100%
rename from simulaqron/tests/__init__.py
rename to simulaqron/virtual_node/__init__.py
diff --git a/simulaqron/virtNode/basics.py b/simulaqron/virtual_node/basics.py
similarity index 100%
rename from simulaqron/virtNode/basics.py
rename to simulaqron/virtual_node/basics.py
diff --git a/simulaqron/virtNode/projectQSimulator.py b/simulaqron/virtual_node/project_q_simulator.py
similarity index 89%
rename from simulaqron/virtNode/projectQSimulator.py
rename to simulaqron/virtual_node/project_q_simulator.py
index f39f125..c67fa48 100644
--- a/simulaqron/virtNode/projectQSimulator.py
+++ b/simulaqron/virtual_node/project_q_simulator.py
@@ -33,7 +33,7 @@
     raise RuntimeError("If you want to use the projectq backend you need to install the python package 'projectq'")
 import numpy as np
 
-from simulaqron.virtNode.basics import quantumEngine, quantumError, noQubitError
+from simulaqron.virtual_node.basics import quantumEngine, quantumError, noQubitError
 
 
 class projectQEngine(quantumEngine):
@@ -76,7 +76,7 @@ def add_fresh_qubit(self):
             raise noQubitError("No more qubits available in register.")
 
         # Prepare a clean qubit state in |0>
-        qubit = self.eng.allocate_qubit()
+        qubit = self.eng.allocate_qubit()[0]
 
         self.qubitReg.append(qubit)
 
@@ -118,12 +118,20 @@ def get_register_RI(self):
         list. Twisted only likes to send real valued lists, not complex ones.
         """
         self.eng.flush()
-        state = self.eng.backend.cheat()[1]
-
+        order, state = self.eng.backend.cheat()
+        # Update the order based on the positions in the qubitReg
+        # and not of the qubit IDs
+        q_reg_order = {}
+        for i, q in enumerate(self.qubitReg):
+            q_reg_order[i] = order[q.id]
+
+        # Note previously the format of real and imaginary numbers were
+        # expected, use the same even though Re will be the qubit mapping
+        # and Im the state
         Re = tuple(n.real for n in state)
         Im = tuple(n.imag for n in state)
 
-        return Re, Im
+        return q_reg_order, (Re, Im)
 
     def apply_H(self, qubitNum):
         """
@@ -284,7 +292,6 @@ def absorb(self, other):
         """
         Absorb the qubits from the other engine into this one. This is done by tensoring the state at the end.
         """
-
         # Check whether there is space
         newNum = self.activeQubits + other.activeQubits
         if newNum > self.maxQubits:
@@ -294,21 +301,10 @@ def absorb(self, other):
         if self.activeQubits == 0:
             self.eng = other.eng
             self.qubitReg = list(other.qubitReg)
+            self.activeQubits = other.activeQubits
         elif other.activeQubits > 0:
-            # Get the current state of the other engine
-            other.eng.flush()
-            other_state = other.eng.backend.cheat()[1]
-
-            # Allocate qubits in this engine for the new qubits from the other engine
-            qreg = self.eng.allocate_qureg(other.activeQubits)
-
-            # Put the new qubits in the correct state
-            pQ.ops.StatePreparation(other_state) | qreg
-
-            # Add the qubits to the list of qubits
-            self.qubitReg += list(qreg)
-
-        self.activeQubits = newNum
+            data = other.get_register_RI()
+            self.absorb_parts(*data, other.activeQubits)
 
     def absorb_parts(self, R, I, activeQ):
         """
@@ -325,6 +321,8 @@ def absorb_parts(self, R, I, activeQ):
             raise quantumError("Cannot merge: qubits exceed the maximum available.\n")
 
         if activeQ > 0:
+            # Unpack the ordering of qubits and the real and imaginary part
+            order, (R, I) = R, I
 
             # Convert the real and imaginary parts to a state
             state = [re + im * 1j for re, im in zip(R, I)]
@@ -335,7 +333,15 @@ def absorb_parts(self, R, I, activeQ):
             # Put the new qubits in the correct state
             pQ.ops.StatePreparation(state) | qreg
 
+            # Put the qubits in the correct order
+            # The `order` is a mapping from the previous qubit IDs
+            # to the bit position in the state. The qubits in the `qreg`
+            # are therefore in the old bit positions which needs to be updated.
+            new_qubits = [None] * len(qreg)
+            for old_q_id, old_bit_pos in order.items():
+                new_qubits[old_q_id] = qreg[old_bit_pos]
+
             # Add the qubits to the list of qubits
-            self.qubitReg += list(qreg)
+            self.qubitReg += new_qubits
 
             self.activeQubits = newNum
diff --git a/simulaqron/virtNode/quantum.py b/simulaqron/virtual_node/quantum.py
similarity index 78%
rename from simulaqron/virtNode/quantum.py
rename to simulaqron/virtual_node/quantum.py
index 1657a19..e6c9b68 100644
--- a/simulaqron/virtNode/quantum.py
+++ b/simulaqron/virtual_node/quantum.py
@@ -30,11 +30,15 @@
 import time
 
 import numpy as np
-from simulaqron import settings
 from twisted.spread import pb
 from twisted.internet.defer import DeferredLock
+from twisted.internet import reactor
+from twisted.internet.task import deferLater
+from twisted.internet.defer import inlineCallbacks
+
+from netqasm.logging.glob import get_netqasm_logger
 
-import logging
+from simulaqron import settings
 
 
 class simulatedQubit(pb.Referenceable):
@@ -69,22 +73,39 @@ def __init__(self, node, register, simNum, num=0):
         # Mark this qubit as active (still connected to a register)
         self.active = True
 
+        # Time until retry
+        self._delay = 1
+
         # Optional parameters for when the simulation is noise
         self.noisy = settings.simulaqron_settings.noisy_qubits
         self.T1 = settings.simulaqron_settings.t1
         self.last_accessed = time.time()
 
+        self._logger = get_netqasm_logger(
+            f"{self.__class__.__name__}(node={node.name}, sim_num={simNum})"
+        )
+
+    @inlineCallbacks
     def lock(self):
-        self._lock.acquire()
+        self._logger.debug(f"locking sim qubit in register with num {self.register.num}")
+        while self.isLocked():
+            yield deferLater(reactor, self._delay, lambda: None)
+        yield self._lock.acquire()
+        self._logger.debug(f"got lock for sim qubit in register with num {self.register.num}")
 
+    @inlineCallbacks
     def remote_lock(self):
-        self._lock.acquire()
+        yield self.lock()
 
     def unlock(self):
-        self._lock.release()
+        try:
+            self._logger.debug(f"unlocking sim qubit in register with num {self.register.num}")
+            self._lock.release()
+        except AssertionError as exc:
+            self._logger.error(f"AssertionError {exc}")
 
     def remote_unlock(self):
-        self._lock.release()
+        self.unlock()
 
     def isLocked(self):
         return self._lock.locked
@@ -103,13 +124,13 @@ def make_fresh(self):
         num = self.register.add_fresh_qubit()
         self.num = num
 
-        logging.info("QUANTUM %s: Adding qubit number %d to register %d", self.node.name, num, self.register.num)
+        self._logger.info("QUANTUM %s: Adding qubit number %d to register %d", self.node.name, num, self.register.num)
 
     def remote_apply_X(self):
         """
         Apply X gate to itself by passing it onto the underlying register.
         """
-        logging.debug("VIRTUAL NODE %s: applying X to number %d", self.node.name, self.num)
+        self._logger.debug("VIRTUAL NODE %s: applying X to number %d", self.node.name, self.num)
         self._apply_random_pauli_noise()
         self.register.apply_X(self.num)
 
@@ -117,7 +138,7 @@ def remote_apply_K(self):
         """
         Apply K gate to itself by passing it onto the underlying register. Maps computational to Y eigenbasis.
         """
-        logging.debug("VIRTUAL NODE %s: applying K to number %d", self.node.name, self.num)
+        self._logger.debug("VIRTUAL NODE %s: applying K to number %d", self.node.name, self.num)
         self._apply_random_pauli_noise()
         self.register.apply_K(self.num)
 
@@ -125,7 +146,7 @@ def remote_apply_Y(self):
         """
         Apply Y gate.
         """
-        logging.debug("VIRTUAL NODE %s: applying Y to number %d", self.node.name, self.num)
+        self._logger.debug("VIRTUAL NODE %s: applying Y to number %d", self.node.name, self.num)
         self._apply_random_pauli_noise()
         self.register.apply_Y(self.num)
 
@@ -133,7 +154,7 @@ def remote_apply_Z(self):
         """
         Apply Z gate.
         """
-        logging.debug("VIRTUAL NODE %s: applying Z to number %d", self.node.name, self.num)
+        self._logger.debug("VIRTUAL NODE %s: applying Z to number %d", self.node.name, self.num)
         self._apply_random_pauli_noise()
         self.register.apply_Z(self.num)
 
@@ -141,7 +162,7 @@ def remote_apply_H(self):
         """
         Apply H gate.
         """
-        logging.debug("VIRTUAL NODE %s: applying H to number %d", self.node.name, self.num)
+        self._logger.debug("VIRTUAL NODE %s: applying H to number %d", self.node.name, self.num)
         self._apply_random_pauli_noise()
         self.register.apply_H(self.num)
 
@@ -149,7 +170,7 @@ def remote_apply_T(self):
         """
         Apply T gate.
         """
-        logging.debug("VIRTUAL NODE %s: applying T to number %d", self.node.name, self.num)
+        self._logger.debug("VIRTUAL NODE %s: applying T to number %d", self.node.name, self.num)
         self._apply_random_pauli_noise()
         self.register.apply_T(self.num)
 
@@ -162,7 +183,7 @@ def remote_apply_rotation(self, *args):
         """
         n = args[0]
         a = args[1]
-        logging.debug(
+        self._logger.debug(
             "VIRTUAL NODE %s: applying rotation to number %d. Axis=%s,angle=%s",
             self.node.name,
             self.num,
@@ -203,7 +224,7 @@ def remote_cnot_onto(self, targetNum):
         targetNum    the qubit to use as the target of the CNOT
         """
 
-        logging.debug("VIRTUAL NODE %s: CNOT from %d to %d", self.node.name, self.num, targetNum)
+        self._logger.debug("VIRTUAL NODE %s: CNOT from %d to %d", self.node.name, self.num, targetNum)
         self._apply_random_pauli_noise()
         self.register.apply_CNOT(self.num, targetNum)
 
@@ -251,10 +272,10 @@ def remote_get_qubit(self):
         """
         Returns the state of the qubits in the list qList by tracing out the rest.
         """
-        backend = settings.simulaqron_settings.backend
-        if backend != "qutip":
+        backend = settings.simulaqron_settings.sim_backend
+        if backend != settings.SimBackend.QUTIP.value:
             raise RuntimeError("Cannot get reduced qubit state using backend {}".format(backend))
-        logging.debug("VIRTUAL NODE %s: Returning qubit %d", self.node.name, self.num)
+        self._logger.debug("VIRTUAL NODE %s: Returning qubit %d", self.node.name, self.num)
         return self.register.get_qubits_RI([self.num])
 
     def remote_get_details(self):
@@ -275,11 +296,11 @@ def _apply_random_pauli_noise(self):
         p = (1 - np.exp(-t / self.T1)) / 4
         x = random.random()
         if x < p:
-            logging.debug("VIRTUAL NODE %s: random pauli X applied on %d", self.node.name, self.num)
+            self._logger.debug("VIRTUAL NODE %s: random pauli X applied on %d", self.node.name, self.num)
             self.register.apply_X(self.num)
         elif x < 2 * p:
-            logging.debug("VIRTUAL NODE %s: random pauli Y applied on %d", self.node.name, self.num)
+            self._logger.debug("VIRTUAL NODE %s: random pauli Y applied on %d", self.node.name, self.num)
             self.register.apply_Y(self.num)
         elif x < 3 * p:
-            logging.debug("VIRTUAL NODE %s: random pauli Z applied on %d", self.node.name, self.num)
+            self._logger.debug("VIRTUAL NODE %s: random pauli Z applied on %d", self.node.name, self.num)
             self.register.apply_Z(self.num)
diff --git a/simulaqron/virtNode/qutipSimulator.py b/simulaqron/virtual_node/qutip_simulator.py
similarity index 99%
rename from simulaqron/virtNode/qutipSimulator.py
rename to simulaqron/virtual_node/qutip_simulator.py
index 4af8807..4308f7f 100644
--- a/simulaqron/virtNode/qutipSimulator.py
+++ b/simulaqron/virtual_node/qutip_simulator.py
@@ -37,7 +37,7 @@
 except ImportError:
     raise RuntimeError("If you want to use the qutip backend you need to install the python package 'qutip'")
 
-from simulaqron.virtNode.basics import quantumEngine, quantumError, noQubitError
+from simulaqron.virtual_node.basics import quantumEngine, quantumError, noQubitError
 
 
 class qutipEngine(quantumEngine):
diff --git a/simulaqron/virtNode/stabilizerSimulator.py b/simulaqron/virtual_node/stabilizer_simulator.py
similarity index 93%
rename from simulaqron/virtNode/stabilizerSimulator.py
rename to simulaqron/virtual_node/stabilizer_simulator.py
index 36fda52..aa53310 100644
--- a/simulaqron/virtNode/stabilizerSimulator.py
+++ b/simulaqron/virtual_node/stabilizer_simulator.py
@@ -26,9 +26,9 @@
 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from simulaqron.virtNode.basics import quantumEngine, quantumError, noQubitError
-from simulaqron.toolbox.stabilizerStates import StabilizerState
+from simulaqron.virtual_node.basics import quantumEngine, quantumError, noQubitError
+from simulaqron.toolbox.stabilizer_states import StabilizerState
+from simulaqron.general import SimUnsupportedError
 
 
 class stabilizerEngine(quantumEngine):
@@ -144,7 +144,7 @@ def apply_T(self, qubitNum):
         """
         Applies a T gate to the qubits with number qubitNum.
         """
-        raise AttributeError("Cannot apply T gate in stabilizer formalism")
+        raise SimUnsupportedError("Cannot apply T gate in stabilizer formalism")
 
     def apply_rotation(self, qubitNum, n, a):
         """
@@ -158,7 +158,7 @@ def apply_rotation(self, qubitNum, n, a):
         :param a: float
             The rotation angle in radians.
         """
-        raise AttributeError("Cannot apply arbitrary rotation gate in stabilizer formalism")
+        raise SimUnsupportedError("Cannot apply arbitrary rotation gate in stabilizer formalism")
 
     def apply_CNOT(self, qubitNum1, qubitNum2):
         """
@@ -182,7 +182,7 @@ def apply_onequbit_gate(self, gate, qubitNum):
         qubitNum 	the number of the qubit this gate is applied to
         """
 
-        raise AttributeError("Cannot apply arbitrary one qubit gate in stabilizer formalism")
+        raise SimUnsupportedError("Cannot apply arbitrary one qubit gate in stabilizer formalism")
 
     def apply_twoqubit_gate(self, gate, qubit1, qubit2):
         """
@@ -193,7 +193,7 @@ def apply_twoqubit_gate(self, gate, qubit1, qubit2):
         qubit1 		the first qubit
         qubit2		the second qubit
         """
-        raise AttributeError("Cannot apply arbitrary two qubit gate in stabilizer formalism")
+        raise SimUnsupportedError("Cannot apply arbitrary two qubit gate in stabilizer formalism")
 
     def measure_qubit_inplace(self, qubitNum):
         """
diff --git a/simulaqron/virtual_node/virtual.py b/simulaqron/virtual_node/virtual.py
new file mode 100644
index 0000000..66a6187
--- /dev/null
+++ b/simulaqron/virtual_node/virtual.py
@@ -0,0 +1,1768 @@
+#
+# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#    This product includes software developed by Stephanie Wehner, QuTech.
+# 4. Neither the name of the QuTech organization nor the
+#    names of its contributors may be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import random
+# import traceback
+
+from collections import deque
+
+from twisted.spread import pb
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, DeferredLock, Deferred, DeferredList
+from twisted.internet.task import deferLater
+from twisted.internet.error import ConnectionRefusedError, CannotListenError
+from twisted.spread.pb import RemoteError, RemoteReference
+
+from netqasm.logging.glob import get_netqasm_logger
+
+from simulaqron.virtual_node.basics import quantumError, noQubitError, virtNetError
+from simulaqron.virtual_node.quantum import simulatedQubit
+from simulaqron.general.host_config import SocketsConfig
+from simulaqron.settings import simulaqron_settings, SimBackend
+
+if simulaqron_settings.sim_backend == SimBackend.QUTIP.value:
+    from simulaqron.virtual_node.qutip_simulator import qutipEngine
+elif simulaqron_settings.sim_backend == SimBackend.PROJECTQ.value:
+    from simulaqron.virtual_node.project_q_simulator import projectQEngine
+elif simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+    from simulaqron.virtual_node.stabilizer_simulator import stabilizerEngine
+else:
+    raise quantumError(f"Unknown backend {simulaqron_settings.sim_backend}")
+
+
+def reraise_remote_error(self, remote_err):
+    """
+    This is a function re-raises the error thrown remotely
+    :param remote_err: :obj:`twisted.spread.pb.RemoteError`
+    :return: class
+    """
+    # Get name of remote error
+    error_name = remote_err.remoteType.split(b".")[-1].decode()
+
+    # Get class of remote error
+    error_class = eval(error_name)
+
+    raise error_class(str(remote_err))
+
+
+@inlineCallbacks
+def call_method(obj, method_name, *args, **kwargs):
+    """Convenience method to call a method on an object, or a remote reference to an object"""
+    if isinstance(obj, RemoteReference):
+        try:
+            output = yield obj.callRemote(method_name, *args, **kwargs)
+        except RemoteError as remote_err:
+            reraise_remote_error(remote_err)
+        except Exception as err:
+            raise err
+    else:
+        output = getattr(obj, f"remote_{method_name}")(*args, **kwargs)
+        if isinstance(output, Deferred):
+            output = yield output
+    return output
+
+
+######
+#
+# Backend - starts the local virtual node and connects to the other virtual nodes
+# forming the quantum network
+#
+class Backend(object):
+    def __init__(self, name, virtualFile, network_name="default"):
+        """
+        Initialize. This will read the configuration file and populate the name,hostname,port information with the
+        information found in the configuration file for the given name.
+        """
+        self._logger = get_netqasm_logger(f"{self.__class__.__name__}({name})")
+
+        # Read the configuration file
+        try:
+            self.config = SocketsConfig(virtualFile, network_name=network_name, config_type="vnode")
+            self.myID = self.config.hostDict[name]
+        except KeyError as e:
+            self._logger.error(f"No such name in the configuration file {virtualFile}: {e}")
+            raise e
+        except Exception as e:
+            self._logger.error(f"Error reading the configuration file {virtualFile}: {e}")
+            raise e
+
+    def start(self, maxQubits=simulaqron_settings.max_qubits, maxRegisters=simulaqron_settings.max_registers):
+        """
+        Start listening to requests from other nodes.
+
+        Arguments
+        maxQubits	maximum qubits in the default register
+        """
+
+        try:
+            self._logger.debug(f"Starting on port {self.myID.port}")
+            node = virtualNode(self.myID, self.config, maxQubits=maxQubits, maxRegisters=maxRegisters)
+            reactor.listenTCP(self.myID.port, pb.PBServerFactory(node))
+
+            self._logger.debug("Running reactor")
+            reactor.run()
+        except CannotListenError:
+            self._logger.error(f"NetQASM server address ({self.myID.port}) is already in use.")
+            return
+        except Exception as e:
+            self._logger.error(f"Critical error when starting local virtual node server: {e}")
+            return
+
+
+#######
+#
+# virtualNode - this is the virtual quantum node. It keeps track of registers simulated here, qubits
+# virtually available at this node, etc
+#
+
+
+class virtualNode(pb.Root):
+    def __init__(self, ID, config, maxQubits=simulaqron_settings.max_qubits,
+                 maxRegisters=simulaqron_settings.max_registers):
+        """
+        Initialize storing also our own name, hostname and port.
+
+        Arguments:
+        ID		host identifier of this node
+        maxQubits	maximum number of qubits to use in the default engine (default 10)
+        maxRegister	maximum number of registers
+        """
+        self._logger = get_netqasm_logger(f"{self.__class__.__name__}({ID.name})")
+
+        # Store our own host identifiers and configuration
+        self.myID = ID
+        self.myID.root = self
+        self.config = config
+
+        # Set max nr of registers and virtual qubits
+        self.maxRegs = maxRegisters
+        self.maxQubits = maxQubits
+
+        # List of connections
+        self.conn = {}
+
+        # Number of registers _created_ at this node
+        # this may not equal the numbers of registers virtually carried
+        self.numRegs = 0
+
+        # Counter for used register numbers
+        self._next_reg_num = 0
+
+        # Set up the dictionary of registers
+        self.registers = {}
+
+        # Initialize the list of qubits at this node
+        self.virtQubits = []
+        self.simQubits = []
+
+        # Set up connections to the neighouring nodes in the network
+        self.connectNet()
+
+        # Global lock: needs to be acquire whenever we want to manipulate more than one
+        # qubit object
+        self._lock = DeferredLock()
+
+        # Time until retry
+        self._delay = 1
+
+        # Maximum number of attempts at getting locks
+        self.maxAttempts = 300
+
+        # List of qubit received to be polled by NetQASM
+        self.qubit_recv = {}
+
+        # List of halves of epr-pairs received to be polled by NetQASM
+        self.qubit_recv_epr = {}
+
+    def connectNet(self):
+        """
+        Initialize the connections to the other virtual nodes in the network according to the available
+        configuration.
+        """
+
+        for key in self.config.hostDict:
+            node = self.config.hostDict[key]
+            if node.name != self.myID.name:
+                self.connect_to_node(node)
+            else:
+                self.conn[node.name] = node
+
+    def remote_check_connections(self):
+        """
+        Checks if all connections are up. (Just checks if the number of
+        connections equal the number of nodes in config-file)
+        """
+        return len(self.conn) == len(self.config.hostDict)
+
+    @inlineCallbacks
+    def get_connection(self, name):
+        """
+        Returns the connection specified by 'name'. If no such connection is
+        up yet but name is in the configuration file, wait and try again.
+        """
+        if name in self.conn:
+            return self.conn[name]
+        else:
+            self._logger.debug(f"Connection to {name} not up yet, need to wait...")
+            conn_to_return = yield deferLater(
+                reactor,
+                simulaqron_settings.conn_retry_time,
+                self.get_connection,
+                name,
+            )
+            return conn_to_return
+
+    def connect_to_node(self, node):
+        """
+        Connects to other node. If node not up yet, waits for CONF_WAIT_TIME seconds.
+        """
+        self._logger.debug(f"Trying to connect to node {node.name}.")
+        node.factory = pb.PBClientFactory()
+        reactor.connectTCP(node.hostname, node.port, node.factory)
+        defer = node.factory.getRootObject()
+        defer.addCallback(self.handle_connection, node)
+        defer.addErrback(self.handle_connection_error, node)
+
+    def handle_connection(self, obj, node):
+        """
+        Callback obtaining twisted root object when connection to the node given by the node details 'node'.
+        """
+        self._logger.debug(f"New connection to {node.name}.")
+        # Retrieve the root object: virtualNode on the remote
+        node.root = obj
+
+        # Add this node to the local connections
+        self.conn[node.name] = node
+
+    def handle_connection_error(self, reason, node):
+        """
+        Handles errors from trying to connect to other node.
+        If a ConnectionRefusedError is raised another try will be made after CONF_WAIT_TIME seconds.
+        CONF_WAIT_TIME is set in 'settings.py'.
+        Any other error is raised again.
+        """
+
+        try:
+            reason.raiseException()
+        except ConnectionRefusedError:
+            self._logger.debug(f"Could not connect to {node.name}, trying again...")
+            reactor.callLater(simulaqron_settings.conn_retry_time, self.connect_to_node, node)
+        except Exception as e:
+            self._logger.error(e)
+            reactor.stop()
+
+    def get_virtual_id(self):
+        """
+        This is a crude and horrible cludge to generate unique IDs for virtual qubits.
+        """
+
+        # Loop through the firt k numbers where k is the number of virtual qubits + 1
+        # Note that this is guaranteed to find a an index which is not yet used
+        for j in range(len(self.virtQubits) + 1):
+            used = 0
+            for q in self.virtQubits:
+                if q.num == j:
+                    used = 1
+            if used == 0:
+                return j
+
+    def get_sim_id(self):
+        """
+        Similarly, this is a crude and horrible cludge to generate unique IDs for simulated qubits.
+        """
+
+        # Loop through the firt k numbers where k is the number of virtual qubits + 1
+        # Note that this is guaranteed to find a an index which is not yet used
+        for j in range(len(self.simQubits) + 1):
+            used = 0
+            for q in self.simQubits:
+                if q.simNum == j:
+                    used = 1
+            if used == 0:
+                return j
+
+    def _q_num_to_obj(self, num):
+        """
+        Given the simulation number of a qubit simulated here, return the corresponding object.
+        """
+        for q in self.simQubits:
+            if q.simNum == num:
+                return q
+        return None
+
+    def remote_isLocked(self):
+        return self._lock.locked
+
+    @inlineCallbacks
+    def _get_global_lock(self):
+        self._logger.debug("GETTING LOCK")
+        while self._lock.locked:
+            yield deferLater(reactor, self._delay, lambda: None)
+        yield self._lock.acquire()
+        self._logger.debug("GOT LOCK")
+
+    @inlineCallbacks
+    def remote_get_global_lock(self):
+        yield self._get_global_lock()
+
+    def _release_global_lock(self):
+        self._logger.debug("RELEASE LOCK")
+        if self._lock.locked:
+            self._lock.release()
+
+    def remote_release_global_lock(self):
+        self._release_global_lock()
+
+    @inlineCallbacks
+    def _lock_reg_qubits(self, qubit):
+        """
+        Acquire the lock on all qubits in the same register as the local sim qubit qubit.
+        """
+        for q in self.simQubits:
+            if q.register == qubit.register:
+                yield q.lock()
+
+    @inlineCallbacks
+    def remote_lock_reg_qubits(self, qubitNum):
+        """
+        Acquire the lock on all qubits in the same register as qubitNum.
+        """
+        yield self._lock_reg_qubits(self._q_num_to_obj(qubitNum))
+
+    @inlineCallbacks
+    def _unlock_reg_qubits(self, qubit):
+        """
+        Release the lock on all qubits in the same register as qubit.
+        """
+        for q in self.simQubits:
+            if q.register == qubit.register:
+                yield q.unlock()
+
+    @inlineCallbacks
+    def remote_unlock_reg_qubits(self, qubitNum):
+        """
+        Release the lock on all qubits in the same register as qubitNum.
+        """
+        yield self._unlock_reg_qubits(self._q_num_to_obj(qubitNum))
+
+    def remote_add_register(self, maxQubits=10):
+        """
+        Adds a new register to the node..
+
+        Arguments:
+        maxQubits	maximum number of qubits to use in the default engine
+        """
+        # TODO We have two methods that do the same thing, should deprecate one of them
+        return self.remote_new_register(maxQubits=maxQubits)
+
+    def get_new_reg_num(self):
+        """
+        Returns an unused register number.
+        """
+        reg_num = self._next_reg_num
+        self._next_reg_num += 1
+        return reg_num
+
+    def remote_new_register(self, maxQubits=10):
+        """
+        Initialize a local register. Right now, this simple creates a register according to the simple engine backend
+        using qubit.
+
+        Arguments:
+        maxQubits	maximum number of qubits to use in the default engine (default 10)
+        """
+
+        # Make sure that reg numbers are assigned correctly
+        if self.numRegs >= self.maxRegs:
+            self._logger.error("Maximum number of registers reached.")
+            raise quantumError("Maximum number of registers reached.")
+
+        self.numRegs = self.numRegs + 1
+        regNum = self.get_new_reg_num()
+        if simulaqron_settings.sim_backend == SimBackend.QUTIP.value:
+            newReg = qutipEngine(self.myID, regNum, maxQubits)
+        elif simulaqron_settings.sim_backend == SimBackend.PROJECTQ.value:
+            newReg = projectQEngine(self.myID, regNum, maxQubits)
+        elif simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+            newReg = stabilizerEngine(self.myID, regNum, maxQubits)
+        else:
+            raise quantumError(f"Unknown backend {simulaqron_settings.sim_backend}")
+
+        self.registers[regNum] = newReg
+
+        self._logger.debug("Initializing new simulated register.")
+        return newReg
+
+    def remote_delete_register(self, reg):
+        """
+        Removes the register from the node.
+        Happens if the last qubit in the register is measured out.
+        """
+
+        # Get register number
+        regnum = reg.num
+
+        # Remove register
+        self.registers.pop(regnum)
+        self.numRegs -= 1
+
+    @inlineCallbacks
+    def remote_new_qubit(self, ignore_max_qubits=False):
+        """
+        Create a new qubit in the default local register.
+
+        :param ignore_max_qubits: bool
+            Used to ignore the check if max virtual qubits is reached. This is used when creating EPR pairs
+            to be able to temporarily create a qubit.
+        """
+        self._logger.debug("Request to create new qubit.")
+
+        # Get a lock to assure IDs are assigned correctly and maxQubits is consitently checked
+        yield self._get_global_lock()
+
+        try:
+            if (len(self.virtQubits) >= self.maxQubits) and (not ignore_max_qubits):
+                self._logger.error("Maximum number of virtual qubits reached.")
+                raise noQubitError("Max virtual qubits reached")
+            else:
+                # Qubit in the simulation backend, initialized to |0>
+                simNum = self.get_sim_id()
+
+                # Create a new register
+                newReg = self.remote_add_register()
+
+                simQubit = simulatedQubit(self.myID, newReg, simNum)
+                simQubit.make_fresh()
+
+                self.simQubits.append(simQubit)
+
+                # Virtual qubit
+                newNum = self.get_virtual_id()
+                newQubit = virtualQubit(self.myID, self.myID, simQubit, newNum)
+                self.virtQubits.append(newQubit)
+        finally:
+            self._release_global_lock()
+
+        return newQubit
+
+    @inlineCallbacks
+    def remote_new_qubit_inreg(self, reg):
+        """
+        Create a new qubit in the specified register reg.
+        """
+
+        # Only allow if the register is local
+        if reg.simNode != self.myID:
+            raise quantumError("Can only create qubits registers simulated locally by this node.")
+
+        # Get a lock to assure IDs are assigned correctly and maxQubits is consitently checked
+        yield self._get_global_lock()
+
+        try:
+            if len(self.virtQubits) >= self.maxQubits:
+                self._logger.error("Maximum number of virtual qubits reached.")
+                raise noQubitError("Max virtual qubits reached")
+            else:
+                # Qubit in the local simulation backend, initialized to |0>
+                simNum = self.get_sim_id()
+                simQubit = simulatedQubit(self.myID, reg, simNum)
+                simQubit.make_fresh()
+                self.simQubits.append(simQubit)
+
+                # Virtual qubit
+                newNum = self.get_virtual_id()
+                newQubit = virtualQubit(self.myID, self.myID, simQubit, newNum)
+                self.virtQubits.append(newQubit)
+        finally:
+            self._release_global_lock()
+
+        return newQubit
+
+    @inlineCallbacks
+    def remote_netqasm_send_qubit(self, num, targetName, app_id, remote_app_id):
+        """
+        Send interface for NetQASM to add the qubit to the remote nodes received list for an application.
+
+        Arguments:
+        num		number of virtual qubit to send
+        targetName	name of the node to send to
+        app_id		application asking to have this qubit delivered
+        remote_app_id	application ID to deliver the qubit to
+        """
+        self._logger.debug(f"request to send qubit {num} to {targetName}")
+
+        virtQubit = self.remote_get_virtual_ref(num)
+
+        newVirtNum = yield self.remote_send_qubit(virtQubit, targetName)
+
+        # Lookup host ID of node
+        if not (targetName in self.config.hostDict):
+            raise virtNetError(
+                f"Trying to get conncetion to virtual node {targetName}, but this is not in configuration file"
+            )
+        remoteNode = yield self.get_connection(targetName)
+
+        # Ask to add to list
+        try:
+            yield call_method(
+                remoteNode.root,
+                "netqasm_add_recv_list",
+                self.myID.name,
+                app_id,
+                remote_app_id,
+                newVirtNum,
+            )
+        except RemoteError as remote_err:
+            self.reraise_remote_error(remote_err)
+
+    def remote_netqasm_add_recv_list(self, fromName, from_epr_socket_id, to_epr_socket_id, new_virt_num=None):
+        """
+        Add an item to the received list for use in NetQASM.
+        """
+
+        if not (to_epr_socket_id in self.qubit_recv):
+            self.qubit_recv[to_epr_socket_id] = deque([])
+
+        self.qubit_recv[to_epr_socket_id].append(
+            QubitNetQASM(
+                fromName,
+                self.myID.name,
+                from_epr_socket_id,
+                to_epr_socket_id,
+                new_virt_num,
+            )
+        )
+        self._logger.debug(f"Added a qubit on EPR socket ID {to_epr_socket_id} to recv list")
+
+    def remote_netqasm_get_recv(self, to_epr_socket_id):
+        """
+        Retrieve the next qubit with the given app ID form the received list.
+        """
+
+        self._logger.debug(f"Trying to retrieve qubit on EPR socket ID {to_epr_socket_id} from recv list")
+        # Get the list corresponding to the specified application ID
+        if not (to_epr_socket_id in self.qubit_recv):
+            return None
+
+        qQueue = self.qubit_recv[to_epr_socket_id]
+        if not qQueue:
+            return None
+
+        # Retrieve the first element on that list (first in, first out)
+        qc = qQueue.popleft()
+        if not qc:
+            return None
+
+        self._logger.debug(f"Returning qubit on EPR socket ID {to_epr_socket_id} from recv list")
+        return self.remote_get_virtual_ref(qc.virt_num)
+
+    @inlineCallbacks
+    def remote_netqasm_send_epr_half(self, num, targetName, app_id, remote_app_id, rawEntInfo):
+        """
+        Send interface for NetQASM to add the qubit to the remote nodes received list for an application.
+
+        Arguments:
+        num		number of virtual qubit to send
+        targetName	name of the node to send to
+        app_id		application asking to have this qubit delivered
+        remote_app_id	application ID to deliver the qubit to
+        entInfo		entanglement information
+        """
+        if num is None:
+            # Only an outcome from measure directly so no qubit
+            newVirtNum = None
+        else:
+            qubit = self.remote_get_virtual_ref(num)
+
+            newVirtNum = yield self.remote_send_qubit(qubit, targetName)
+
+        # Lookup host ID of node
+        if not (targetName in self.config.hostDict):
+            raise virtNetError(
+                f"Trying to get conncetion to virtual node {targetName}, but this is not in configuration file"
+            )
+        remoteNode = yield self.get_connection(targetName)
+
+        # Ask to add to list
+        try:
+            yield call_method(
+                remoteNode.root,
+                "netqasm_add_epr_list",
+                self.myID.name,
+                app_id,
+                remote_app_id,
+                newVirtNum,
+                rawEntInfo,
+            )
+        except RemoteError as remote_err:
+            self.reraise_remote_error(remote_err)
+
+    def remote_netqasm_add_epr_list(self, fromName, from_epr_socket_id, to_epr_socket_id, new_virt_num, rawEntInfo):
+        """
+        Add an item to the epr list for use in NetQASM.
+        """
+
+        if not (to_epr_socket_id in self.qubit_recv_epr):
+            self._logger.debug(f"Creating epr list for EPR socket ID {to_epr_socket_id}")
+            self.qubit_recv_epr[to_epr_socket_id] = deque([])
+
+        self.qubit_recv_epr[to_epr_socket_id].append(
+            QubitNetQASM(
+                fromName,
+                self.myID.name,
+                from_epr_socket_id,
+                to_epr_socket_id,
+                new_virt_num,
+                rawEntInfo=rawEntInfo,
+            )
+        )
+        self._logger.debug(f"Added a qubit on EPR socket ID {to_epr_socket_id} to epr list")
+
+    def remote_netqasm_get_epr_recv(self, to_epr_socket_id):
+        """
+        Retrieve the next qubit (half of an EPR-pair) with the given app ID from the received list.
+        """
+        self._logger.debug(f"Trying to retrieve qubit on EPR socket ID {to_epr_socket_id} from epr list")
+        # Get the list corresponding to the specified application ID
+        if not (to_epr_socket_id in self.qubit_recv_epr):
+            self._logger.debug(f"No epr list for EPR socket ID {to_epr_socket_id}")
+            return None
+
+        qQueue = self.qubit_recv_epr[to_epr_socket_id]
+        if not qQueue:
+            self._logger.debug(f"Nothing in epr list for EPR socket ID {to_epr_socket_id}")
+            return None
+
+        # Retrieve the first element on that list (first in, first out)
+        qc = qQueue.popleft()
+        if not qc:
+            self._logger.debug(f"First element in epr list is empty for EPR socket ID {to_epr_socket_id}")
+            return None
+
+        self._logger.debug(f"Returning qubit on EPR socket ID {to_epr_socket_id} from epr list")
+        return self.remote_get_virtual_ref(qc.virt_num), qc.rawEntInfo
+
+    @inlineCallbacks
+    def remote_send_qubit(self, qubit, targetName):
+        """
+        Sends the qubit to the specified target node. This creates a new virtual qubit object at the remote node
+        with the right qubit and backend details.
+
+        Arguments
+        qubit		virtual qubit to be sent
+        targetName	target ndoe to place qubit at (host object)
+        """
+        self._logger.debug(f"Request to send qubit sim Num {qubit.num} to {targetName}.")
+        if qubit.active != 1:
+            self._logger.debug("Attempt to manipulate qubit no longer at this node.")
+            return
+
+        # Lookup host id of node
+        if not (targetName in self.config.hostDict):
+            raise virtNetError(
+                f"Trying to get conncetion to virtual node {targetName}, but this is not in configuration file"
+            )
+        remoteNode = yield self.get_connection(targetName)
+
+        # Get lock to prevent access to qubits between sending and manipulating local list
+        yield self._get_global_lock()
+
+        try:
+            # Check whether we are just the virtual, or also the simulating node
+            if qubit.virtNode == qubit.simNode:
+                self._logger.debug("Sending qubit simulated locally")
+                # We are both the virtual as well as the simulating node
+                # Pass a reference to our locally simulated qubit object to the remote node
+                try:
+                    newNum = yield call_method(remoteNode.root, "add_qubit", self.myID.name, qubit.simQubit)
+                except RemoteError as remote_err:
+                    self.reraise_remote_error(remote_err)
+            else:
+                self._logger.debug(f"Sending qubit simulated remotely at {qubit.simNode.name}")
+                # Also lock the virtual node of the simulating node unless it is the remoteNode or this node
+                locked_node = yield self._lock_simulating_node(exclude=[self.virtNode, remoteNode])
+                try:
+                    # We are only the virtual node, not the simulating one. In this case, we need to ask
+                    # the actual simulating node to do the transfer for us. Due to the pecularities of Twisted PB
+                    # we need to do this by the simulated qubit number
+                    try:
+                        simQubitNum = yield call_method(qubit.simQubit, "get_sim_number")
+                    except RemoteError as remote_err:
+                        self.reraise_remote_error(remote_err)
+                    try:
+                        newNum = yield call_method(qubit.simNode.root, "transfer_qubit", simQubitNum, targetName)
+                    except RemoteError as remote_err:
+                        self.reraise_remote_error(remote_err)
+                finally:
+                    if locked_node is not None:
+                        yield call_method(locked_node.root, "release_global_lock")
+
+            # We gave it away so mark as inactive
+            qubit.active = 0
+
+            # Remove the qubit from the local virtual list. Note it remains in the simulated
+            # list, since we continue to simulate this qubit if we did so before.
+            self.virtQubits.remove(qubit)
+        finally:
+            self._release_global_lock()
+
+        return newNum
+
+    @inlineCallbacks
+    def remote_transfer_qubit(self, simQubitNum, targetName):
+        """
+        Transfer the qubit to the destination node if we are the simulating node. The reason why we cannot
+        do this directly is that Twisted PB does not allow objects to be passed between connecting nodes.
+        Only between the creator of the object and its immediate connections.
+
+        Arguments
+        simQubitNum	simulated qubit number to be sent
+        targetName	target node to place qubit at (host object)
+        """
+        self._logger.debug(f"Request to transfer qubit to {targetName}.")
+
+        # Convert the number into the right local object
+        simQubit = self._q_num_to_obj(simQubitNum)
+
+        # Lookup host id of node
+        if not (targetName in self.config.hostDict):
+            raise virtNetError(
+                f"Trying to get conncetion to virtual node {targetName}, but this is not in configuration file"
+            )
+        remoteNode = yield self.get_connection(targetName)
+
+        # Check if we are both the destination node and simulating node
+        if self.myID.name == targetName:
+            newNum = yield remoteNode.root.remote_add_qubit(self.myID.name, simQubit)
+        else:
+            try:
+                newNum = yield call_method(remoteNode.root, "add_qubit", self.myID.name, simQubit)
+            except RemoteError as remote_err:
+                self.reraise_remote_error(remote_err)
+
+        return newNum
+
+    @inlineCallbacks
+    def remote_add_qubit(self, name, simQubit):
+        """
+        Add a qubit to the local virtual node.
+
+        Arguments
+        name		name of the node simulating this qubit
+        simQubit 	simulated qubit reference in the backend we're adding
+        """
+
+        self._logger.debug(f"Request to add qubit from {name}.")
+
+        # Get the details of the remote node
+        if not (name in self.config.hostDict):
+            raise virtNetError(
+                f"Trying to get conncetion to virtual node {name}, but this is not in configuration file"
+            )
+        nb = yield self.get_connection(name)
+
+        # Get a lock to make sure IDs are assigned correctly
+        yield self._get_global_lock()
+
+        try:
+            if len(self.virtQubits) >= self.maxQubits:
+                raise noQubitError("Max virtual qubits reached")
+
+            # Generate a new virtual qubit object for the qubit now at this node
+            newNum = self.get_virtual_id()
+            newQubit = virtualQubit(self.myID, nb, simQubit, newNum)
+
+            # Add to local list
+            self.virtQubits.append(newQubit)
+        finally:
+            self._release_global_lock()
+
+        return newNum
+
+    def remote_get_virtual_ref(self, num):
+        """
+        Return a virual qubit object for the given number.
+
+        Arguments
+        num		number of the virtual qubit
+        """
+
+        for q in self.virtQubits:
+            if q.num == num:
+                return q
+
+        return None
+
+    @inlineCallbacks
+    def remote_remove_sim_qubit_num(self, delNum):
+        """
+        Removes the simulated qubit delQubit from the node and also from the underlying engine. Relies on this qubit
+        having been locked.
+
+        Arguments
+        delNum		simID of the simulated qubit to delete
+        """
+
+        yield self._remove_sim_qubit(self._q_num_to_obj(delNum))
+
+    @inlineCallbacks
+    def _remove_sim_qubit(self, delQubit):
+        """
+        Removes the simulated qubit object.
+
+        Arguments
+        delQubit	simulated qubit object to delete
+        """
+        # Caution: Only qubits simulated at this node can be removed
+        if delQubit not in self.simQubits:
+            self._logger.error("Attempt to delete qubit not simulated at this node.")
+            raise quantumError("Cannot delete qubits we don't simulate.")
+
+        #
+        delNum = delQubit.num
+        delRegister = delQubit.register
+
+        # Should already be locked
+        assert self._lock.locked, "Virtual node is not locked"
+
+        try:
+            # Lock all relevant qubits first
+            for q in self.simQubits:
+                if q.register == delRegister:
+                    if q.num != delNum:  # Don't lock the delQubit since it should already be locked
+                        yield q.lock()
+                    else:
+                        assert q.isLocked(), "Qubit should be locked but isn't"
+
+            # First we remove the physical qubit from the register
+            delRegister.remove_qubit(delNum)
+
+            # Check if this was the last qubit
+            if delRegister.activeQubits == 0:
+                self.remote_delete_register(delRegister)
+            else:
+                # When removing a qubit, we need to update the positions of the qubits in
+                # the underlying physical register
+                # in all relevant qubit objects.
+                for q in self.simQubits:
+                    # If they are in the same engine, and update is required
+                    if q.register == delRegister:
+                        if q.num > delNum:
+                            q.num = q.num - 1
+
+            # Remove the qubit form the list of simulated qubits
+            self._logger.debug(f"removing qubit {delQubit.simNum} from {self.simQubits}")
+            self.simQubits.remove(delQubit)
+
+        finally:
+            # Release all relevant qubits again
+            for q in self.simQubits:
+                if q.register == delRegister:
+                    q.unlock()
+
+    def remote_merge_regs(self, num1, num2):
+        """
+        Merges the two local quantum registers. Note that these register may simulate virtual qubits across different
+        network nodes. This will ignore maxQubits and simply create one large register allowing twice maxQubits qubits.
+
+        Arguments
+        num1 		number of the first qubit
+        num2		number of the second qubit
+        """
+
+        # Lookup the qubit objects corresponding to these numbers
+        for q in self.simQubits:
+            if q.simNum == num1:
+                q1 = q
+            elif q.simNum == num2:
+                q2 = q
+
+        self.local_merge_regs(q1, q2)
+
+    def local_merge_regs(self, qubit1, qubit2):
+        """
+        Merges the two local quantum registers. Note that these register may simulate virtual qubits across different
+        network nodes. This will ignore maxQubits and simply create one large register allowing twice maxQubits qubits.
+
+        Arguments
+        qubit1		qubit1 in reg1, called from remote having access to only qubits
+        qubit2		qubit2 in reg2
+        """
+        self._logger.debug(
+            f"Request to merge local register for qubits simNum {qubit1.simNum} and simNum {qubit2.simNum}."
+        )
+
+        # This should only be called if locks are acquired
+        assert qubit1._lock.locked, "Qubit should be locked but isn't"
+        assert qubit2._lock.locked, "Qubit should be locked but isn't"
+        assert self._lock.locked, "No global lock present"
+
+        self._logger.debug("Request to merge LOCKS PRESENT")
+
+        reg1 = qubit1.register
+        reg2 = qubit2.register
+
+        # Check if there's anything to do at all
+        if reg1 == reg2:
+            self._logger.debug("not required")
+            return
+
+        self._logger.debug("need merge")
+
+        # Allow reg 1 to absorb reg 2
+        reg1.maxQubits = reg1.maxQubits + reg2.activeQubits
+
+        # For relabelling qubit numbers get the offset
+        offset = reg1.activeQubits
+
+        # Add reg2 to reg1
+        reg1.absorb(reg2)
+
+        # Update the simulated qubit numbering and register
+        for i, q in enumerate(self.simQubits):
+            if q.register == reg2:
+                self._logger.debug(f"Updating register {q.num} to {q.num + offset}.")
+                q.register = reg1
+                q.num = q.num + offset
+
+        self.remote_delete_register(reg2)
+
+    @inlineCallbacks
+    def remote_merge_from(self, simNodeName, simQubitNum, localReg):
+        """
+        Bring a remote register to this node.
+
+        Arguments
+        simNodeName	name of the node who simulates right now
+        simQubitNum	simulation number of qubit whose register we will merge
+        localReg	local register to merge with
+        """
+
+        self._logger.debug(f"Merging from {simNodeName}")
+
+        # This should only be called if lock is acquired
+        assert self._lock.locked, f"No global lock present for node {self.myID.name}"
+
+        self._logger.debug(f"Merging from {simNodeName} LOCKS PRESENT")
+
+        # Lookup the local connection for this simulating node
+        if not (simNodeName in self.config.hostDict):
+            raise virtNetError(
+                f"Trying to get conncetion to virtual node {simNodeName}, but this is not in configuration file"
+            )
+        simNode = yield self.get_connection(simNodeName)
+
+        # Fetch the details of the remote register and qubit, and remove sim qubits at node
+        try:
+            (R, I, activeQ, oldRegNum, oldQubitNum) = yield call_method(simNode.root, "get_register_del", simQubitNum)
+        except RemoteError as remote_err:
+            self.reraise_remote_error(remote_err)
+
+        # Get numbering offset from previous register: append at end
+        offset = localReg.activeQubits
+
+        # Allow localReg to absorb the remote register
+        localReg.maxQubits = localReg.maxQubits + activeQ
+        localReg.absorb_parts(R, I, activeQ)
+
+        # Collect mappings between numbers and objects for updating the virtual qubits
+        newD = {}
+
+        # Make new qubit objects
+        for k in range(activeQ):
+            simNum = self.get_sim_id()
+            newQubit = simulatedQubit(self.myID, localReg, simNum, offset + k)
+            # Lock the qubit directly until merge is finished
+            yield newQubit.lock()
+            self.simQubits.append(newQubit)
+            newD[k] = newQubit
+
+        # Issue an update call to all nodes to update their virtual qubits if necessary
+        # for name in self.conn:
+        for name in self.config.hostDict:
+            if name != self.myID.name:
+                nb = yield self.get_connection(name)
+                try:
+                    yield call_method(nb.root, "update_virtual_merge", self.myID.name, simNodeName, oldRegNum, newD)
+                except RemoteError as remote_err:
+                    self.reraise_remote_error(remote_err)
+
+        # Locally, we might also already have virtual qubits which were in the remote simulated
+        # register. Update them as well
+        self._logger.debug("Updating local virtual qubits.")
+        yield self.remote_update_virtual_merge(self.myID.name, simNodeName, oldRegNum, newD)
+
+        # Return the qubit object corresponding to the new physical qubit
+        return newD[oldQubitNum]
+
+    @inlineCallbacks
+    def remote_update_virtual_merge(self, newSimNodeName, oldSimNodeName, oldRegNum, newD):
+        """
+        Update the virtual qubits to the new simulating node, if applicable. This is extremely
+        inefficient due to not keeping register information in virtualQubit.
+
+        Arguments
+        newSimNodeName	new node simulating this qubit
+        oldSimNodeName	old node simulating the qubit
+        oldReg		old register
+        newD		dictionary mapping qubit numbers to qubit objects at the new simulating node
+        """
+
+        self._logger.debug("Request to update local virtual qubits.")
+
+        # If this is a third node (not involved in the two qubit gate, but carrying virtual qubits
+        # which were in the simulated register), then they will now be updated. We remark that this function
+        # can only be called from the _simulating node_ now handing over simulation to someone else. Both the simulating
+        # node and the new simulating node are globally locked so there should be no conflicts here in updating:
+        # a third node that may wish to do a 2 qubit gate between the qubits to be updated needs to wait.
+
+        # Lookup the local connections for the given node names
+        if not (newSimNodeName in self.config.hostDict):
+            raise virtNetError(
+                f"Trying to get conncetion to virtual node {newSimNodeName}, but this is not in configuration file"
+            )
+        if not (oldSimNodeName in self.config.hostDict):
+            raise virtNetError(
+                f"Trying to get conncetion to virtual node {oldSimNodeName}, but this is not in configuration file"
+            )
+        newSimNode = yield self.get_connection(newSimNodeName)
+        oldSimNode = yield self.get_connection(oldSimNodeName)
+
+        for q in self.virtQubits:
+            if q.virtNode == q.simNode and q.simNode == oldSimNode:
+                self._logger.debug("Simulating node update.")
+                # We previously simulated this qubit ourselves
+                givenReg = q.simQubit.register.num
+                givenNum = q.simQubit.num
+            elif q.simNode == oldSimNode:
+                self._logger.debug("Previously remote simulator node update.")
+                # We had the virtual qubit but it was simulated elsewhere
+                try:
+                    (givenNum, givenReg) = yield call_method(q.simQubit, "get_numbers")
+                except RemoteError as remote_err:
+                    self.reraise_remote_error(remote_err)
+
+            # Check if this qubit needs updating
+            if q.simNode == oldSimNode and givenReg == oldRegNum:
+                self._logger.debug(
+                    f"Updating virtual qubit {q.num}, previously {oldSimNode.name} now {newSimNode.name}"
+                )
+                q.simNode = newSimNode
+                q.simQubit = newD[givenNum]
+
+    @inlineCallbacks
+    def remote_get_register_RI(self, qubit):
+        """
+        Return the real and imaginary part of the (possibly remote) simulated register which
+        contains this virtual qubit.
+        """
+        if isinstance(qubit, virtualQubit):
+            realM, imagM = yield qubit.remote_get_register_RI()
+        else:
+            realM, imagM = yield call_method(qubit, "get_register_RI")
+        return realM, imagM
+
+    def remote_get_register(self, qubit):
+        """
+        Return the value of of a locally simulated register which contains this virtual qubit.
+        """
+
+        (realM, imagM) = qubit.simQubit.register.get_register_RI()
+        activeQ = qubit.simQubit.register.activeQubits
+        oldRegNum = qubit.simQubit.register.num
+        oldQubitNum = qubit.simQubit.num
+
+        return (realM, imagM, activeQ, oldRegNum, oldQubitNum)
+
+    def remote_get_register_del(self, qubitNum):
+        """
+        Return the value of of a locally simulated register, and remove the simulated qubits from this node.
+
+        Caution: virtual qubits not updated.
+        """
+
+        assert self._lock.locked, "Virtual node is not locked"
+
+        # Locate the qubit object for this ID
+        gotQ = None
+        for q in self.simQubits:
+            if q.simNum == qubitNum:
+                gotQ = q
+
+        # If nothing is found, return
+        if gotQ is None:
+            self._logger.debug(f"No simulated qubit with ID {qubitNum}.")
+            return ([], [], 0, 0, 0)
+
+        (realM, imagM) = gotQ.register.get_register_RI()
+        activeQ = gotQ.register.activeQubits
+        oldRegNum = gotQ.register.num
+        oldQubitNum = gotQ.num
+        delRegister = gotQ.register
+
+        # Remove all simulated qubits and the register
+        # Need to iterate of simQubits in reverse, otherwise wrong elements are removed
+        self._logger.debug(f"removing all sim qubits in reg {oldRegNum}")
+        for q in reversed(self.simQubits):
+            if q.register.num == oldRegNum:
+                self.simQubits.remove(q)
+
+        self.remote_delete_register(delRegister)
+
+        return (realM, imagM, activeQ, oldRegNum, oldQubitNum)
+
+    @inlineCallbacks
+    def remote_get_multiple_qubits(self, qList):
+        """
+        Return the state of multiple qubits virtually located at this node. This will fail if the qubits
+        are not in the same register or thus also simulating node.
+
+        Arguments
+        qList		list of virtual qubits of which to retrieve the state
+        """
+
+        localSim = False
+        remoteSim = False
+
+        # Check whether we are the simulating node.
+        for q in qList:
+            if q.simNode == q.virtNode:
+                localSim = True
+            elif q.simNode != q.virtNode:
+                remoteSim = True
+
+        # Check whether two nodes are the simulator, for now we simply fail in this case
+        if localSim and remoteSim:
+            self._logger.error("Getting multiple qubits from multiple simulators is currently not supported.")
+            return ([0], [0])
+
+        if localSim:
+            # Qubits are local, simply retrieve from the simulation
+            nums = []
+            for q in qList:
+                nums.append(q.simQubit.simNum)
+            self._logger.debug(f"Looking for simulated qubits. {nums}")
+            (R, I) = self.remote_get_state(nums)
+        else:
+            # Qubits are located elsewhere.
+            nums = []
+            for q in qList:
+                try:
+                    (num, name) = yield call_method(q.simQubit, "get_details")
+                except RemoteError as remote_err:
+                    self.reraise_remote_error(remote_err)
+                nums.append(num)
+            try:
+                (R, I) = yield call_method(qList[0].simNode.root, "get_state", nums)
+            except RemoteError as remote_err:
+                self.reraise_remote_error(remote_err)
+
+        return (R, I)
+
+    def remote_get_state(self, simNumList):
+        """
+        Return the state of multiple qubits corresponding to the IDs in simNumList.
+        """
+
+        # Convert simulation numbers to register and real number in register
+        traceList = []
+        foundOne = False
+        prev = None
+        for n in simNumList:
+            for q in self.simQubits:
+                if q.simNum == n:
+                    if foundOne is True and prev.register != q.register:
+                        self._logger.error("Getting multiple qubits from different registers not supported.")
+                        return ([], [])
+                    prev = q
+                    foundOne = True
+                    traceList.append(q.num)
+        if not foundOne:
+            self._logger.error("No such qubits found.")
+            return
+
+        traceList.sort()
+        (realM, imagM) = prev.register.get_qubits_RI(traceList)
+
+        return (realM, imagM)
+
+    def remote_sim_qubit_num_in_same_reg(self, sim_qubit_num1, sim_qubit_num2):
+        """Checks if two qubits are in the same register"""
+        sim_qubit1 = self._q_num_to_obj(sim_qubit_num1)
+        sim_qubit2 = self._q_num_to_obj(sim_qubit_num2)
+        assert sim_qubit1 is not None, "Sim num {sim_qubit_num1} not in this node"
+        assert sim_qubit2 is not None, "Sim num {sim_qubit_num2} not in this node"
+        return sim_qubit1.register == sim_qubit2.register
+
+
+#######
+#
+# virtualQubit - a qubit that is virtually carried at this node. It may be simulated elsewhere
+# but in the simulation it is located at this particular virtualNode.
+#
+# This is given out as a reference object to users who ask for a "local" qubit
+#
+#
+
+
+class virtualQubit(pb.Referenceable):
+    def __init__(self, virtNode, simNode, simQubit, num):
+        """
+        Creates a virtual qubit object simulated in the specified simulation register backend
+
+        Arguments
+        virtNode	node where this qubit is virtually located
+        simNode		node where this qubit is simulated
+        simQubit	reference to the underlying qubit object (may be remote)
+        num		number ID among the virtual qubits
+        """
+        self._logger = get_netqasm_logger(f"{self.__class__.__name__}({virtNode.name}, {num})")
+
+        # Node where this qubit is virtually located
+        self.virtNode = virtNode
+
+        # Node where this qubit is being simulated
+        self.simNode = simNode
+
+        # Underlying qubit object for simulation
+        self.simQubit = simQubit
+
+        # Qubit active at this node. The client may retain a reference to this object,
+        # which will cause python to keep it, while it has actually be transferred to
+        # another node. We do not allow operations on a qubit that is now virtually elsewhere.
+        self.active = 1
+
+        # Our number at this virtual node. Note that this has nothing to do
+        # with the number of the qubits in the register
+        self.num = num
+
+    @inlineCallbacks
+    def _single_gate(self, name, *args):
+        """
+        Apply the single gate function to the underlying qubit. This is an internal method used by all the other
+        single qubit calls, which will perform the correct local or remote method calls as applicable after
+        performing the necessary locking.
+
+        Arguments
+        name		name of the method corresponding to the name. For example: name = apply_X
+        param		parameters for gates such as rotations (axis,angle)
+        """
+        self._logger.debug(f"applying gate {name} to virtual qubit {self.num}")
+        if self.active != 1:
+            self._logger.error("Attempt to manipulate qubits no longer at this node.")
+            return False
+
+        locked_node = yield self._lock_simulating_node()
+        yield call_method(self.simQubit, "lock")
+        try:
+            active = yield call_method(self.simQubit, "isActive")
+            if active:
+                yield call_method(self.simQubit, name, *args)
+        finally:
+            yield call_method(self.simQubit, "unlock")
+            assert locked_node == self.simNode, "Something went wrong"
+            yield call_method(self.simNode.root, "release_global_lock")
+
+    @inlineCallbacks
+    def remote_apply_X(self):
+        """
+        Apply X gate to itself by passing it onto the underlying register.
+        """
+        yield self._single_gate("apply_X")
+
+    @inlineCallbacks
+    def remote_apply_Y(self):
+        """
+        Apply Y gate.
+        """
+        yield self._single_gate("apply_Y")
+
+    @inlineCallbacks
+    def remote_apply_Z(self):
+        """
+        Apply Z gate.
+        """
+        yield self._single_gate("apply_Z")
+
+    @inlineCallbacks
+    def remote_apply_H(self):
+        """
+        Apply H gate.
+        """
+        yield self._single_gate("apply_H")
+
+    @inlineCallbacks
+    def remote_apply_K(self):
+        """
+        Apply K gate - taking computational basis to Y eigenbasis.
+        """
+        yield self._single_gate("apply_K")
+
+    @inlineCallbacks
+    def remote_apply_T(self):
+        """
+        Apply T gate.
+        """
+        yield self._single_gate("apply_T")
+
+    @inlineCallbacks
+    def remote_apply_rotation(self, n, a):
+        """
+        Apply rotation around axis n with angle a.
+        Arguments:
+        n	A tuple of three numbers specifying the rotation axis, e.g n=(1,0,0)
+        a	The rotation angle in radians.
+        """
+        yield self._single_gate("apply_rotation", n, a)
+
+    @inlineCallbacks
+    def remote_measure(self, inplace=False):
+        """
+        Measure the qubit in the standard basis. If inplace=False, this does delete the qubit from the simulation.
+
+        Returns the measurement outcome.
+        """
+
+        if self.active != 1:
+            self._logger.error("Attempt to manipulate qubits no longer at this node.")
+            return
+
+        self._logger.debug("measuring virtual qubit {self.num}")
+        locked_node = yield self._lock_simulating_node()
+        yield call_method(self.simQubit, "lock")
+
+        try:
+            active = yield call_method(self.simQubit, "isActive")
+            if active:
+                outcome = yield call_method(self.simQubit, "measure_inplace")
+                if not inplace:
+                    num = yield call_method(self.simQubit, "get_sim_number")
+                    yield call_method(self.simNode.root, "remove_sim_qubit_num", num)
+
+                    # Delete from virtual qubits
+                    self.virtNode.root.virtQubits.remove(self)
+        finally:
+            yield call_method(self.simQubit, "unlock")
+            assert locked_node == self.simNode, "Something went wrong"
+            yield call_method(self.simNode.root, "release_global_lock")
+
+        return outcome
+
+    @inlineCallbacks
+    def _lock_nodes(self, target):
+        """
+        Wrapper to acquire the global register lock on nodes that involve the qubits, and local node.
+        This can in fact be everyting from a single node if both qubits are simulated locally or three nodes
+        if both qubits are simulated remotely.
+
+        Since deadlocks can occur a random timeout is used when acquiring the locks and the acquisition of the locks
+        will be tried again until success.
+
+        Furthermore, when waiting for locks of a simulating node, this might change in the meantime, so we check
+        that indeed the simulating nodes are the same after acquiring the locks. If not, we try again until success.
+
+        Parameters
+        ----------
+        target : :class:`~.virtualQubit`
+            virtual qubit of the target qubit
+
+        Returns
+        -------
+        list: The nodes that have been locked so that they can be unlocked again by the caller
+        """
+        local_node = self.virtNode
+        control_sim_node = self.simNode
+        target_sim_node = target.simNode
+
+        ds = {}
+        # Get deferreds for locking all the relevant nodes once (which might overlap)
+        for node in set([local_node, control_sim_node, target_sim_node]):
+            ds[node] = call_method(node.root, "get_global_lock")
+        self._logger.debug(f"For merging gonna lock the nodes {list(ds.keys())}")
+        # Deferred for all of the locks
+        d_lock = DeferredList(list(ds.values()), fireOnOneCallback=False, consumeErrors=True)
+        # Since deadlock might occur also schedule a random timeout
+        d_timeout = deferLater(reactor, random.uniform(1, 4), lambda: None)
+        # Yield on either the locks or the timeout
+        yield DeferredList([d_lock, d_timeout], fireOnOneCallback=True, fireOnOneErrback=True, consumeErrors=True)
+        if d_timeout.called:
+            # Timeout so cancel any locks and revert any that were acquired
+            d_lock.cancel()
+            self._logger.debug("Trying to get lock timedout, will try again")
+            for node, d in ds.items():
+                if d.called:
+                    yield call_method(node.root, "release_global_lock")
+            # Try again
+            locked_nodes = yield self._lock_nodes(target=target)
+            return locked_nodes
+        else:
+            assert d_lock.called, "Neither timeout or locked was called"
+            # Check that the simulating nodes have not changed in the meantime
+            if control_sim_node != self.simNode or target_sim_node != target.simNode:
+                self._logger.debug("at least on simulating node changed, releasing and trying again")
+                for node, d in ds.items():
+                    assert d.called, "Something went wrong, locked deferred not called"
+                    yield call_method(node.root, "release_global_lock")
+                # Try again
+                locked_nodes = yield self._lock_nodes(target=target)
+                return locked_nodes
+            else:
+                return list(ds.keys())
+
+    @inlineCallbacks
+    def _lock_inreg(self, qubit):
+        """
+        Lock all qubits in the same register as the virtual qubit qubit.
+        """
+
+        try:
+            if qubit.simNode == qubit.virtNode:
+                yield qubit.simNode.root._lock_reg_qubits(qubit.simQubit)
+            else:
+                simNum = yield call_method(qubit.simQubit, "get_sim_number")
+                yield call_method(qubit.simNode.root, "lock_reg_qubits", simNum)
+        except RemoteError as remote_err:
+            self.virtNode.root.reraise_remote_error(remote_err)
+
+    @inlineCallbacks
+    def _unlock_inreg(self, qubit):
+        """
+        Lock all qubits in the same register as the virtual qubit qubit.
+        """
+
+        try:
+            if qubit.simNode == qubit.virtNode:
+                yield qubit.simNode.root._unlock_reg_qubits(qubit.simQubit)
+            else:
+                simNum = yield call_method(qubit.simQubit, "get_sim_number")
+                yield call_method(qubit.simNode.root, "unlock_reg_qubits", simNum)
+        except RemoteError as remote_err:
+            self.virtNode.root.reraise_remote_error(remote_err)
+
+    @inlineCallbacks
+    def remote_cnot_onto(self, target):
+        """
+        Performs a CNOT operation with this qubit as control, and the other qubit as target.
+
+        Arguments
+        target		the virtual qubit to use as the target of the CNOT
+        """
+
+        yield self._two_qubit_gate(target, "cnot_onto")
+
+    @inlineCallbacks
+    def remote_cphase_onto(self, target):
+        """
+        Performs a CPHASE operation with this qubit as control, and the other qubit as target.
+
+        Arguments
+        target		the virtual qubit to use as the target of the CPHASE
+        """
+
+        yield self._two_qubit_gate(target, "cphase_onto")
+
+    @inlineCallbacks
+    def _two_qubit_gate(self, target, name):
+        """
+        Perform a two qubit gate including all the required locking.
+
+        Arguments
+        target		second virtual qubit (beyond self which is the first)
+        name		name of the gate to perform
+        """
+
+        if self.active != 1 or target.active != 1:
+            self._logger.error("Attempt to manipulate qubits no longer at this node.")
+            return
+
+        localName = "".join(["remote_", name])
+        self._logger.debug(f"Doing 2 qubit gate name {name} and local call {localName}")
+
+        # First lock the relevant nodes
+        locked_nodes = yield self._lock_nodes(target=target)
+
+        # We have now acquired the relevant global node locks. If more than one qubit is locked, all code
+        # will first acquire the global lock, so this should be safe from deadlocks now, so we will not timeout
+        # Lock the control qubits register
+        # If the target is in the same register we don't want to lock again so first check
+        # what case we are in
+        yield self._lock_inreg(self)
+        # if self.simQubit.register != target.simQubit.register:
+
+        # Todo a 2 qubit gate, both qubits must be in the same simulated register. We will merge
+        # registers if this is not already the case.
+        try:
+            if self.simNode == target.simNode:
+                # Both qubits are simulated at the same node
+                self._logger.debug("both qubits same node for two qubit gate")
+
+                if self.simNode == self.virtNode:
+                    self._logger.debug("both qubits locally for two qubit gate")
+                    # Both qubits are both locally simulated, check whether they are in the same register
+
+                    if self.simQubit.register == target.simQubit.register:
+                        # They are even in the same register, just do the gate
+                        getattr(self.simQubit, localName)(target.simQubit.num)
+                    else:
+                        yield self._lock_inreg(target)
+                        self._logger.debug("2qubit command demands register merge.")
+                        # Both are local but not in the same register
+                        yield self.simNode.root.local_merge_regs(self.simQubit, target.simQubit)
+
+                        # After the merge, just do the gate
+                        getattr(self.simQubit, localName)(target.simQubit.num)
+                else:
+                    # Both are remotely simulated
+                    self._logger.debug("2qubit command demands remote register merge.")
+
+                    # Fetch the details of the two simulated qubits from remote
+                    (fNum, fNode) = yield call_method(self.simQubit, "get_details")
+                    (tNum, tNode) = yield call_method(target.simQubit, "get_details")
+
+                    same_reg = yield call_method(self.simNode.root, "sim_qubit_num_in_same_reg", fNum, tNum)
+                    if not same_reg:
+                        # Not same register so also lock target register
+                        yield self._lock_inreg(target)
+
+                    # Sanity check: we really have the right simulating node
+                    if fNode != self.simNode.name or tNode != target.simNode.name:
+                        self._logger.error("Inconsistent simulation. Cannot merge.")
+                        raise quantumError("Inconsistent simulation")
+
+                    # Merge the remote register according to the simulation IDs of the qubits
+                    self._logger.debug("merging remote same node")
+                    yield call_method(self.simNode.root, "merge_regs", fNum, tNum)
+
+                    # Get the number of the target in the new register
+                    targetNum = yield call_method(target.simQubit, "get_number")
+
+                    # Execute the 2 qubit gate
+                    yield call_method(self.simQubit, name, targetNum)
+                    self._logger.debug(f"Remote 2qubit command to {target.simNode.name}.")
+            else:
+                # They are simulated at two different nodes
+                if self.simNode == self.virtNode:
+                    self._logger.debug("control is simulated locally")
+
+                    # We are the locally simulating node of the first qubit, merge all to us
+                    self._logger.debug(
+                        "2qubit command demands merge from remote target sim %s to us.",
+                        target.simNode.name,
+                    )
+                    (fNum, fNode) = yield call_method(target.simQubit, "get_details")
+                    if fNode != target.simNode.name:
+                        self._logger.error("Inconsistent simulation. Cannot merge.")
+                        raise quantumError("Inconsistent simulation.")
+                    target.simQubit = yield self.simNode.root.remote_merge_from(
+                        target.simNode.name, fNum, self.simQubit.register
+                    )
+
+                    # Get the number of the target in the new register
+                    targetNum = target.simQubit.num
+
+                    # Execute the 2 qubit gate
+                    getattr(self.simQubit, localName)(targetNum)
+
+                elif target.simNode == target.virtNode:
+                    # We are the locally simulating node of the target qubit, merge all to us
+                    self._logger.debug(
+                        "2qubit command demands merge from remote sim %s to us.",
+                        self.simNode.name,
+                    )
+                    (fNum, fNode) = yield call_method(self.simQubit, "get_details")
+                    if fNode != self.simNode.name:
+                        self._logger.error("Inconsistent simulation. Cannot merge.")
+                        raise quantumError("Inconsistent simulation.")
+                    self.simQubit = yield target.simNode.root.remote_merge_from(
+                        self.simNode.name, fNum, target.simQubit.register
+                    )
+
+                    # Get the number of the target in the new register
+                    targetNum = target.simQubit.num
+
+                    # Execute the 2 qubit gate
+                    getattr(self.simQubit, localName)(targetNum)
+
+                else:
+                    self._logger.debug("both are remote")
+                    # Both qubits are remotely simulated - we will pull both registers to become one local register
+                    self._logger.debug(
+                        "2qubit command demands total remote merge from %s and %s.",
+                        target.simNode.name,
+                        self.simNode.name,
+                    )
+
+                    # Create a new local register
+                    newLocalReg = self.virtNode.root.remote_add_register()
+
+                    # Fetch the detail of the two registers from remote
+                    (fNum, fNode) = yield call_method(self.simQubit, "get_details")
+                    if fNode != self.simNode.name:
+                        self._logger.error("Inconsistent simulation. Cannot merge.")
+                        raise quantumError("Inconsistent simulation.")
+                    (tNum, tNode) = yield call_method(target.simQubit, "get_details")
+                    if tNode != target.simNode.name:
+                        self._logger.error("Inconsistent simulation. Cannot merge.")
+                        raise quantumError("Inconsistent simulation.")
+
+                    # Pull the remote registers to this node
+                    self.simQubit = yield self.virtNode.root.remote_merge_from(
+                        self.simNode.name,
+                        fNum,
+                        newLocalReg,
+                    )
+                    target.simQubit = yield target.virtNode.root.remote_merge_from(
+                        target.simNode.name, tNum, newLocalReg
+                    )
+                    # Get the number of the target in the new register
+                    targetNum = target.simQubit.num
+
+                    # Finally, execute the two qubit gate
+                    getattr(self.simQubit, localName)(targetNum)
+        except RemoteError as remote_err:
+            self.virtNode.root.reraise_remote_error(remote_err)
+        finally:
+            # Release the locks in the register of the control (which now contains also the others)
+            yield self._unlock_inreg(self)
+            for node in locked_nodes:
+                yield call_method(node.root, "release_global_lock")
+
+    @inlineCallbacks
+    def remote_get_number(self):
+        """
+        Returns the number of this qubit in whatever local register it is in. Not useful for the client,
+        but convenient for debugging.
+        """
+
+        if self.active != 1:
+            self._logger.error("Attempt to manipulate qubits no longer at this node.")
+
+        if self.virtNode == self.simNode:
+            num = self.simQubit.num
+        else:
+            try:
+                num = yield call_method(self.simQubit, "get_number")
+            except ConnectionError:
+                self._logger.error("cannot get qubit number.")
+                return
+
+        return num
+
+    def remote_get_virt_num(self):
+        """
+        Returns the number of the virtual qubit.
+        """
+        return self.num
+
+    def remote_get_virtNode(self):
+        """
+        Returns the virtNode of this virtual qubit
+        """
+        return self.virtNode.name
+
+    def remote_get_simNode(self):
+        """
+        Returns the simNode of this virtual qubit
+        """
+        return self.simNode.name
+
+    @inlineCallbacks
+    def remote_get_qubit(self):
+        """
+        Returns the state of this qubit in real and imaginary parts separated. This is required
+        single Twisted cannot natively transfer complex valued objects.
+        """
+
+        if self.active != 1:
+            self._logger.error("Attempt to manipulate qubits no longer at this node.")
+
+        if self.virtNode == self.simNode:
+            (R, I) = self.simQubit.remote_get_qubit()
+        else:
+            try:
+                try:
+                    (R, I) = yield call_method(self.simQubit, "get_qubit")
+                except RemoteError as remote_err:
+                    self.virtNode.root.reraise_remote_error(remote_err)
+            except ConnectionError:
+                self._logger.error("cannot get qubit number.")
+
+        return (R, I)
+
+    @inlineCallbacks
+    def remote_get_register_RI(self):
+        if self.simNode == self.virtNode:
+            realM, imagM = self.simQubit.register.get_register_RI()
+        else:
+            realM, imagM = yield call_method(self.simQubit, "get_register_RI")
+        return realM, imagM
+
+    @inlineCallbacks
+    def _lock_simulating_node(self, exclude=None):
+        """Aquires a global lock on the simulating node
+
+        Since the simulating node can change while trying to acquire the lock, we
+        check if this happened and if so, try again.
+
+        Parameters
+        ----------
+        exclude : list
+            List of hosts which to exclude since they might already have been locked
+        """
+        if exclude is None:
+            exclude = []
+        curr_sim_node = self.simNode
+        if curr_sim_node in exclude:
+            return
+        yield call_method(curr_sim_node.root, "get_global_lock")
+        # Now that we have a look, check that the sim node hasn't change before the looked was acquired
+        if curr_sim_node != self.simNode:
+            # Release the lock since this is not anymore the simulating one and try again
+            self._logger.debug("simulating node changed, releasing and trying again")
+            yield call_method(curr_sim_node.root, "release_global_lock")
+            locked_node = yield self._lock_simulating_node()
+            return locked_node
+        else:
+            self._logger.debug("got lock of simulating node {curr_sim_node}")
+            return curr_sim_node
+
+
+############################################
+#
+# Keeping track of received qubits and outcomes for NetQASM
+
+
+class QubitNetQASM:
+    def __init__(self, fromName, toName, from_epr_socket_id, to_epr_socket_id, new_virt_num=None, rawEntInfo=None):
+        self.fromName = fromName
+        self.toName = toName
+        self.from_epr_socket_id = from_epr_socket_id
+        self.to_epr_socket_id = to_epr_socket_id
+        self.virt_num = new_virt_num
+        self.rawEntInfo = rawEntInfo


Test patch:
diff --git a/simulaqron/tests/performance/backend/ghzstate/plot_ghz_state_runtime.py b/simulaqron/tests/performance/backend/ghzstate/plot_ghz_state_runtime.py
deleted file mode 100644
index 148155b..0000000
--- a/simulaqron/tests/performance/backend/ghzstate/plot_ghz_state_runtime.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from matplotlib import pyplot as plt
-
-qutip_sizes = []
-qutip_times = []
-projectq_sizes = []
-projectq_times = []
-stabilizer_sizes = []
-stabilizer_times = []
-
-with open("run_time_ghz_state_qutip.txt", "r") as f:
-    for line in f.readlines():
-        words = line.split(" ")
-        n = int(words[0].strip())
-        t = float(words[1].strip())
-        qutip_sizes.append(n)
-        qutip_times.append(t)
-
-with open("run_time_ghz_state_projectq.txt", "r") as f:
-    for line in f.readlines():
-        words = line.split(" ")
-        n = int(words[0].strip())
-        t = float(words[1].strip())
-        projectq_sizes.append(n)
-        projectq_times.append(t)
-
-with open("run_time_ghz_state_stabilizer.txt", "r") as f:
-    for line in f.readlines():
-        words = line.split(" ")
-        n = int(words[0].strip())
-        t = float(words[1].strip())
-        stabilizer_sizes.append(n)
-        stabilizer_times.append(t)
-
-plt.plot(qutip_sizes, qutip_times)
-plt.plot(projectq_sizes, projectq_times)
-plt.plot(stabilizer_sizes, stabilizer_times)
-plt.xlabel("Size of GHZ state (nr qubits)")
-plt.ylabel("Runtime (s)")
-plt.legend(["Backend: QuTip", "Backend: Project Q", "Backend: Stabilizer"])
-
-plt.savefig("runtime_qutip_vs_projectq_vs_stabilizer.pdf")
diff --git a/simulaqron/tests/performance/backend/ghzstate/run.sh b/simulaqron/tests/performance/backend/ghzstate/run.sh
deleted file mode 100644
index 68e6764..0000000
--- a/simulaqron/tests/performance/backend/ghzstate/run.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-simulaqron stop
-simulaqron reset -f
-simulaqron set backend stabilizer
-simulaqron set max-qubits 1000
-simulaqron set max-registers 1000
-simulaqron start -f
-python3 test_ghz_state.py "$@"
diff --git a/simulaqron/tests/performance/backend/ghzstate/run_time_ghz_state_projectq.txt b/simulaqron/tests/performance/backend/ghzstate/run_time_ghz_state_projectq.txt
deleted file mode 100644
index 92b4947..0000000
--- a/simulaqron/tests/performance/backend/ghzstate/run_time_ghz_state_projectq.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-1 0.004999439999999966
-2 0.01029643800000013
-3 0.015963330000000164
-4 0.021492639999999952
-5 0.027152728999999987
-6 0.032731551000000136
-7 0.0387744430000001
-8 0.0446457979999999
-9 0.05494803300000006
-10 0.06421963799999997
-11 0.07687025000000003
-12 0.08546654500000006
-13 0.09586167700000003
-14 0.10638109600000001
-15 0.11818878700000002
-16 0.12917792000000006
-17 0.1433051999999999
-18 0.16113516799999994
-19 0.1863077409999998
-20 0.2289012920000002
-21 0.32724743599999995
-22 0.49840012999999983
-23 0.8362445270000003
-24 1.5606935779999995
-25 2.6909910869999996
-26 5.241448554
-27 14.497422626999999
diff --git a/simulaqron/tests/performance/backend/ghzstate/run_time_ghz_state_qutip.txt b/simulaqron/tests/performance/backend/ghzstate/run_time_ghz_state_qutip.txt
deleted file mode 100644
index 3e9f762..0000000
--- a/simulaqron/tests/performance/backend/ghzstate/run_time_ghz_state_qutip.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-1 0.006542590000000015
-2 0.011556601999999971
-3 0.018530555000000115
-4 0.02314708600000004
-5 0.03069548099999997
-6 0.03715880999999999
-7 0.042678779999999916
-8 0.050136433999999896
-9 0.06238582800000003
-10 0.092035058
-11 0.18396889399999994
-12 0.5387795790000001
-13 3.278779162
-14 16.37736521
diff --git a/simulaqron/tests/performance/backend/ghzstate/run_time_ghz_state_stabilizer.txt b/simulaqron/tests/performance/backend/ghzstate/run_time_ghz_state_stabilizer.txt
deleted file mode 100644
index e635ac2..0000000
--- a/simulaqron/tests/performance/backend/ghzstate/run_time_ghz_state_stabilizer.txt
+++ /dev/null
@@ -1,100 +0,0 @@
-1 0.003501551000000047
-2 0.006350319999999909
-3 0.010089114999999982
-4 0.012761052999999967
-5 0.0162460499999999
-6 0.019598420000000116
-7 0.023027866999999924
-8 0.028023821000000115
-9 0.030231827000000155
-10 0.033601557999999976
-11 0.03730203600000004
-12 0.04126821599999997
-13 0.04459774299999997
-14 0.04801504199999984
-15 0.05322845700000012
-16 0.055709175000000055
-17 0.05958958400000003
-18 0.06327381100000018
-19 0.07006536700000021
-20 0.07127237800000019
-21 0.075933064
-22 0.07901626400000006
-23 0.08492574300000011
-24 0.08674795300000016
-25 0.09112516300000006
-26 0.09707312000000012
-27 0.10034597400000012
-28 0.10559131400000021
-29 0.10908869100000018
-30 0.1138465179999999
-31 0.11752137699999965
-32 0.121137037
-33 0.1278971590000002
-34 0.1307098299999998
-35 0.1363510149999998
-36 0.1406072789999997
-37 0.17131267900000013
-38 0.15280177199999967
-39 0.15442896599999978
-40 0.15988615000000017
-41 0.1643985490000004
-42 0.16884273400000005
-43 0.17481414600000011
-44 0.1813376799999995
-45 0.18411315100000003
-46 0.1898911649999997
-47 0.19593146299999997
-48 0.1999061839999996
-49 0.20591698900000033
-50 0.2110066890000004
-51 0.2158218449999998
-52 0.22097190100000041
-53 0.22655475699999972
-54 0.2335555040000008
-55 0.235907804
-56 0.24256454100000013
-57 0.24883105900000047
-58 0.2555616510000007
-59 0.2607881499999998
-60 0.26988021800000084
-61 0.2718157320000003
-62 0.2780918410000002
-63 0.2839833029999994
-64 0.28956450999999994
-65 0.29819425099999997
-66 0.30178647199999986
-67 0.3078959439999984
-68 0.31532109299999966
-69 0.3416074100000017
-70 0.32584912299999935
-71 0.33321961799999933
-72 0.33823688200000035
-73 0.3445965519999987
-74 0.35369019499999865
-75 0.3590681559999993
-76 0.3650780919999992
-77 0.39878891100000047
-78 0.37753650500000013
-79 0.3844177559999995
-80 0.39117511599999943
-81 0.3990677369999993
-82 0.4062604020000009
-83 0.41196637699999883
-84 0.4192134509999974
-85 0.4276464649999987
-86 0.4332098749999993
-87 0.4449970830000005
-88 0.448672865999999
-89 0.45854951700000157
-90 0.46435259400000106
-91 0.4716352310000005
-92 0.4793345140000014
-93 0.48830866100000136
-94 0.49445556299999893
-95 0.5117719919999999
-96 0.5109725419999975
-97 0.5179981389999995
-98 0.5256635759999995
-99 0.5616057969999986
-100 0.5612218359999979
diff --git a/simulaqron/tests/performance/backend/ghzstate/runtime_qutip_vs_projectq.pdf b/simulaqron/tests/performance/backend/ghzstate/runtime_qutip_vs_projectq.pdf
deleted file mode 100644
index 6047570..0000000
Binary files a/simulaqron/tests/performance/backend/ghzstate/runtime_qutip_vs_projectq.pdf and /dev/null differ
diff --git a/simulaqron/tests/performance/backend/ghzstate/runtime_qutip_vs_projectq_vs_stabilizer.pdf b/simulaqron/tests/performance/backend/ghzstate/runtime_qutip_vs_projectq_vs_stabilizer.pdf
deleted file mode 100644
index da12dcc..0000000
Binary files a/simulaqron/tests/performance/backend/ghzstate/runtime_qutip_vs_projectq_vs_stabilizer.pdf and /dev/null differ
diff --git a/simulaqron/tests/performance/backend/ghzstate/runtime_qutip_vs_projectq_vs_stabilizer.png b/simulaqron/tests/performance/backend/ghzstate/runtime_qutip_vs_projectq_vs_stabilizer.png
deleted file mode 100644
index 03fcbb8..0000000
Binary files a/simulaqron/tests/performance/backend/ghzstate/runtime_qutip_vs_projectq_vs_stabilizer.png and /dev/null differ
diff --git a/simulaqron/tests/performance/backend/ghzstate/test_ghz_state.py b/simulaqron/tests/performance/backend/ghzstate/test_ghz_state.py
deleted file mode 100644
index 51bcc8d..0000000
--- a/simulaqron/tests/performance/backend/ghzstate/test_ghz_state.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import sys
-
-from cqc.pythonLib import CQCConnection, qubit
-from simulaqron.settings import simulaqron_settings
-
-from timeit import default_timer as timer
-
-with CQCConnection("Alice") as cqc:
-    nmax = int(sys.argv[1])
-    backend = simulaqron_settings.backend
-    times = []
-    sizes = []
-    for n in range(1, nmax + 1):
-        t1 = timer()
-        qubits = [qubit(cqc)]
-        qubits[0].H()
-        for i in range(n - 1):
-            q = qubit(cqc)
-            qubits[0].cnot(q)
-            qubits.append(q)
-        [q.measure() for q in qubits]
-        t2 = timer()
-        times.append(t2 - t1)
-        sizes.append(n)
-        print("Creating GHZ state on {} qubits took {} s using {} as backend".format(n, t2 - t1, backend))
-    with open("run_time_ghz_state_{}.txt".format(backend), "w") as f:
-        for (n, t) in zip(sizes, times):
-            f.write("{} {}\n".format(n, t))
diff --git a/simulaqron/tests/performance/backend/wstate/plot_w_state_runtime.py b/simulaqron/tests/performance/backend/wstate/plot_w_state_runtime.py
deleted file mode 100644
index 9e12d7f..0000000
--- a/simulaqron/tests/performance/backend/wstate/plot_w_state_runtime.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from matplotlib import pyplot as plt
-
-qutip_sizes = []
-qutip_times = []
-projectq_sizes = []
-projectq_times = []
-
-with open("run_time_w_state_qutip.txt", "r") as f:
-    for line in f.readlines():
-        words = line.split(" ")
-        n = int(words[0].strip())
-        t = float(words[1].strip())
-        qutip_sizes.append(n)
-        qutip_times.append(t)
-
-with open("run_time_w_state_projectq.txt", "r") as f:
-    for line in f.readlines():
-        words = line.split(" ")
-        n = int(words[0].strip())
-        t = float(words[1].strip())
-        projectq_sizes.append(n)
-        projectq_times.append(t)
-
-plt.plot(qutip_sizes, qutip_times)
-plt.plot(projectq_sizes, projectq_times)
-plt.xlabel("Size of W state (nr qubits)")
-plt.ylabel("Runtime (s)")
-plt.legend(["Backend: QuTip", "Backend: Project Q"])
-
-plt.savefig("runtime_qutip_vs_projectq.png")
diff --git a/simulaqron/tests/performance/backend/wstate/run_time_w_state_projectq.txt b/simulaqron/tests/performance/backend/wstate/run_time_w_state_projectq.txt
deleted file mode 100644
index 93ce125..0000000
--- a/simulaqron/tests/performance/backend/wstate/run_time_w_state_projectq.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-1 0.004082448000000016
-2 0.022423234999999986
-3 0.04345560600000009
-4 0.07194596299999989
-5 0.10101386400000001
-6 0.13210747599999983
-7 0.162711155
-8 0.1877978549999999
-9 0.21548180800000005
-10 0.2459783550000001
-11 0.2746620689999997
-12 0.3045763670000001
-13 0.3335249979999997
-14 0.3655221169999998
-15 0.395596179
-16 0.42659734999999976
-17 0.4627386970000007
-18 0.5045735790000005
-19 0.5690607490000001
-20 0.7104145069999994
-21 0.8878600510000005
-22 1.2614814259999996
-23 2.0591940100000006
-24 3.6784099260000005
-25 7.085137431
-26 13.805535234999997
diff --git a/simulaqron/tests/performance/backend/wstate/run_time_w_state_qutip.txt b/simulaqron/tests/performance/backend/wstate/run_time_w_state_qutip.txt
deleted file mode 100644
index b4f8896..0000000
--- a/simulaqron/tests/performance/backend/wstate/run_time_w_state_qutip.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-1 0.005666539999999998
-2 0.025758181000000047
-3 0.04627227999999994
-4 0.06339636900000012
-5 0.07906096600000012
-6 0.0993346129999999
-7 0.11487993299999988
-8 0.133517793
-9 0.15717948700000006
-10 0.19438595300000028
-11 0.28230015900000005
-12 0.6000471900000002
-13 2.0247051640000002
-14 9.909082764
diff --git a/simulaqron/tests/performance/backend/wstate/runtime_qutip_vs_projectq.pdf b/simulaqron/tests/performance/backend/wstate/runtime_qutip_vs_projectq.pdf
deleted file mode 100644
index ea79e51..0000000
Binary files a/simulaqron/tests/performance/backend/wstate/runtime_qutip_vs_projectq.pdf and /dev/null differ
diff --git a/simulaqron/tests/performance/backend/wstate/runtime_qutip_vs_projectq.png b/simulaqron/tests/performance/backend/wstate/runtime_qutip_vs_projectq.png
deleted file mode 100644
index 61acda1..0000000
Binary files a/simulaqron/tests/performance/backend/wstate/runtime_qutip_vs_projectq.png and /dev/null differ
diff --git a/simulaqron/tests/performance/backend/wstate/test_w_state.py b/simulaqron/tests/performance/backend/wstate/test_w_state.py
deleted file mode 100644
index f8aba5a..0000000
--- a/simulaqron/tests/performance/backend/wstate/test_w_state.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import sys
-
-from cqc.pythonLib import CQCConnection
-from cqc.pythonLib_protocols import wstate
-from simulaqron.settings import simulaqron_settings
-
-from timeit import default_timer as timer
-
-with CQCConnection("Alice") as cqc:
-    nmax = int(sys.argv[1])
-    backend = simulaqron_settings.backend
-    times = []
-    sizes = []
-    for n in range(1, nmax + 1):
-        t1 = timer()
-        qubits = wstate.create_Nqubit_Wstate(n, cqc)
-        [q.measure() for q in qubits]
-        t2 = timer()
-        times.append(t2 - t1)
-        sizes.append(n)
-        print("Creating W state on {} qubits took {} s using {} as backend".format(n, t2 - t1, backend))
-    with open("run_time_w_state_{}.txt".format(backend), "w") as f:
-        for (n, t) in zip(sizes, times):
-            f.write("{} {}\n".format(n, t))
diff --git a/simulaqron/tests/performance/number_of_qubits/alice.py b/simulaqron/tests/performance/number_of_qubits/alice.py
deleted file mode 100644
index 1042b24..0000000
--- a/simulaqron/tests/performance/number_of_qubits/alice.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from cqc.pythonLib import CQCConnection, qubit
-
-import sys
-
-from timeit import default_timer as timer
-
-import matplotlib.pyplot as plt
-
-min_nr = int(sys.argv[1])
-max_nr = int(sys.argv[2])
-
-Alice = CQCConnection("Alice")
-
-times = []
-
-for n in range(min_nr, max_nr + 1):
-
-    qubits = []
-
-    t1 = timer()
-
-    for _ in range(n):
-        qubits.append(qubit(Alice))
-
-    for q in qubits:
-        q.measure()
-
-    t2 = timer()
-
-    times.append(t2 - t1)
-
-with open("times_qubits.txt", "w") as f:
-    for t in times:
-        f.write(str(t) + "\n")
-
-plt.plot(list(range(min_nr, max_nr + 1)), times, marker=".", markersize=10, linestyle="none", color="red")
-plt.xlabel("nr of qubits")
-plt.ylabel("time (s)")
-
-plt.savefig("times_qubits.pdf")
diff --git a/simulaqron/tests/performance/ring_teleport/README.txt b/simulaqron/tests/performance/ring_teleport/README.txt
deleted file mode 100644
index e6f909f..0000000
--- a/simulaqron/tests/performance/ring_teleport/README.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-In this example Alice teleports a qubit to Bob.
-
-Alice and Bob starts by creating a shared EPR pair. Alice creates a fresh qubit which she wants to teleport. In this case she puts it in the |+> state by performing a Hadamard operation. She performes the local teleportation operations and measures her qubits. She sends her measurement outcomes using a classical channel. Bob receives the measurement outcomes and performs corrections if needed. He finally measures his qubit in the standard basis and prints the measurement
-outcome.
diff --git a/simulaqron/tests/performance/ring_teleport/configure_ring.py b/simulaqron/tests/performance/ring_teleport/configure_ring.py
deleted file mode 100644
index 6500eec..0000000
--- a/simulaqron/tests/performance/ring_teleport/configure_ring.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import sys
-import os
-
-from simulaqron.toolbox import get_simulaqron_path
-
-# Get path to SimulaQron folder
-simulaqron_path = get_simulaqron_path.main()
-
-tot_nr = int(sys.argv[1])
-
-# configure run files for nodes
-
-with open("run.sh", "w") as f:
-    f.write("#!/bin/sh\n\n")
-    for i in range(tot_nr - 1):
-        f.write("python3 node.py {} {} &\n".format(i, tot_nr))
-    f.write("python3 node.py {} {}\n".format(tot_nr - 1, tot_nr))
-
-with open("run_v2.sh", "w") as f:
-    f.write("#!/bin/sh\n\n")
-    for i in range(tot_nr - 1):
-        f.write("python3 node_v2.py {} {} &\n".format(i, tot_nr))
-    f.write("python3 node_v2.py {} {}\n".format(tot_nr - 1, tot_nr))
-
-# configure network
-
-nodes = "".join(["n" + str(i) + " " for i in range(tot_nr)])
-
-os.system("python3 " + simulaqron_path + "configFiles.py " + nodes)
diff --git a/simulaqron/tests/performance/ring_teleport/doNew.sh b/simulaqron/tests/performance/ring_teleport/doNew.sh
deleted file mode 100755
index 0671f29..0000000
--- a/simulaqron/tests/performance/ring_teleport/doNew.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env sh
-NODE_PIDS=$(ps aux | grep python | grep -E "node\.py|node_v2\.py" | awk {'print $2'})
-if [ "$NODE_PIDS" != "" ]
-then
-        kill -9 $NODE_PIDS
-fi
-sh run.sh
diff --git a/simulaqron/tests/performance/ring_teleport/doNew_v2.sh b/simulaqron/tests/performance/ring_teleport/doNew_v2.sh
deleted file mode 100755
index bbf9197..0000000
--- a/simulaqron/tests/performance/ring_teleport/doNew_v2.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env sh
-NODE_PIDS=$(ps aux | grep python | grep -E "node\.py|node_v2\.py" | awk {'print $2'})
-if [ "$NODE_PIDS" != "" ]
-then
-        kill -9 $NODE_PIDS
-fi
-sh run_v2.sh
diff --git a/simulaqron/tests/performance/ring_teleport/node.py b/simulaqron/tests/performance/ring_teleport/node.py
deleted file mode 100644
index aa10f76..0000000
--- a/simulaqron/tests/performance/ring_teleport/node.py
+++ /dev/null
@@ -1,148 +0,0 @@
-#
-# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from cqc.pythonLib import CQCConnection, qubit
-
-import sys
-from timeit import default_timer as timer
-
-
-#####################################################################################################
-#
-# main
-#
-def main():
-
-    input_data = sys.argv[1:]
-
-    # Set node numbers
-    node_nr = int(input_data[0])
-    tot_nr = int(input_data[1])
-    next_node_nr = (node_nr + 1) % tot_nr
-
-    # Initialize the connection
-    node = CQCConnection("n" + str(node_nr))
-
-    if node_nr == 0:  # this is the first node so create qubit
-
-        # start timer
-        t1 = timer()
-
-        # Create a qubit to teleport
-        q = qubit(node)
-
-        # Prepare the qubit to teleport in |+>
-        q.H()
-
-        # ------
-        # Qubit is created, send it to next node
-        # ------
-
-    else:  # we are node in chain so receive qubit
-
-        # Make an EPR pair with previous node
-        q = node.recvEPR()
-
-        # Receive info about corrections
-        data = node.recvClassical()
-        message = list(data)
-        a = message[0]
-        b = message[1]
-
-        # Apply corrections
-        if b == 1:
-            q.X()
-        if a == 1:
-            q.Z()
-
-            # ------
-            # Qubit is receive, send it to next node
-            # ------
-
-            # Make an EPR pair with next node
-    qEPR = node.createEPR("n" + str(next_node_nr))
-
-    # Apply the local teleportation operations
-    q.cnot(qEPR)
-    q.H()
-
-    # Measure the qubits
-    a = q.measure()
-    b = qEPR.measure()
-    to_print = "App {}: Measurement outcomes are: a={}, b={}".format(node.name, a, b)
-    print("|" + "-" * (len(to_print) + 2) + "|")
-    print("| " + to_print + " |")
-    print("|" + "-" * (len(to_print) + 2) + "|")
-
-    # Send corrections to next node
-    node.sendClassical("n" + str(next_node_nr), [a, b])
-    # node.closeClassicalChannel('n'+str(next_node_nr))
-
-    if node_nr == 0:  # this is first node, so receive again after qubit traversed chain
-
-        # Make an EPR pair with last node
-        q = node.recvEPR()
-
-        # Receive info about corrections
-        data = node.recvClassical()
-        message = list(data)
-        a = message[0]
-        b = message[1]
-
-        # Apply corrections
-        if b == 1:
-            q.X()
-        if a == 1:
-            q.Z()
-
-            # ------
-            # Qubit is receive, so measure it
-            # ------
-
-            # measure the qubit, print the outcome and record the time it took
-        m = q.measure()
-        t2 = timer()
-        to_print = "App {}: Measurement outcome is: m={}".format(node.name, m)
-        print("|" + "-" * (len(to_print) + 2) + "|")
-        print("| " + to_print + " |")
-        print("|" + "-" * (len(to_print) + 2) + "|")
-        to_print = "App {}: Time elapsed: t={}".format(node.name, t2 - t1)
-        print("|" + "-" * (len(to_print) + 2) + "|")
-        print("| " + to_print + " |")
-        print("|" + "-" * (len(to_print) + 2) + "|")
-
-        with open("times.txt", "a") as f:
-            f.write("{}, {}\n".format(tot_nr, t2 - t1))
-
-            # Stop the connection
-    node.close()
-
-
-##################################################################################################
-main()
diff --git a/simulaqron/tests/performance/ring_teleport/node_v2.py b/simulaqron/tests/performance/ring_teleport/node_v2.py
deleted file mode 100644
index c4925b0..0000000
--- a/simulaqron/tests/performance/ring_teleport/node_v2.py
+++ /dev/null
@@ -1,147 +0,0 @@
-#
-# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from cqc.pythonLib import CQCConnection, qubit
-
-import sys
-from timeit import default_timer as timer
-
-
-#####################################################################################################
-#
-# main
-#
-def main():
-
-    input_data = sys.argv[1:]
-
-    # Set node numbers
-    node_nr = int(input_data[0])
-    tot_nr = int(input_data[1])
-    next_node_nr = (node_nr + 1) % tot_nr
-
-    # Initialize the connection
-    node = CQCConnection("n" + str(node_nr))
-
-    # Create EPR pairs with previous and next node
-    if node_nr == 0:
-
-        # start timer
-        t1 = timer()
-
-        qNext = node.createEPR("n" + str(next_node_nr))
-        qPrev = node.recvEPR()
-    else:
-        qPrev = node.recvEPR()
-        qNext = node.createEPR("n" + str(next_node_nr))
-
-    if node_nr == 0:  # this is the first node so create qubit
-
-        # Create a qubit to teleport
-        q = qubit(node)
-
-        # Prepare the qubit to teleport in |+>
-        q.H()
-
-        # ------
-        # Qubit is created, send it to next node
-        # ------
-
-    else:  # we are node in chain so receive classical corrections
-
-        # Receive info about corrections
-        data = node.recvClassical()
-        message = list(data)
-        a = message[0]
-        b = message[1]
-
-        # Apply corrections
-        if b == 1:
-            qPrev.X()
-        if a == 1:
-            qPrev.Z()
-
-            # ------
-            # Qubit is receive, send it to next node
-            # ------
-
-            # Apply the local teleportation operations
-    qPrev.cnot(qNext)
-    qPrev.H()
-
-    # Measure the qubits
-    a = qPrev.measure()
-    b = qNext.measure()
-    to_print = "App {}: Measurement outcomes are: a={}, b={}".format(node.name, a, b)
-    print("|" + "-" * (len(to_print) + 2) + "|")
-    print("| " + to_print + " |")
-    print("|" + "-" * (len(to_print) + 2) + "|")
-
-    # Send corrections to next node
-    node.sendClassical("n" + str(next_node_nr), [a, b])
-
-    if node_nr == 0:  # this is first node, so receive again after qubit traversed chain
-
-        # Receive info about corrections
-        data = node.recvClassical()
-        message = list(data)
-        a = message[0]
-        b = message[1]
-
-        # Apply corrections
-        if b == 1:
-            qPrev.X()
-        if a == 1:
-            qPrev.Z()
-
-            # ------
-            # Qubit is receive, so measure it
-            # ------
-
-            # measure the qubit, print the outcome and record the time it took
-        m = q.measure()
-        t2 = timer()
-        to_print = "App {}: Measurement outcome is: m={}".format(node.name, m)
-        print("|" + "-" * (len(to_print) + 2) + "|")
-        print("| " + to_print + " |")
-        print("|" + "-" * (len(to_print) + 2) + "|")
-        to_print = "App {}: Time elapsed: t={}".format(node.name, t2 - t1)
-        print("|" + "-" * (len(to_print) + 2) + "|")
-        print("| " + to_print + " |")
-        print("|" + "-" * (len(to_print) + 2) + "|")
-
-        with open("times_v2.txt", "a") as f:
-            f.write("{}, {}\n".format(tot_nr, t2 - t1))
-
-            # Stop the connection
-    node.close()
-
-
-##################################################################################################
-main()
diff --git a/simulaqron/tests/performance/ring_teleport/plot_data.py b/simulaqron/tests/performance/ring_teleport/plot_data.py
deleted file mode 100644
index 85e9188..0000000
--- a/simulaqron/tests/performance/ring_teleport/plot_data.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import matplotlib.pyplot as plt
-import sys
-
-filename = sys.argv[1]
-
-with open(filename + ".txt", "r") as f:
-    data = f.readlines()
-
-times = {}
-avg = {}
-for datapoint_str in data:
-    datapoint = datapoint_str.split(", ")
-    nr = int(datapoint[0])
-    time = float(datapoint[1])
-    if nr in times:
-        times[nr].append(time)
-    else:
-        times[nr] = [time]
-
-for (nr, tims) in times.items():
-    avg[nr] = sum(tims) / len(tims)
-
-plt.plot(list(avg.keys()), list(avg.values()), marker=".", markersize=10, linestyle="none", color="green")
-
-with open(filename + "_v2.txt", "r") as f:
-    data2 = f.readlines()
-
-times2 = {}
-avg2 = {}
-for datapoint_str in data2:
-    datapoint = datapoint_str.split(", ")
-    nr = int(datapoint[0])
-    time = float(datapoint[1])
-    if nr in times2:
-        times2[nr].append(time)
-    else:
-        times2[nr] = [time]
-
-for (nr, tims) in times2.items():
-    avg2[nr] = sum(tims) / len(tims)
-
-plt.plot(list(avg2.keys()), list(avg2.values()), marker=".", markersize=10, linestyle="none", color="red")
-
-plt.xlabel("nr of nodes")
-plt.ylabel("time (s)")
-plt.legend(["EPR on the fly", "EPR first"])
-
-plt.savefig(filename + ".pdf")
diff --git a/simulaqron/tests/performance/ring_teleport/run.sh b/simulaqron/tests/performance/ring_teleport/run.sh
deleted file mode 100755
index 74f5d75..0000000
--- a/simulaqron/tests/performance/ring_teleport/run.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-
-python3 node.py 0 20 &
-python3 node.py 1 20 &
-python3 node.py 2 20 &
-python3 node.py 3 20 &
-python3 node.py 4 20 &
-python3 node.py 5 20 &
-python3 node.py 6 20 &
-python3 node.py 7 20 &
-python3 node.py 8 20 &
-python3 node.py 9 20 &
-python3 node.py 10 20 &
-python3 node.py 11 20 &
-python3 node.py 12 20 &
-python3 node.py 13 20 &
-python3 node.py 14 20 &
-python3 node.py 15 20 &
-python3 node.py 16 20 &
-python3 node.py 17 20 &
-python3 node.py 18 20 &
-python3 node.py 19 20
diff --git a/simulaqron/tests/performance/ring_teleport/run_multiple.py b/simulaqron/tests/performance/ring_teleport/run_multiple.py
deleted file mode 100644
index 5d3e435..0000000
--- a/simulaqron/tests/performance/ring_teleport/run_multiple.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import sys
-import os
-
-from simulaqron.toolbox import get_simulaqron_path
-
-# Get path to SimulaQron folder
-simulaqron_path = get_simulaqron_path.main()
-
-input_data = sys.argv[1:]
-if len(input_data) == 0:
-    min_nodes = 2
-    max_nodes = 3
-    iterations = 1
-elif len(input_data) == 1:
-    min_nodes = 2
-    max_nodes = int(input_data[0])
-    iterations = 1
-elif len(input_data) == 2:
-    min_nodes = int(input_data[0])
-    max_nodes = int(input_data[1])
-    iterations = 1
-else:
-    min_nodes = int(input_data[0])
-    max_nodes = int(input_data[1])
-    iterations = int(input_data[2])
-
-
-with open("times.txt", "w") as f:
-    pass
-
-with open("times_v2.txt", "w") as f:
-    pass
-
-for n in range(min_nodes, max_nodes + 1):
-    os.system("python3 configure_ring.py {}".format(n))
-    os.system("sh " + simulaqron_path + "run/startAll.sh")
-    # time.sleep(30)
-    for _ in range(iterations):
-        os.system("sh doNew.sh")
-        # time.sleep(10)
-        os.system("sh doNew_v2.sh")
-        # time.sleep(10)
diff --git a/simulaqron/tests/performance/ring_teleport/run_v2.sh b/simulaqron/tests/performance/ring_teleport/run_v2.sh
deleted file mode 100755
index 8c78425..0000000
--- a/simulaqron/tests/performance/ring_teleport/run_v2.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-
-python3 node_v2.py 0 20 &
-python3 node_v2.py 1 20 &
-python3 node_v2.py 2 20 &
-python3 node_v2.py 3 20 &
-python3 node_v2.py 4 20 &
-python3 node_v2.py 5 20 &
-python3 node_v2.py 6 20 &
-python3 node_v2.py 7 20 &
-python3 node_v2.py 8 20 &
-python3 node_v2.py 9 20 &
-python3 node_v2.py 10 20 &
-python3 node_v2.py 11 20 &
-python3 node_v2.py 12 20 &
-python3 node_v2.py 13 20 &
-python3 node_v2.py 14 20 &
-python3 node_v2.py 15 20 &
-python3 node_v2.py 16 20 &
-python3 node_v2.py 17 20 &
-python3 node_v2.py 18 20 &
-python3 node_v2.py 19 20
diff --git a/simulaqron/tests/performance/ring_teleport/times2_16.txt b/simulaqron/tests/performance/ring_teleport/times2_16.txt
deleted file mode 100644
index 4c3a4a4..0000000
--- a/simulaqron/tests/performance/ring_teleport/times2_16.txt
+++ /dev/null
@@ -1,150 +0,0 @@
-2, 0.7177341419999834
-2, 0.281532722999998
-2, 0.28964965400001574
-2, 0.2957459609999944
-2, 0.2813727589999928
-2, 0.29540408400001184
-2, 0.295517459999985
-2, 0.2775127679999798
-2, 0.28138913199998683
-2, 0.29949067199999035
-3, 0.32490682200000265
-3, 0.4343534690000297
-3, 0.44215179299999363
-3, 0.4238476579999997
-3, 0.43464125299999523
-3, 0.435625205000008
-3, 0.4238021369999956
-3, 0.4374064940000153
-3, 0.43577280800002427
-3, 0.44142959800001336
-4, 0.47265854300002275
-4, 0.37427639800000634
-4, 0.3815459929999747
-4, 0.3813588229999709
-4, 0.49565387899997404
-4, 0.377755202000003
-4, 0.3726617109999779
-4, 0.5990742329999534
-4, 0.600492289999977
-4, 0.47452886499996794
-5, 0.7457403249999857
-5, 0.6293713280000475
-5, 0.5270614479999836
-5, 0.6340524559999494
-5, 0.6338451859999736
-5, 0.743383295000001
-5, 0.6258817420000469
-5, 0.6335610489999794
-5, 0.6429174550000312
-5, 0.6256524579999905
-6, 0.5660075870000014
-6, 0.7977657199999726
-6, 0.7884604120000063
-6, 0.8019833669999912
-6, 0.8059711409999863
-6, 0.7793825399999719
-6, 0.6812949479999588
-6, 0.6811086289999935
-6, 0.7888797240000258
-6, 0.6761409599999979
-7, 1.1615561209999896
-7, 0.9617467079999642
-7, 0.8240641610000239
-7, 0.9658207019999736
-7, 0.8315088419999483
-7, 0.8367814990000397
-7, 0.9326173850000146
-7, 0.9404792120000138
-7, 0.7539289190000318
-7, 0.747546012999976
-8, 1.51666386200003
-8, 0.9928524370000105
-8, 0.7966122519999885
-8, 1.2246171410000102
-8, 1.1176952760000063
-8, 1.000232244000017
-8, 0.889982821999979
-8, 1.0108629360000236
-8, 0.996735086000001
-8, 1.1139424699999836
-9, 1.079239298999994
-9, 1.273117334999995
-9, 1.2749151159999883
-9, 1.1782757500000116
-9, 1.2817653120000045
-9, 1.0506011380000473
-9, 1.1691652640000143
-9, 1.073245189999966
-9, 1.1877186290000168
-9, 1.0400227539999491
-10, 1.1296687219999626
-10, 1.2082653280000386
-10, 1.1820539650000228
-10, 1.1210895320000418
-10, 1.3386315839999838
-10, 1.0918213560000822
-10, 1.3397774520000212
-10, 1.5414498150000782
-10, 1.4391375769999968
-10, 1.0974395449999292
-11, 1.856534613000008
-11, 1.3048477450000746
-11, 1.4111046809999834
-11, 1.273162952000007
-11, 1.5196409400000448
-11, 1.6401408079999555
-11, 1.6182262319999836
-11, 1.0005450920000385
-11, 1.6242967550000458
-11, 1.003995385000053
-12, 2.2003637010000148
-12, 1.4983622749999768
-12, 1.4914345229999526
-12, 1.685619273000043
-12, 1.6471908290000101
-12, 1.4869182620000174
-12, 1.5553871930000014
-12, 1.5534593250000626
-12, 1.804590423000036
-12, 1.50278223600003
-13, 2.4145459689999598
-13, 1.400197845999969
-13, 1.3694786160000376
-13, 1.6459205099999963
-13, 1.769564589999959
-13, 1.7796383180000248
-13, 1.8893862380000428
-13, 1.4379082410000592
-13, 1.7733141610000303
-13, 1.6702558700000054
-14, 1.7515891799999963
-14, 1.7304180000000997
-14, 1.538629582999988
-14, 1.6418091460000142
-14, 1.5684067649999633
-14, 1.1999038719999362
-14, 1.941977501999986
-14, 1.7437992930000519
-14, 1.6430917220000083
-14, 1.9681642799999963
-15, 1.844753565000019
-15, 1.7716235149999875
-15, 2.0154265580000583
-15, 1.8302748849999944
-15, 2.127891405000014
-15, 2.1018817740000486
-15, 1.7015870909999649
-15, 1.775872703999994
-15, 2.0805090310000196
-15, 2.015952422000055
-16, 2.2236141509999925
-16, 1.9509080369999765
-16, 2.1451873129999512
-16, 1.8963959519999207
-16, 1.839533361000008
-16, 2.323609168999951
-16, 1.7396894790000488
-16, 1.6318306119999306
-16, 2.545900051999979
-16, 2.366539794000005
diff --git a/simulaqron/tests/performance/ring_teleport/times2_16_v2.txt b/simulaqron/tests/performance/ring_teleport/times2_16_v2.txt
deleted file mode 100644
index 659dd75..0000000
--- a/simulaqron/tests/performance/ring_teleport/times2_16_v2.txt
+++ /dev/null
@@ -1,150 +0,0 @@
-2, 0.28404893499998707
-2, 0.2860272409999993
-2, 0.29262076500000944
-2, 0.2879151479999962
-2, 0.2826393850000102
-2, 0.2885471649999829
-2, 0.27662814199999275
-2, 0.281068748999985
-2, 0.18070615400000634
-2, 0.278038448000018
-3, 0.41562448799999174
-3, 0.325104065000005
-3, 0.4234252260000062
-3, 0.4290979749999906
-3, 0.4335263149999946
-3, 0.4334778869999809
-3, 0.4253531779999662
-3, 0.3234500650000314
-3, 0.42697826700003816
-3, 0.41517975899995463
-4, 0.4728982710000196
-4, 0.5757036059999905
-4, 0.4699263690000066
-4, 0.3639614910000546
-4, 0.5727127409999753
-4, 0.47886609700003646
-4, 0.5626271080000151
-4, 0.5800150320000057
-4, 0.3695317050000426
-4, 0.4709837739999898
-5, 0.6175482539999848
-5, 0.6074733410000022
-5, 0.7099020550000432
-5, 0.69824724099999
-5, 0.49987974999999096
-5, 0.6039745310000058
-5, 0.6073009970000385
-5, 0.7142678980000028
-5, 0.7081149570000207
-5, 0.4879584220000197
-6, 0.5413591479999695
-6, 0.45500375900002155
-6, 0.7570937499999673
-6, 0.6534841099999653
-6, 0.6537160010000207
-6, 0.7632949919999987
-6, 0.557659415000046
-6, 0.653463644999988
-6, 0.7541608309999788
-6, 0.5488482300000328
-7, 0.5784686349999788
-7, 0.702796372000023
-7, 0.8166959779999843
-7, 0.8122141250000254
-7, 0.8115033259999791
-7, 0.699040172000025
-7, 0.794855014999996
-7, 0.7055498770000099
-7, 0.9110088740000037
-7, 0.6909959790000357
-8, 0.752180214999953
-8, 0.9601315450000243
-8, 0.7552253510000355
-8, 0.8555885490000037
-8, 0.7616903830000297
-8, 0.8416280839999786
-8, 0.8447874750000324
-8, 0.9594810609999627
-8, 0.9443687179999642
-8, 0.7455156659999602
-9, 0.8024121029999947
-9, 0.8987783040000181
-9, 1.0078074689999994
-9, 0.9946591030000036
-9, 0.8913689400000067
-9, 1.0013741359999813
-9, 0.9150695810000116
-9, 0.896071457000005
-9, 1.113923977000013
-9, 0.9971926619999749
-10, 1.1806404189999853
-10, 1.0499554330000365
-10, 1.064725449999969
-10, 1.1513944239999887
-10, 1.0644452620000493
-10, 1.0490701930000341
-10, 1.0677186749999237
-10, 1.274300427000071
-10, 1.1577743340000097
-10, 1.0488447599999517
-11, 1.326247813000009
-11, 1.2359233880000602
-11, 1.3525398129999076
-11, 1.124668751999934
-11, 1.2432879350000121
-11, 1.1314197830000694
-11, 1.3406060370000432
-11, 1.3284337580000738
-11, 1.1146285529999886
-11, 1.1227551580000181
-12, 1.2027814719999697
-12, 1.295015309000064
-12, 1.504533293999998
-12, 1.2894934529999773
-12, 1.2887174469999536
-12, 1.5931979509999792
-12, 1.2959334559999434
-12, 1.2881887190000043
-12, 1.498538549999921
-12, 1.4804186589999517
-13, 1.4286025810000638
-13, 1.4329216659999702
-13, 1.3481884870000158
-13, 1.4523925849999841
-13, 1.354231077999998
-13, 1.75915588700002
-13, 1.6583373910000319
-13, 1.6320210239999824
-13, 1.661745615999962
-13, 1.5289439120000452
-14, 1.6086658829999578
-14, 1.6412809429999697
-14, 1.6586599739999883
-14, 1.6155854750001026
-14, 1.67307145999996
-14, 1.7757157739999911
-14, 1.6370818059999692
-14, 1.7650148270000727
-14, 1.7205414530000098
-14, 1.7417459490000056
-15, 1.7235814489999939
-15, 1.9172952249999753
-15, 1.6836801929999865
-15, 1.9143907669999862
-15, 1.5665866299999607
-15, 2.004752326000016
-15, 2.0125609990000157
-15, 1.7906143619999284
-15, 1.8157188879999921
-15, 1.709970574999943
-16, 1.8876010710000628
-16, 2.4420793400000775
-16, 2.3162020710000206
-16, 1.7790030969999862
-16, 1.8179636730000084
-16, 2.051601044999984
-16, 1.8622893290000775
-16, 2.30536027200003
-16, 2.324920407000036
-16, 2.163671759999943
diff --git a/simulaqron/tests/performance/ring_teleport/times2_42_1.txt b/simulaqron/tests/performance/ring_teleport/times2_42_1.txt
deleted file mode 100644
index 53f15b7..0000000
--- a/simulaqron/tests/performance/ring_teleport/times2_42_1.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-2, 0.2989235939999162
-3, 0.30307358199979717
-4, 0.6759920489998876
-5, 0.6439982480001163
-6, 0.8058300880002207
-7, 0.9146319090000361
-8, 0.8043384430002334
-9, 1.4513638669995998
-10, 1.3279565029997684
-11, 1.4283249449999857
-12, 2.074446855000133
-13, 1.6422774089996892
-14, 1.6554575220002334
-15, 2.079433153000082
-16, 2.6130706649996682
-17, 2.5140244859999257
-18, 2.59253948200012
-19, 2.803334119000283
-20, 2.6480106019998857
-21, 2.635266010000123
-22, 3.085091796999677
-23, 3.2924870299998474
-24, 3.335579224000412
-25, 3.0583485560000554
-26, 3.189754450000237
-27, 3.526896483999735
-28, 3.9577867959997093
-29, 3.818573946000015
-30, 3.6112188470001456
-31, 3.896520365000015
-32, 4.458145407000302
-33, 4.539265265000267
-34, 4.3520230979997905
-35, 4.679897528999845
-36, 4.679567891999795
-37, 4.691245972999695
-38, 4.76922780599989
-39, 5.5387099919998946
-40, 6.624812531999851
-41, 6.334433107000223
diff --git a/simulaqron/tests/performance/ring_teleport/times2_42_1_v2.txt b/simulaqron/tests/performance/ring_teleport/times2_42_1_v2.txt
deleted file mode 100644
index be8cb8c..0000000
--- a/simulaqron/tests/performance/ring_teleport/times2_42_1_v2.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-2, 0.286325639000097
-3, 0.8477906690000054
-4, 0.47416921400008505
-5, 0.507759759000237
-6, 0.5652044990001741
-7, 0.7965436660001615
-8, 0.9645479569999225
-9, 0.8838307460000578
-10, 1.0640344439998444
-11, 1.4086253299997225
-12, 1.386994292000054
-13, 1.6646950020003715
-14, 1.4321891240001605
-15, 1.7939979550001226
-16, 1.8279019209999205
-17, 1.8252453740001329
-18, 2.089057245999811
-19, 2.432721283000319
-20, 2.8450996729998224
-21, 2.576379008999993
-22, 3.15357760400002
-23, 2.964505575000203
-24, 3.181551736000074
-25, 4.076232151000113
-26, 4.384805818000132
-27, 3.7544137390000287
-28, 4.734913637999853
-29, 4.804471573000228
-30, 5.535417140000391
-31, 5.729646267000135
-32, 5.383611506999841
-33, 5.545613188000061
-34, 5.758532325000033
-35, 5.1878886750000675
-36, 6.209775368999999
-37, 5.937321964000148
-38, 6.974993882000035
-39, 6.1659786969999
-40, 6.35835594900027
-41, 7.8005022389997976
diff --git a/simulaqron/tests/performance/two_computers/client.py b/simulaqron/tests/performance/two_computers/client.py
deleted file mode 100644
index cb62009..0000000
--- a/simulaqron/tests/performance/two_computers/client.py
+++ /dev/null
@@ -1,136 +0,0 @@
-#
-# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from cqc.pythonLib import CQCConnection, qubit
-
-import sys
-from timeit import default_timer as timer
-
-
-#####################################################################################################
-#
-# main
-#
-def main():
-
-    input_data = sys.argv[1:]
-
-    # Set node numbers
-    min_tel = int(input_data[0])
-    max_tel = int(input_data[1])
-    number = int(input_data[2])
-
-    # Clear file
-    with open("times{}_{}_{}.txt".format(min_tel, max_tel, number), "w") as f:
-        pass
-
-        # Initialize the connection
-    Alice = CQCConnection("Alice")
-
-    times = []
-
-    for n in range(min_tel, max_tel + 1):
-
-        print("========")
-        print(n)
-        print("========")
-
-        for _ in range(number):
-
-            # start timer
-            t1 = timer()
-
-            # Create a qubit to teleport
-            q = qubit(Alice)
-
-            # Prepare the qubit to teleport in |+>
-            q.H()
-
-            # Start teleporting back and fourth
-
-            for _ in range(n):
-                # Make an EPR pair with next node
-                qEPR = Alice.createEPR("Bob")
-
-                # Apply the local teleportation operations
-                q.cnot(qEPR)
-                q.H()
-
-                # Measure the qubits
-                a = q.measure()
-                b = qEPR.measure()
-                to_print = "App {}: Measurement outcomes are: a={}, b={}".format(Alice.name, a, b)
-                print("|" + "-" * (len(to_print) + 2) + "|")
-                print("| " + to_print + " |")
-                print("|" + "-" * (len(to_print) + 2) + "|")
-
-                # Send corrections to other node
-                Alice.sendClassical("Bob", [a, b])
-                Alice.closeClassicalChannel("Bob")
-
-                # Make an EPR pair with other node
-                q = Alice.createEPR("Bob")
-
-                # Receive info about corrections
-                data = Alice.recvClassical(timout=3600)
-                Alice.closeClassicalServer()
-                message = list(data)
-                a = message[0]
-                b = message[1]
-
-                # Apply corrections
-                if b == 1:
-                    q.X()
-                if a == 1:
-                    q.Z()
-
-                    # measure the qubit, print the outcome and record the time it took
-            m = q.measure()
-            t2 = timer()
-            to_print = "App {}: Measurement outcome is: m={}".format(Alice.name, m)
-            print("|" + "-" * (len(to_print) + 2) + "|")
-            print("| " + to_print + " |")
-            print("|" + "-" * (len(to_print) + 2) + "|")
-            to_print = "App {}: Time elapsed: t={}".format(Alice.name, t2 - t1)
-            print("|" + "-" * (len(to_print) + 2) + "|")
-            print("| " + to_print + " |")
-            print("|" + "-" * (len(to_print) + 2) + "|")
-
-            times.append((n, t2 - t1))
-
-    with open("times{}_{}_{}.txt".format(min_tel, max_tel, number), "a") as f:
-        for (n, t) in times:
-            f.write("{}, {}\n".format(n, t))
-
-            # Stop the connection
-    Alice.close()
-
-
-##################################################################################################
-main()
diff --git a/simulaqron/tests/performance/two_computers/configure.py b/simulaqron/tests/performance/two_computers/configure.py
deleted file mode 100644
index dfae542..0000000
--- a/simulaqron/tests/performance/two_computers/configure.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import sys
-import os
-
-from simulaqron.toolbox import get_simulaqron_path
-
-
-simulaqron_path = get_simulaqron_path.main()
-my_ip = sys.argv[1]
-other_ip = sys.argv[2]
-client = int(sys.argv[3])
-min_tel = int(sys.argv[4])
-max_tel = int(sys.argv[5])
-number = int(sys.argv[6])
-
-with open(os.path.join(simulaqron_path, "config/virtualNodes.cfg"), "w") as f:
-    if client:
-        f.write("Alice, " + my_ip + ", 8801\n")
-        f.write("Bob, " + other_ip + ", 8802")
-    else:
-        f.write("Alice, " + other_ip + ", 8801\n")
-        f.write("Bob, " + my_ip + ", 8802")
-
-with open(os.path.join(simulaqron_path, "config/cqcNodes.cfg"), "w") as f:
-    if client:
-        f.write("Alice, " + my_ip + ", 8803\n")
-        f.write("Bob, " + other_ip + ", 8804")
-    else:
-        f.write("Alice, " + other_ip + ", 8803\n")
-        f.write("Bob, " + my_ip + ", 8804")
-
-with open(os.path.join(simulaqron_path, "config/appNodes.cfg"), "w") as f:
-    if client:
-        f.write("Alice, " + my_ip + ", 8805\n")
-        f.write("Bob, " + other_ip + ", 8806")
-    else:
-        f.write("Alice, " + other_ip + ", 8805\n")
-        f.write("Bob, " + my_ip + ", 8806")
-
-with open("run.sh", "w") as f:
-    f.write("#!/bin/sh\n\n")
-    if client:
-        f.write("python3 " + simulaqron_path + "run/startNode.py Alice &\n")
-        f.write("python3 " + simulaqron_path + "run/startCQC.py Alice &\n")
-        # f.write("python3 "+netsim_path+"run/startNode.py Bob &\n")
-        # f.write("python3 "+netsim_path+"run/startCQC.py Bob &\n")
-
-        f.write("\nsleep 10s\n\n")
-
-        # f.write("python3 server.py {} {} {}&\n".format(min_tel,max_tel,number))
-
-        f.write("python3 client.py {} {} {}\n".format(min_tel, max_tel, number))
-    else:
-        f.write("python3 " + simulaqron_path + "run/startNode.py Bob\n")
-        f.write("python3 " + simulaqron_path + "run/startCQC.py Bob\n")
-
-        f.write("\nsleep 10s\n\n")
-
-        f.write("python3 server.py {} {} {}\n".format(min_tel, max_tel, number))
diff --git a/simulaqron/tests/performance/two_computers/doNew.sh b/simulaqron/tests/performance/two_computers/doNew.sh
deleted file mode 100755
index bf06a50..0000000
--- a/simulaqron/tests/performance/two_computers/doNew.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env sh
-PIDS=$(ps aux | grep python | grep -E "start|server\.py" | awk {'print $2'})
-if [ "$PIDS" != "" ]
-then
-        kill -9 $PIDS
-fi
-sh run.sh
diff --git a/simulaqron/tests/performance/two_computers/plot_data.py b/simulaqron/tests/performance/two_computers/plot_data.py
deleted file mode 100644
index 6c0a9a1..0000000
--- a/simulaqron/tests/performance/two_computers/plot_data.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import matplotlib.pyplot as plt
-import sys
-
-filename = sys.argv[1]
-
-with open(filename + ".txt", "r") as f:
-    data = f.readlines()
-
-times = {}
-avg = {}
-for datapoint_str in data:
-    datapoint = datapoint_str.split(", ")
-    nr = int(datapoint[0])
-    time = float(datapoint[1])
-    if nr in times:
-        times[nr].append(time)
-    else:
-        times[nr] = [time]
-
-for (nr, tims) in times.items():
-    avg[nr] = sum(tims) / len(tims)
-
-plt.plot(list(avg.keys()), list(avg.values()), marker=".", markersize=10, linestyle="none", color="red")
-
-plt.xlabel("nr of teleport (back & fourth)")
-plt.ylabel("time (s)")
-
-plt.savefig(filename + ".pdf")
diff --git a/simulaqron/tests/performance/two_computers/run.sh b/simulaqron/tests/performance/two_computers/run.sh
deleted file mode 100644
index e291dc7..0000000
--- a/simulaqron/tests/performance/two_computers/run.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-
-python3 /home/axel/Documents/SimulaQron/run/startNode.py Alice &
-python3 /home/axel/Documents/SimulaQron/run/startCQC.py Alice &
-
-sleep 10s
-
-python3 client.py 1 5 1
diff --git a/simulaqron/tests/performance/two_computers/server.py b/simulaqron/tests/performance/two_computers/server.py
deleted file mode 100644
index a5f9d4c..0000000
--- a/simulaqron/tests/performance/two_computers/server.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#
-# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from cqc.pythonLib import CQCConnection
-
-import sys
-
-
-#####################################################################################################
-#
-# main
-#
-def main():
-
-    input_data = sys.argv[1:]
-
-    # Set node numbers
-    min_tel = int(input_data[0])
-    max_tel = int(input_data[1])
-    number = int(input_data[2])
-
-    # Initialize the connection
-    Bob = CQCConnection("Bob")
-
-    for n in range(min_tel, max_tel + 1):
-
-        for _ in range(number):
-
-            # Start teleporting back and fourth
-
-            for _ in range(n):
-
-                # Make an EPR pair with other node
-                q = Bob.recvEPR()
-
-                # Receive info about corrections
-                data = Bob.recvClassical(timout=3600)
-                Bob.closeClassicalServer()
-                message = list(data)
-                a = message[0]
-                b = message[1]
-
-                # Apply corrections
-                if b == 1:
-                    q.X()
-                if a == 1:
-                    q.Z()
-
-                    # Make an EPR pair with next node
-                qEPR = Bob.recvEPR()
-
-                # Apply the local teleportation operations
-                q.cnot(qEPR)
-                q.H()
-
-                # Measure the qubits
-                a = q.measure()
-                b = qEPR.measure()
-                to_print = "App {}: Measurement outcomes are: a={}, b={}".format(Bob.name, a, b)
-                print("|" + "-" * (len(to_print) + 2) + "|")
-                print("| " + to_print + " |")
-                print("|" + "-" * (len(to_print) + 2) + "|")
-
-                # Send corrections to other node
-                Bob.sendClassical("Alice", [a, b])
-                Bob.closeClassicalChannel("Alice")
-
-                # Stop the connection
-    Bob.close()
-
-
-##################################################################################################
-main()
diff --git a/simulaqron/tests/performance/two_computers/times1_12_1.txt b/simulaqron/tests/performance/two_computers/times1_12_1.txt
deleted file mode 100644
index 1db5efa..0000000
--- a/simulaqron/tests/performance/two_computers/times1_12_1.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-1, 0.30869658701703884
-2, 0.5998739209899213
-3, 0.7873349309957121
-4, 1.1059292329882737
-5, 1.1984066249860916
-6, 1.5183787230052985
-7, 1.8013232309895102
-8, 2.1693921939877328
-9, 2.64721214902238
-10, 2.7742130649858154
-11, 3.0758592000056524
-12, 3.253846538020298
diff --git a/simulaqron/tests/performance/two_computers/times1_6_1.txt b/simulaqron/tests/performance/two_computers/times1_6_1.txt
deleted file mode 100644
index 1125982..0000000
--- a/simulaqron/tests/performance/two_computers/times1_6_1.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-1, 0.29814221200649627
-2, 0.5972449940163642
-3, 0.8002081179874949
-4, 1.1837152700172737
-5, 1.1986685350129846
-6, 1.7817335900035687
diff --git a/simulaqron/tests/unittests/__init__.py b/simulaqron/tests/unittests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/quick/__init__.py b/simulaqron/tests/unittests/quick/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/quick/engine/__init__.py b/simulaqron/tests/unittests/quick/engine/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/quick/network/__init__.py b/simulaqron/tests/unittests/quick/network/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/quick/network_topology/__init__.py b/simulaqron/tests/unittests/quick/network_topology/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/quick/network_topology/test_default_topology.py b/simulaqron/tests/unittests/quick/network_topology/test_default_topology.py
deleted file mode 100644
index 19c6eb4..0000000
--- a/simulaqron/tests/unittests/quick/network_topology/test_default_topology.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import unittest
-import logging
-
-from simulaqron.network import Network
-from simulaqron.settings import simulaqron_settings
-from cqc.pythonLib import CQCConnection, qubit, CQCUnsuppError
-
-
-class TestDefaultTopology(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        simulaqron_settings.default_settings()
-        simulaqron_settings._read_user = False
-        simulaqron_settings.log_level = logging.CRITICAL
-        cls.node_names = ["Alice", "Bob", "Charlie"]
-        cls.network = Network(nodes=cls.node_names, force=True)
-        cls.network.start()
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.network.stop()
-        simulaqron_settings.default_settings()
-
-    def test_send(self):
-        for sender_name in self.node_names:
-            for receiver_name in self.node_names:
-                with CQCConnection(sender_name) as sender:
-                    with CQCConnection(receiver_name) as receiver:
-                        q = qubit(sender)
-                        if sender_name == receiver_name:
-                            with self.assertRaises(CQCUnsuppError):
-                                sender.sendQubit(q=q, name=receiver_name, remote_appID=receiver._appID)
-                        else:
-                            sender.sendQubit(q=q, name=receiver_name, remote_appID=receiver._appID)
-                            q = receiver.recvQubit()
-                        m = q.measure()
-                        self.assertEqual(m, 0)
-
-    def test_EPR(self):
-        for sender_name in self.node_names:
-            for receiver_name in self.node_names:
-                with CQCConnection(sender_name) as sender:
-                    with CQCConnection(receiver_name) as receiver:
-                        if sender_name == receiver_name:
-                            with self.assertRaises(CQCUnsuppError):
-                                sender.createEPR(name=receiver_name, remote_appID=receiver._appID)
-                        else:
-                            qs = sender.createEPR(name=receiver_name, remote_appID=receiver._appID)
-                            qr = receiver.recvEPR()
-                            ms = qs.measure()
-                            mr = qr.measure()
-                            self.assertEqual((ms + mr) % 2, 0)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/simulaqron/tests/unittests/quick/network_topology/test_restricted_topology.py b/simulaqron/tests/unittests/quick/network_topology/test_restricted_topology.py
deleted file mode 100644
index 9c8c51e..0000000
--- a/simulaqron/tests/unittests/quick/network_topology/test_restricted_topology.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import unittest
-import logging
-
-from simulaqron.network import Network
-from simulaqron.settings import simulaqron_settings
-from cqc.pythonLib import CQCConnection, qubit, CQCUnsuppError
-
-
-class TestRestrictedTopology(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        simulaqron_settings.default_settings()
-        simulaqron_settings._read_user = False
-        simulaqron_settings.log_level = logging.CRITICAL
-        nodes = ["Alice", "Bob", "Charlie"]
-        cls.network = Network(nodes=nodes, topology="path", force=True)
-        cls.network.start()
-
-        cls.edges = [("Alice", "Bob"), ("Bob", "Charlie")]
-        cls.non_edges = [(node, node) for node in nodes] + [("Alice", "Charlie")]
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.network.stop()
-        simulaqron_settings.default_settings()
-
-    def test_send(self):
-        for sender_name, receiver_name in self.edges:
-            with CQCConnection(sender_name) as sender:
-                with CQCConnection(receiver_name) as receiver:
-                    q = qubit(sender)
-                    sender.sendQubit(q=q, name=receiver_name, remote_appID=receiver._appID)
-                    q = receiver.recvQubit()
-                    m = q.measure()
-                    self.assertEqual(m, 0)
-        for sender_name, receiver_name in self.non_edges:
-            with CQCConnection(sender_name) as sender:
-                with CQCConnection(receiver_name) as receiver:
-                    q = qubit(sender)
-                    with self.assertRaises(CQCUnsuppError):
-                        sender.sendQubit(q=q, name=receiver_name, remote_appID=receiver._appID)
-                    m = q.measure()
-                    self.assertEqual(m, 0)
-
-    def test_EPR(self):
-        for sender_name, receiver_name in self.edges:
-            with CQCConnection(sender_name) as sender:
-                with CQCConnection(receiver_name) as receiver:
-                    qs = sender.createEPR(name=receiver_name, remote_appID=receiver._appID)
-                    qr = receiver.recvEPR()
-                    ms = qs.measure()
-                    mr = qr.measure()
-                    self.assertEqual((ms + mr) % 2, 0)
-        for sender_name, receiver_name in self.non_edges:
-            with CQCConnection(sender_name) as sender:
-                with CQCConnection(receiver_name) as receiver:
-                    with self.assertRaises(CQCUnsuppError):
-                        sender.createEPR(name=receiver_name, remote_appID=receiver._appID)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/simulaqron/tests/unittests/quick/pythonLib/__init__.py b/simulaqron/tests/unittests/quick/pythonLib/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/quick/pythonLib/test_context.py b/simulaqron/tests/unittests/quick/pythonLib/test_context.py
deleted file mode 100644
index fa46274..0000000
--- a/simulaqron/tests/unittests/quick/pythonLib/test_context.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import unittest
-import logging
-
-from cqc.pythonLib import CQCConnection, qubit, CQCNoQubitError
-from simulaqron.settings import simulaqron_settings
-from simulaqron.network import Network
-
-
-class TestContext(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        simulaqron_settings.default_settings()
-        simulaqron_settings._read_user = False
-        simulaqron_settings.log_level = logging.CRITICAL
-        cls.network = Network(nodes=["Alice"], force=True)
-        cls.network.start()
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.network.stop()
-        simulaqron_settings.default_settings()
-
-    def setUp(self):
-        self.cqcs = []
-
-    def tearDown(self):
-        for cqc in self.cqcs:
-            cqc.close()
-
-    def test_without_context(self):
-        for _ in range(simulaqron_settings.max_qubits):
-            cqc = CQCConnection("Alice")
-            self.cqcs.append(cqc)
-            qubit(cqc)
-        with self.assertRaises(CQCNoQubitError):
-            cqc = CQCConnection("Alice")
-            self.cqcs.append(cqc)
-            qubit(cqc)
-
-    def test_with_context(self):
-        for _ in range(simulaqron_settings.max_qubits):
-            with CQCConnection("Alice") as cqc:
-                qubit(cqc)
-        with CQCConnection("Alice") as cqc:
-            qubit(cqc)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/simulaqron/tests/unittests/quick/pythonLib/test_maxQubits.py b/simulaqron/tests/unittests/quick/pythonLib/test_maxQubits.py
deleted file mode 100644
index 1bbfcd9..0000000
--- a/simulaqron/tests/unittests/quick/pythonLib/test_maxQubits.py
+++ /dev/null
@@ -1,101 +0,0 @@
-import unittest
-import logging
-
-from cqc.pythonLib import CQCConnection, qubit, CQCNoQubitError
-from simulaqron.settings import simulaqron_settings
-from simulaqron.network import Network
-
-
-class TestMaxQubit(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        simulaqron_settings.default_settings()
-        simulaqron_settings._read_user = False
-        simulaqron_settings.log_level = logging.CRITICAL
-        cls.network = Network(nodes=["Alice", "Bob"], force=True)
-        cls.network.start()
-
-        cls.alice = CQCConnection("Alice")
-        cls.bob = CQCConnection("Bob")
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.alice.close()
-        cls.bob.close()
-
-        cls.network.stop()
-        simulaqron_settings.default_settings()
-
-    def test_exact_max(self):
-        with CQCConnection("Alice") as alice:
-            for _ in range(simulaqron_settings.max_qubits):
-                qubit(alice)
-
-    def test_more_than_max(self):
-        with CQCConnection("Alice") as alice:
-            for _ in range(simulaqron_settings.max_qubits):
-                qubit(alice)
-
-            with self.assertRaises(CQCNoQubitError):
-                qubit(alice)
-
-    def test_send_for_last_position(self):
-        with CQCConnection("Alice") as alice:
-            with CQCConnection("Bob") as bob:
-                alice_qubits = []
-                bob_qubits = []
-                for _ in range(simulaqron_settings.max_qubits):
-                    q = qubit(alice)
-                    alice_qubits.append(q)
-                for _ in range(simulaqron_settings.max_qubits - 1):
-                    q = qubit(bob)
-                    bob_qubits.append(q)
-
-                alice.sendQubit(alice_qubits[0], "Bob", remote_appID=bob._appID)
-                q = bob.recvQubit()
-                bob_qubits.append(q)
-
-                with self.assertRaises(CQCNoQubitError):
-                    alice.sendQubit(alice_qubits[1], "Bob", remote_appID=bob._appID)
-
-                    # remove one qubit from Bob
-                bob_qubits[0].measure()
-
-                alice.sendQubit(alice_qubits[1], "Bob", remote_appID=bob._appID)
-                bob.recvQubit()
-
-    def test_epr_for_last_position(self):
-        with CQCConnection("Alice") as alice:
-            with CQCConnection("Bob") as bob:
-                alice_qubits = []
-                bob_qubits = []
-                for _ in range(simulaqron_settings.max_qubits - 1):
-                    q = qubit(alice)
-                    alice_qubits.append(q)
-                for _ in range(simulaqron_settings.max_qubits - 1):
-                    q = qubit(bob)
-                    bob_qubits.append(q)
-
-                q = alice.createEPR("Bob", remote_appID=bob._appID)
-                alice_qubits.append(q)
-                q = bob.recvEPR()
-                bob_qubits.append(q)
-
-                with self.assertRaises(CQCNoQubitError):
-                    alice.createEPR("Bob", remote_appID=bob._appID)
-
-                    # remove one qubit from Alice
-                alice_qubits[0].measure()
-
-                with self.assertRaises(CQCNoQubitError):
-                    alice.createEPR("Bob", remote_appID=bob._appID)
-
-                    # remove one qubit from Bob
-                bob_qubits[0].measure()
-
-                alice.createEPR("Bob", remote_appID=bob._appID)
-                bob.recvEPR()
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/simulaqron/tests/unittests/quick/pythonLib/test_other.py b/simulaqron/tests/unittests/quick/pythonLib/test_other.py
deleted file mode 100644
index f8f998e..0000000
--- a/simulaqron/tests/unittests/quick/pythonLib/test_other.py
+++ /dev/null
@@ -1,76 +0,0 @@
-#
-# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import time
-import unittest
-
-from cqc.pythonLib import CQCConnection, qubit
-from simulaqron.network import Network
-from simulaqron.settings import simulaqron_settings
-
-
-class OthersTest(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        simulaqron_settings.default_settings()
-        cls.network = Network(nodes=["Alice"], force=True)
-        cls.network.start()
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.network.stop()
-        simulaqron_settings.default_settings()
-
-    def testMeasureInplace(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            q = qubit(cqc)
-            q.H()
-            m1 = q.measure(inplace=True)
-            for _ in range(10):
-                m2 = q.measure(inplace=True)
-                self.assertEqual(m1, m2)
-            q.measure()
-
-    def testGetTime(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            # Test Get time
-            q1 = qubit(cqc)
-            time.sleep(3)
-            q2 = qubit(cqc)
-            t1 = q1.getTime()
-            t2 = q2.getTime()
-            self.assertGreaterEqual(t2 - t1, 3)
-            q1.measure()
-            q2.measure()
-
-
-##################################################################################################
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/simulaqron/tests/unittests/quick/pythonLib/test_sequence.py b/simulaqron/tests/unittests/quick/pythonLib/test_sequence.py
deleted file mode 100644
index 024716b..0000000
--- a/simulaqron/tests/unittests/quick/pythonLib/test_sequence.py
+++ /dev/null
@@ -1,248 +0,0 @@
-#
-# Copyright (c) 2018, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#####################################################################################################
-#
-# main
-#
-import unittest
-
-from cqc.pythonLib import CQCConnection, qubit
-from simulaqron.network import Network
-from simulaqron.settings import simulaqron_settings
-
-
-class sequenceTest(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls._alice = None
-        cls._bob = None
-
-        simulaqron_settings.default_settings()
-        cls.network = Network(nodes=["Alice", "Bob"], force=True)
-        cls.network.start()
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.network.stop()
-        simulaqron_settings.default_settings()
-
-    def testNoSequence(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                res = alice.flush()
-                self.assertEqual(res, [])
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-    def testSingleGates(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                q = qubit(alice)
-                q.X()
-                q.measure(inplace=True)
-                r = alice.flush()
-                self.assertEqual(r[0], 1)
-                q.reset()
-                q.Y()
-                q.measure(inplace=True)
-                r = alice.flush()
-                self.assertEqual(r, [1])
-                q.reset()
-                q.Z()
-                q.measure(inplace=True)
-                r = alice.flush()
-                self.assertEqual(r, [0])
-                q.reset()
-                q.H()
-                q.H()
-                q.measure()
-                r = alice.flush()
-                self.assertEqual(r, [0])
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-    def testSimpleSequence(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                q = qubit(alice)
-                q.H()
-                q.Z()
-                q.H()
-                q.measure()
-                r = alice.flush()[0]
-                self.assertEqual(r, 1)
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-    def testMultipleNewQubits(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                qs = alice.create_qubits(10)
-                self.assertEqual(len(qs), 10)
-                for i in range(1, 10):
-                    self.assertEqual(qs[i]._qID, qs[i - 1]._qID + 1)
-                alice.set_pending(False)
-                for q in qs:
-                    self.assertEqual(q.measure(), 0)
-                alice.set_pending(True)
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-    def testMeasuringMultipleQubits(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                qs = alice.create_qubits(10)
-                for q in qs:
-                    q.measure()
-                ms = alice.flush()
-                self.assertEqual(ms, [0] * 10)
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-    def testCNOT(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                qs = alice.create_qubits(10)
-                qs[0].X()
-                for i in range(1, 10):
-                    qs[i - 1].cnot(qs[i])
-                [q.measure() for q in qs]
-                ms = alice.flush()
-                self.assertEqual(ms, [1] * 10)  # all outcomes should be one
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-    def testCreatingGHZ(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                qs = alice.create_qubits(10)
-                qs[0].H()
-                for i in range(1, 10):
-                    qs[i - 1].cnot(qs[i])
-                [q.measure() for q in qs]
-                ms = alice.flush()
-                self.assertEqual(len(set(ms)), 1)  # all outcomes should be the same
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-    def testAlternating(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                q = qubit(alice)
-                alice.flush()
-                q.X()
-                q.measure(inplace=True)
-                res = alice.flush_factory(10)
-                q.measure()
-                alice.flush()
-                self.assertEqual(res, [1, 0] * 5)
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-    def testMultipleTypes(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                q = qubit(alice)
-                alice.flush()
-                q.X()
-                q.measure(inplace=True)
-                res = alice.flush_factory(8)
-                alice.set_pending(False)
-                q.measure()
-                self.assertEqual(len(res), 8)
-                self.assertEqual(res, [1, 0] * 4)
-                alice.set_pending(True)
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-    def testEPR(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                alice.createEPR(name="Bob", remote_appID=1)
-                bob.recvEPR()
-                qAs = alice.flush_factory(5)
-                qBs = bob.flush_factory(5)
-                alice.set_pending(False)
-                bob.set_pending(False)
-                for i in range(5):
-                    self.assertEqual(qAs[i].measure(), qBs[i].measure())
-                alice.set_pending(True)
-                bob.set_pending(True)
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-    def testSend(self):
-        with CQCConnection("Alice", pend_messages=True) as alice:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                qAs = alice.create_qubits(10)
-
-                for q in qAs:
-                    self.assertTrue(q._active)
-                    alice.sendQubit(q, name="Bob", remote_appID=1)
-                    self.assertFalse(q._active)
-
-                alice.flush()
-                bob.recvQubit()
-                qBs = bob.flush_factory(10)
-
-                for i in range(1, 10):
-                    self.assertEqual(qBs[i - 1]._qID + 1, qBs[i]._qID)
-                bob.set_pending(False)
-                for q in qBs:
-                    self.assertEqual(q.measure(), 0)
-                bob.set_pending(True)
-                self.assertEqual(alice._pending_headers, [])
-                self.assertEqual(bob._pending_headers, [])
-                alice.flush()
-                bob.flush()
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/simulaqron/tests/unittests/quick/stabilizerStates/__init__.py b/simulaqron/tests/unittests/quick/stabilizerStates/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/slow/__init__.py b/simulaqron/tests/unittests/slow/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/slow/merges/__init__.py b/simulaqron/tests/unittests/slow/merges/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/slow/optional_noise/__init__.py b/simulaqron/tests/unittests/slow/optional_noise/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/slow/pythonLib/__init__.py b/simulaqron/tests/unittests/slow/pythonLib/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/simulaqron/tests/unittests/slow/pythonLib/test_factory_gates.py b/simulaqron/tests/unittests/slow/pythonLib/test_factory_gates.py
deleted file mode 100644
index e528293..0000000
--- a/simulaqron/tests/unittests/slow/pythonLib/test_factory_gates.py
+++ /dev/null
@@ -1,603 +0,0 @@
-#
-# Copyright (c) 2018, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import unittest
-import numpy as np
-from scipy.linalg import expm
-
-from cqc.pythonLib import CQCConnection, qubit, CQCUnsuppError
-from simulaqron.settings import simulaqron_settings
-from simulaqron.network import Network
-
-
-def calc_exp_values_single(q):
-    """
-    Calculates the expected value for measurements in the X,Y and Z basis and returns these in a tuple.
-    q should be a numpy array representing a qubit density matrix
-    """
-    # eigenvectors
-    z0 = np.array([[1], [0]])
-    z1 = np.array([[0], [1]])
-    x1 = 1 / np.sqrt(2) * (z0 - z1)
-    y1 = 1 / np.sqrt(2) * (z0 - 1j * z1)
-
-    # projectors
-    P_X1 = np.dot(x1, np.transpose(np.conj(x1)))
-    P_Y1 = np.dot(y1, np.transpose(np.conj(y1)))
-    P_Z1 = np.dot(z1, np.transpose(np.conj(z1)))
-
-    # probabilities
-    d_dag = np.transpose(np.conj(q))
-    p_x = np.real(np.dot(d_dag, np.dot(P_X1, q))[0, 0])
-    p_y = np.real(np.dot(d_dag, np.dot(P_Y1, q))[0, 0])
-    p_z = np.real(np.dot(d_dag, np.dot(P_Z1, q))[0, 0])
-
-    return p_x, p_y, p_z
-
-
-def calc_exp_values_two(q):
-    """
-    Calculates the expected value for measurements in the X,Y and Z basis and returns these in a tuple.
-    q should be a numpy array representing a qubit density matrix
-    """
-    # eigenvectors
-    z0 = np.array([[1], [0]])
-    z1 = np.array([[0], [1]])
-    x1 = 1 / np.sqrt(2) * (z0 - z1)
-    y1 = 1 / np.sqrt(2) * (z0 - 1j * z1)
-
-    # projectors
-    P_X1 = np.dot(x1, np.transpose(np.conj(x1)))
-    P_Y1 = np.dot(y1, np.transpose(np.conj(y1)))
-    P_Z1 = np.dot(z1, np.transpose(np.conj(z1)))
-
-    # probabilities
-    p_x = np.real(np.trace(np.dot(P_X1, q)))
-    p_y = np.real(np.trace(np.dot(P_Y1, q)))
-    p_z = np.real(np.trace(np.dot(P_Z1, q)))
-
-    return p_x, p_y, p_z
-
-
-def prep_I_CQC_FACTORY(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.I()
-    cqc.flush_factory(3)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_I_state():
-    q = np.array([[1], [0]])
-    return q
-
-
-def prep_X_CQC_FACTORY_ODD(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.X()
-    cqc.flush_factory(3)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_X_CQC_FACTORY_EVEN(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.X()
-    cqc.flush_factory(4)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_X_state():
-    q = np.array([[0], [1]])
-    return q
-
-
-def prep_Y_CQC_FACTORY_ODD(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.Y()
-    cqc.flush_factory(3)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_Y_CQC_FACTORY_EVEN(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.Y()
-    cqc.flush_factory(4)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_Y_state():
-    q = np.array([[0], [1j]])
-    return q
-
-
-def prep_Z_CQC_FACTORY_ODD(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.Z()
-    cqc.flush_factory(3)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_Z_CQC_FACTORY_EVEN(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.Z()
-    cqc.flush_factory(4)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_Z_state():
-    q = np.array([[1], [0]])
-    return q
-
-
-def prep_T_CQC_FACTORY_QUARTER(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.T()
-    cqc.flush_factory(5)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_T_CQC_FACTORY_HALF(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.T()
-    cqc.flush_factory(6)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_T_CQC_FACTORY_THREE_QUARTER(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.T()
-    cqc.flush_factory(7)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_T_CQC_FACTORY_FULL(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.T()
-    cqc.flush_factory(8)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_T_state(amount):
-    q = np.array([[1], [0]])
-    T = np.array([[1, 0], [0, np.exp(amount * np.pi / 4)]])
-    return np.dot(T, q)
-
-
-def prep_H_CQC_FACTORY_ODD(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.H()
-    cqc.flush_factory(3)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_H_CQC_FACTORY_EVEN(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.H()
-    cqc.flush_factory(4)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_H_state():
-    q = np.array([[1], [1]]) / np.sqrt(2)
-    return q
-
-
-def prep_K_CQC_FACTORY_ODD(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.K()
-    cqc.flush_factory(3)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_K_CQC_FACTORY_EVEN(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.K()
-    cqc.flush_factory(4)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_K_state():
-    q = np.array([[1], [1j]]) / np.sqrt(2)
-    return q
-
-
-def prep_ROT_X(cqc):
-    q = qubit(cqc)
-    cqc.set_pending(True)
-    q.rot_X(step=4)
-    cqc.flush_factory(4)
-    cqc.set_pending(False)
-    return q
-
-
-def prep_ROT_X_state():
-    theta = np.pi / 8
-    return prep_rot_state([1, 0, 0], theta)
-
-
-def prep_rot_state(n, a):
-    q = np.array([[1], [0]])
-    nNorm = np.linalg.norm(n)
-    X = np.array([[0, 1], [1, 0]])
-    Y = np.array([[0, -1j], [1j, 0]])
-    Z = np.array([[1, 0], [0, -1]])
-    R = expm(-1j * a / (2 * nNorm) * (n[0] * X + n[1] * Y + n[2] * Z))
-    return R * q
-
-
-def prep_mixed_state():
-    q = np.eye(2) / 2
-    return q
-
-
-def prep_CNOT_control_CQC_FACTORY_even(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
-    q1.H()
-    cqc.set_pending(True)
-    q1.cnot(q2)
-    cqc.flush_factory(4)
-    cqc.set_pending(False)
-    q2.measure()
-    return q1
-
-
-def prep_CNOT_control_CQC_FACTORY_odd(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
-    q1.H()
-    cqc.set_pending(True)
-    q1.cnot(q2)
-    cqc.flush_factory(3)
-    cqc.set_pending(False)
-    q2.measure()
-    return q1
-
-
-def prep_CNOT_target_CQC_FACTORY_even(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
-    q1.H()
-    cqc.set_pending(True)
-    q1.cnot(q2)
-    cqc.flush_factory(4)
-    cqc.set_pending(False)
-    q1.measure()
-    return q2
-
-
-def prep_CNOT_target_CQC_FACTORY_odd(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
-    q1.H()
-    cqc.set_pending(True)
-    q1.cnot(q2)
-    cqc.flush_factory(3)
-    cqc.set_pending(False)
-    q1.measure()
-    return q2
-
-
-def prep_CPHASE_control_CQC_FACTORY_even(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
-    q1.H()
-    q2.H()
-    cqc.set_pending(True)
-    q1.cphase(q2)
-    cqc.flush_factory(4)
-    cqc.set_pending(False)
-    q2.H()
-    q2.measure()
-    return q1
-
-
-def prep_CPHASE_control_CQC_FACTORY_odd(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
-    q1.H()
-    q2.H()
-    cqc.set_pending(True)
-    q1.cphase(q2)
-    cqc.flush_factory(3)
-    cqc.set_pending(False)
-    q2.H()
-    q2.measure()
-    return q1
-
-
-def prep_CPHASE_target_CQC_FACTORY_even(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
-    q1.H()
-    q2.H()
-    cqc.set_pending(True)
-    q1.cphase(q2)
-    cqc.flush_factory(4)
-    cqc.set_pending(False)
-    q2.H()
-    q1.measure()
-    return q2
-
-
-def prep_CPHASE_target_CQC_FACTORY_odd(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
-    q1.H()
-    q2.H()
-    cqc.set_pending(True)
-    q1.cphase(q2)
-    cqc.flush_factory(3)
-    cqc.set_pending(False)
-    q2.H()
-    q1.measure()
-    return q2
-
-
-class FactoryGateTest(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls.iterations = 100
-        print("Testing factory gates with {} iterations \r\n".format(cls.iterations))
-
-        simulaqron_settings.default_settings()
-        cls.network = Network(force=True)
-        cls.network.start()
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.network.stop()
-        simulaqron_settings.default_settings()
-
-    def testIFactory(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            # Test I factory
-            print("Testing I factory:")
-            exp_values = calc_exp_values_single(prep_I_state())
-            ans = cqc.test_preparation(prep_I_CQC_FACTORY, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-    def testXFactory(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            print("Testing X factory (odd):")
-            exp_values = calc_exp_values_single(prep_X_state())
-            ans = cqc.test_preparation(prep_X_CQC_FACTORY_ODD, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test X factory even
-            print("Testing X factory (even):")
-            exp_values = calc_exp_values_single(prep_I_state())
-            ans = cqc.test_preparation(prep_X_CQC_FACTORY_EVEN, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-    def testYFactory(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            # Test Y factory odd
-            print("Testing Y factory (odd):")
-            exp_values = calc_exp_values_single(prep_Y_state())
-            ans = cqc.test_preparation(prep_Y_CQC_FACTORY_ODD, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test Y factory even
-            print("Testing Y factory (even):")
-            exp_values = calc_exp_values_single(prep_I_state())
-            ans = cqc.test_preparation(prep_Y_CQC_FACTORY_EVEN, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-    def testZFactory(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            # Test Z factory odd
-            print("Testing Z factory (odd):")
-            exp_values = calc_exp_values_single(prep_Z_state())
-            ans = cqc.test_preparation(prep_Z_CQC_FACTORY_ODD, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test Z factory even
-            print("Testing Z factory (even):")
-            exp_values = calc_exp_values_single(prep_I_state())
-            ans = cqc.test_preparation(prep_Z_CQC_FACTORY_EVEN, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-    def testTFactory(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            # Test T factory quarter
-            print("Testing T factory (quarter):")
-            exp_values = calc_exp_values_single(prep_T_state(1))
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(
-                        prep_T_CQC_FACTORY_QUARTER, exp_values, iterations=self.iterations, progress=False
-                    )
-            else:
-                ans = cqc.test_preparation(prep_T_CQC_FACTORY_QUARTER, exp_values, iterations=self.iterations)
-                self.assertTrue(ans)
-
-            # Test T factory half
-            print("Testing T factory (half):")
-            exp_values = calc_exp_values_single(prep_T_state(2))
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(
-                        prep_T_CQC_FACTORY_HALF, exp_values, iterations=self.iterations, progress=False
-                    )
-            else:
-                ans = cqc.test_preparation(prep_T_CQC_FACTORY_HALF, exp_values, iterations=self.iterations)
-                self.assertTrue(ans)
-
-            # Test T factory half
-            print("Testing T factory (three quarters):")
-            exp_values = calc_exp_values_single(prep_T_state(3))
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(
-                        prep_T_CQC_FACTORY_THREE_QUARTER, exp_values, iterations=self.iterations, progress=False
-                    )
-            else:
-                ans = cqc.test_preparation(prep_T_CQC_FACTORY_THREE_QUARTER, exp_values, iterations=self.iterations)
-                self.assertTrue(ans)
-
-            # Test T factory half
-            print("Testing T factory (full):")
-            exp_values = calc_exp_values_single(prep_I_state())
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(
-                        prep_T_CQC_FACTORY_FULL, exp_values, iterations=self.iterations, progress=False
-                    )
-            else:
-                ans = cqc.test_preparation(prep_T_CQC_FACTORY_FULL, exp_values, iterations=self.iterations)
-                self.assertTrue(ans)
-
-    def testHFactory(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            # Test H factory odd
-            print("Testing H factory (odd):")
-            exp_values = calc_exp_values_single(prep_H_state())
-            ans = cqc.test_preparation(prep_H_CQC_FACTORY_ODD, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test H factory even
-            print("Testing H factory (even):")
-            exp_values = calc_exp_values_single(prep_I_state())
-            ans = cqc.test_preparation(prep_H_CQC_FACTORY_EVEN, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-    def testKFactory(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            # Test K factory odd
-            print("Testing K factory (odd):")
-            exp_values = calc_exp_values_single(prep_K_state())
-            ans = cqc.test_preparation(prep_K_CQC_FACTORY_ODD, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test K factory even
-            print("Testing K factory (even):")
-            exp_values = calc_exp_values_single(prep_I_state())
-            ans = cqc.test_preparation(prep_K_CQC_FACTORY_EVEN, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-    def testRot_X_Factory(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            # Test ROT_X factory pi/8
-            # TODO, add these tests when decided that we make it possible to do this
-            # (As of writing at 2018/03/27 the angle of rotation is (up to a factor 2pi/256)
-            # To the amount of times this rotation is done
-            print("Testing CNOT rotation of 4 times 1/32:")
-            exp_values = calc_exp_values_single(prep_ROT_X_state())
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(prep_ROT_X, exp_values, iterations=self.iterations, progress=False)
-            else:
-                ans = cqc.test_preparation(prep_ROT_X, exp_values, iterations=self.iterations)
-                self.assertTrue(ans)
-
-    def testCNOTFactory(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            # Test CNOT Factory Control even
-            print("Testing CNOT factory control even:")
-            exp_values = calc_exp_values_single(prep_H_state())
-            ans = cqc.test_preparation(prep_CNOT_control_CQC_FACTORY_even, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test CNOT Factory Control odd
-            print("Testing CNOT factory control odd:")
-            exp_values = calc_exp_values_two(prep_mixed_state())
-            ans = cqc.test_preparation(prep_CNOT_control_CQC_FACTORY_odd, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test CNOT Factory target even
-            print("Testing CNOT factory target even:")
-            exp_values = calc_exp_values_single(prep_I_state())
-            ans = cqc.test_preparation(prep_CNOT_target_CQC_FACTORY_even, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test CNOT Factory target odd
-            print("Testing CNOT factory target odd:")
-            exp_values = calc_exp_values_two(prep_mixed_state())
-            ans = cqc.test_preparation(prep_CNOT_target_CQC_FACTORY_odd, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-    def testCPhaseFactory(self):
-        with CQCConnection("Alice", appID=1) as cqc:
-            # Test CPHASE Factory Control even
-            print("Testing CPHASE factory control even:")
-            exp_values = calc_exp_values_single(prep_H_state())
-            ans = cqc.test_preparation(prep_CPHASE_control_CQC_FACTORY_even, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test CPHASE Factory Control odd
-            print("Testing CPHASE factory control odd:")
-            exp_values = calc_exp_values_two(prep_mixed_state())
-            ans = cqc.test_preparation(prep_CPHASE_control_CQC_FACTORY_odd, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test CPHASE Factory target even
-            print("Testing CPHASE factory target even:")
-            exp_values = calc_exp_values_single(prep_I_state())
-            ans = cqc.test_preparation(prep_CPHASE_target_CQC_FACTORY_even, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-            # Test CPHASE Factory target odd
-            print("Testing CPHASE factory target odd:")
-            exp_values = calc_exp_values_two(prep_mixed_state())
-            ans = cqc.test_preparation(prep_CPHASE_target_CQC_FACTORY_odd, exp_values, iterations=self.iterations)
-            self.assertTrue(ans)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/simulaqron/tests/unittests/slow/pythonLib/test_factory_other.py b/simulaqron/tests/unittests/slow/pythonLib/test_factory_other.py
deleted file mode 100644
index 85adaf0..0000000
--- a/simulaqron/tests/unittests/slow/pythonLib/test_factory_other.py
+++ /dev/null
@@ -1,163 +0,0 @@
-#
-# Copyright (c) 2018, Stephanie Wehner and Axel Dahlberg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#    This product includes software developed by Stephanie Wehner, QuTech.
-# 4. Neither the name of the QuTech organization nor the
-#    names of its contributors may be used to endorse or promote products
-#    derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#####################################################################################################
-#
-# main
-#
-import unittest
-
-from cqc.pythonLib import CQCConnection, CQCNoQubitError, qubit
-from simulaqron.network import Network
-from simulaqron.settings import simulaqron_settings
-
-
-class CQCFactoryTest(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls.iterations = 8
-
-        simulaqron_settings.default_settings()
-        cls.network = Network(nodes=["Alice", "Bob"], force=True)
-        cls.network.start()
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.network.stop()
-        simulaqron_settings.default_settings()
-
-    def testNew(self):
-        with CQCConnection("Alice", appID=0, pend_messages=True) as cqc:
-            qubits = cqc.create_qubits(self.iterations)
-            self.assertEqual(len(qubits), self.iterations)
-            for q in qubits:
-                q.X()
-                q.measure()
-            results = cqc.flush()
-            self.assertEqual(results, [1] * self.iterations)
-            self.assertEqual(len(cqc._pending_headers), 0)
-
-    def testMeasure(self):
-        with CQCConnection("Alice", appID=0, pend_messages=True) as cqc:
-            q = qubit(cqc)
-            q.X()  # Let's do an X so all measurement outcomes should be 1
-            # (to show no reinitialisation)
-            cqc.flush()
-            q.measure()
-            with self.assertRaises(CQCNoQubitError):
-                cqc.flush_factory(self.iterations)
-            self.assertFalse(q._active)
-            self.assertEqual(len(cqc._pending_headers), 0)
-
-    def testMeasureInplace(self):
-        with CQCConnection("Alice", appID=0, pend_messages=True) as cqc:
-            q = qubit(cqc)
-            q.X()  # Let's do an X so all measurement outcomes should be 1
-            # (to show no reinitialisation)
-            cqc.flush()
-            q.measure(inplace=True)
-            m = cqc.flush_factory(self.iterations)
-            self.assertEqual(len(m), self.iterations)
-            self.assertTrue(x == 1 for x in m)
-            q.measure()
-            cqc.flush()
-            self.assertEqual(len(cqc._pending_headers), 0)
-
-    def testReset(self):
-        with CQCConnection("Alice", appID=0, pend_messages=True) as cqc:
-            q1 = qubit(cqc)
-            cqc.flush()
-            q1.X()
-            q1.reset()
-            cqc.flush_factory(self.iterations)
-            q1.measure()
-            m = cqc.flush()
-            self.assertEqual(m, [0])
-            self.assertEqual(len(cqc._pending_headers), 0)
-
-    def testSend(self):
-        with CQCConnection("Alice", appID=0, pend_messages=True) as cqc:
-            q = qubit(cqc)
-            q.X()
-            cqc.flush()
-            with CQCConnection("Bob", appID=1) as bob:
-                # Get receiving host
-                cqc.sendQubit(q, "Bob", remote_appID=1)
-                with self.assertRaises(CQCNoQubitError):
-                    cqc.flush_factory(self.iterations)
-                qB = bob.recvQubit()
-                self.assertTrue(qB.measure(), 1)
-                self.assertFalse(q._active)
-            self.assertEqual(len(cqc._pending_headers), 0)
-
-    def testRecv(self):
-        with CQCConnection("Alice", appID=0, pend_messages=True) as cqc:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                for _ in range(self.iterations):
-                    q = qubit(bob)
-                    q.X()
-                    bob.sendQubit(q, "Alice", remote_appID=0)
-                    bob.flush()
-                cqc.recvQubit()
-                qubits = cqc.flush_factory(self.iterations)
-                self.assertEqual(self.iterations, len(qubits))
-                for q in qubits:
-                    self.assertTrue(q._active)
-                    q.X()
-                    q.measure()
-                f = cqc.flush(self.iterations)
-                self.assertEqual([0] * self.iterations, f)
-            self.assertEqual(len(cqc._pending_headers), 0)
-
-    def testEPR(self):
-        with CQCConnection("Alice", appID=0, pend_messages=True) as cqc:
-            with CQCConnection("Bob", appID=1, pend_messages=True) as bob:
-                cqc.createEPR("Bob", 1)
-                bob.recvEPR()
-
-                it = int(self.iterations / 2)
-
-                qubitsAlice = cqc.flush_factory(it)
-                qubitsBob = bob.flush_factory(it)
-                self.assertEqual(len(qubitsBob), it)
-                self.assertEqual(len(qubitsAlice), len(qubitsBob))
-                for i in range(it):
-                    # Each pair should have the same measurement outcomes
-                    # if measured in the same basis, test this
-                    qubitsAlice[i].measure()
-                    qubitsBob[i].measure()
-                mAlice = cqc.flush()
-                mBob = bob.flush()
-                self.assertEqual(len(mAlice), it)
-                self.assertEqual(mAlice, mBob)
-            self.assertEqual(len(cqc._pending_headers), 0)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/simulaqron/tests_run.py b/simulaqron/tests_run.py
deleted file mode 100644
index 24249be..0000000
--- a/simulaqron/tests_run.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import os
-import unittest
-from argparse import ArgumentParser
-import logging
-
-
-logger = logging.getLogger()
-logger.setLevel(logging.CRITICAL)
-
-
-def main(test_type="quick"):
-
-    path_to_here = os.path.dirname(os.path.abspath(__file__))
-    if test_type == "quick":
-        directory = os.path.join(path_to_here, "tests", "unittests", "quick")
-    elif test_type == "full":
-        directory = os.path.join(path_to_here, "tests", "unittests")
-    elif test_type == "network":
-        directory = os.path.join(path_to_here, "tests", "unittests", "quick", "network")
-    else:
-        raise ValueError("Unknown test type {}".format(test_type))
-
-    test_loader = unittest.defaultTestLoader
-    test_runner = unittest.TextTestRunner()
-    test_suite = test_loader.discover(start_dir=directory)
-    result = test_runner.run(test_suite)
-
-    return len(result.errors) == 0 and len(result.failures) == 0
-
-
-def parse_args():
-    parser = ArgumentParser()
-
-    parser.add_argument('--quick', default=False, action='store_true')
-    parser.add_argument('--full', default=False, action='store_true')
-
-    args = parser.parse_args()
-    if args.full:
-        return "full"
-    else:
-        return "quick"
-
-
-if __name__ == '__main__':
-    test_type = parse_args()
-    success = main(test_type=test_type)
-    exit(not success)
diff --git a/test_requirements.txt b/test_requirements.txt
new file mode 100644
index 0000000..822eca3
--- /dev/null
+++ b/test_requirements.txt
@@ -0,0 +1,1 @@
+flake8>=3.6.0,<4.0.0
diff --git a/simulaqron/tests/unittests/quick/engine/test_projectQEngine.py b/tests/quick/engine/test_project_q_engine.py
similarity index 89%
rename from simulaqron/tests/unittests/quick/engine/test_projectQEngine.py
rename to tests/quick/engine/test_project_q_engine.py
index 350a041..90742a5 100644
--- a/simulaqron/tests/unittests/quick/engine/test_projectQEngine.py
+++ b/tests/quick/engine/test_project_q_engine.py
@@ -2,13 +2,14 @@
 import numpy as np
 
 from simulaqron.toolbox import has_module
+from simulaqron.settings import SimBackend
 
-if has_module.main("projectq"):
+if has_module.main(SimBackend.PROJECTQ.value):
 
-    from simulaqron.virtNode.projectQSimulator import projectQEngine
-    from simulaqron.virtNode.basics import noQubitError, quantumError
+    from simulaqron.virtual_node.project_q_simulator import projectQEngine
+    from simulaqron.virtual_node.basics import noQubitError, quantumError
 
-    from projectq.types._qubit import Qureg
+    from projectq.types._qubit import Qubit
 
     _has_module = True
 
@@ -56,7 +57,7 @@ def test_add_fresh_qubit(self):
         self.assertEqual(num, 0)
         self.assertEqual(self.eng.activeQubits, 1)
         self.assertEqual(len(self.eng.qubitReg), 1)
-        self.assertTrue(isinstance(self.eng.qubitReg[num], Qureg))
+        self.assertTrue(isinstance(self.eng.qubitReg[num], Qubit))
 
     @if_has_module
     def test_add_to_many_fresh_qubits(self):
@@ -72,7 +73,7 @@ def test_add_qubit(self):
         self.assertEqual(num, 0)
         self.assertEqual(self.eng.activeQubits, 1)
         self.assertEqual(len(self.eng.qubitReg), 1)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         self.assertAlmostEqual(self.abs_inner_product(state, new_state), 1)
 
     @if_has_module
@@ -82,7 +83,7 @@ def test_add_qubit_H(self):
         self.assertEqual(num, 0)
         self.assertEqual(self.eng.activeQubits, 1)
         self.assertEqual(len(self.eng.qubitReg), 1)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         self.assertAlmostEqual(self.abs_inner_product(state, new_state), 1)
 
     @if_has_module
@@ -104,28 +105,28 @@ def test_remove_qubit(self):
     def test_get_register_RI(self):
         self.eng.add_fresh_qubit()
         self.eng.add_fresh_qubit()
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         self.assertAlmostEqual(self.abs_inner_product(state, [1, 0, 0, 0]), 1)
 
     @if_has_module
     def test_H(self):
         num = self.eng.add_fresh_qubit()
         self.eng.apply_H(num)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         self.assertAlmostEqual(self.abs_inner_product(state, [1 / np.sqrt(2), 1 / np.sqrt(2)]), 1)
 
     @if_has_module
     def test_K(self):
         num = self.eng.add_fresh_qubit()
         self.eng.apply_K(num)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         self.assertAlmostEqual(self.abs_inner_product(state, [1 / np.sqrt(2), 1j / np.sqrt(2)]), 1)
 
     @if_has_module
     def test_X(self):
         num = self.eng.add_fresh_qubit()
         self.eng.apply_X(num)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         self.assertAlmostEqual(self.abs_inner_product(state, [0, 1]), 1)
 
     @if_has_module
@@ -133,7 +134,7 @@ def test_Y(self):
         num = self.eng.add_fresh_qubit()
         self.eng.apply_H(num)
         self.eng.apply_Y(num)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [-1j / np.sqrt(2), 1j / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -142,7 +143,7 @@ def test_Z(self):
         num = self.eng.add_fresh_qubit()
         self.eng.apply_H(num)
         self.eng.apply_Z(num)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2), -1 / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -150,7 +151,7 @@ def test_Z(self):
     def test_Rx(self):
         num = self.eng.add_fresh_qubit()
         self.eng.apply_rotation(num, (1, 0, 0), np.pi / 2)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2), -1j / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -158,7 +159,7 @@ def test_Rx(self):
     def test_Ry(self):
         num = self.eng.add_fresh_qubit()
         self.eng.apply_rotation(num, (0, 1, 0), np.pi / 2)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2), 1 / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -167,7 +168,7 @@ def test_Rz(self):
         num = self.eng.add_fresh_qubit()
         self.eng.apply_H(num)
         self.eng.apply_rotation(num, (0, 0, 1), np.pi / 2)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2), 1j / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -184,7 +185,7 @@ def test_cnot(self):
         num2 = self.eng.add_fresh_qubit()
         self.eng.apply_H(num1)
         self.eng.apply_CNOT(num1, num2)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -195,7 +196,7 @@ def test_cz(self):
         self.eng.apply_H(num1)
         self.eng.apply_H(num2)
         self.eng.apply_CPHASE(num1, num2)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / 2, 1 / 2, 1 / 2, -1 / 2]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -236,7 +237,7 @@ def test_absorb_other_empty(self):
         self.eng.absorb(eng2)
         self.assertEqual(self.eng.activeQubits, 1)
         self.assertEqual(len(self.eng.qubitReg), 1)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2), 1 / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -248,7 +249,7 @@ def test_absorb_this_empty_H(self):
         self.eng.absorb(eng2)
         self.assertEqual(self.eng.activeQubits, 1)
         self.assertEqual(len(self.eng.qubitReg), 1)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2), 1 / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -262,7 +263,7 @@ def test_absorb_this_empty_CNOT(self):
         self.eng.absorb(eng2)
         self.assertEqual(self.eng.activeQubits, 2)
         self.assertEqual(len(self.eng.qubitReg), 2)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -277,7 +278,7 @@ def test_absorb_this_empty_GHZ(self):
         self.eng.absorb(eng2)
         self.assertEqual(self.eng.activeQubits, n)
         self.assertEqual(len(self.eng.qubitReg), n)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2)] + [0] * (2 ** n - 2) + [1 / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -326,7 +327,7 @@ def test_absorb_parts(self):
         self.eng.absorb_parts(*eng2.get_register_RI(), eng2.activeQubits)
         self.assertEqual(self.eng.activeQubits, 2)
         self.assertEqual(len(self.eng.qubitReg), 2)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1, 0, 0, 0]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -340,7 +341,7 @@ def test_absorb_parts_EPR(self):
         self.eng.absorb_parts(*eng2.get_register_RI(), eng2.activeQubits)
         self.assertEqual(self.eng.activeQubits, 2)
         self.assertEqual(len(self.eng.qubitReg), 2)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
@@ -352,7 +353,7 @@ def test_absorb_parts_other_empty(self):
         self.eng.absorb_parts(*eng2.get_register_RI(), eng2.activeQubits)
         self.assertEqual(self.eng.activeQubits, 1)
         self.assertEqual(len(self.eng.qubitReg), 1)
-        state = self.eng.get_register_RI()
+        state = self.eng.get_register_RI()[1]
         ref = [1 / np.sqrt(2), 1 / np.sqrt(2)]
         self.assertAlmostEqual(self.abs_inner_product(state, ref), 1)
 
diff --git a/simulaqron/tests/unittests/quick/engine/test_qutipEngine.py b/tests/quick/engine/test_qutip_engine.py
similarity index 95%
rename from simulaqron/tests/unittests/quick/engine/test_qutipEngine.py
rename to tests/quick/engine/test_qutip_engine.py
index 806d579..685e856 100644
--- a/simulaqron/tests/unittests/quick/engine/test_qutipEngine.py
+++ b/tests/quick/engine/test_qutip_engine.py
@@ -30,10 +30,11 @@
 import unittest
 
 from simulaqron.toolbox import has_module
+from simulaqron.settings import SimBackend
 
-if has_module.main("qutip"):
+if has_module.main(SimBackend.QUTIP.value):
 
-    from simulaqron.virtNode.qutipSimulator import qutipEngine
+    from simulaqron.virtual_node.qutip_simulator import qutipEngine
 
     _has_module = True
 
diff --git a/simulaqron/tests/unittests/quick/engine/test_stabilizerEngine.py b/tests/quick/engine/test_stabilizer_engine.py
similarity index 96%
rename from simulaqron/tests/unittests/quick/engine/test_stabilizerEngine.py
rename to tests/quick/engine/test_stabilizer_engine.py
index 722c7ad..5b1f449 100644
--- a/simulaqron/tests/unittests/quick/engine/test_stabilizerEngine.py
+++ b/tests/quick/engine/test_stabilizer_engine.py
@@ -1,9 +1,10 @@
 import unittest
 import numpy as np
 
-from simulaqron.virtNode.stabilizerSimulator import stabilizerEngine
-from simulaqron.virtNode.basics import noQubitError, quantumError
-from simulaqron.toolbox.stabilizerStates import StabilizerState
+from simulaqron.virtual_node.stabilizer_simulator import stabilizerEngine
+from simulaqron.virtual_node.basics import noQubitError, quantumError
+from simulaqron.toolbox.stabilizer_states import StabilizerState
+from simulaqron.general import SimUnsupportedError
 
 
 class TestStabilizerEngine_init(unittest.TestCase):
@@ -101,17 +102,17 @@ def test_Z(self):
 
     def test_Rx(self):
         num = self.eng.add_fresh_qubit()
-        with self.assertRaises(AttributeError):
+        with self.assertRaises(SimUnsupportedError):
             self.eng.apply_rotation(num, (1, 0, 0), np.pi / 2)
 
     def test_Ry(self):
         num = self.eng.add_fresh_qubit()
-        with self.assertRaises(AttributeError):
+        with self.assertRaises(SimUnsupportedError):
             self.eng.apply_rotation(num, (0, 1, 0), np.pi / 2)
 
     def test_Rz(self):
         num = self.eng.add_fresh_qubit()
-        with self.assertRaises(AttributeError):
+        with self.assertRaises(SimUnsupportedError):
             self.eng.apply_rotation(num, (0, 0, 1), np.pi / 2)
 
     def test_cnot(self):
diff --git a/simulaqron/tests/unittests/quick/general/resources/network.json b/tests/quick/general/resources/network.json
similarity index 75%
rename from simulaqron/tests/unittests/quick/general/resources/network.json
rename to tests/quick/general/resources/network.json
index 5ddd486..7844a94 100644
--- a/simulaqron/tests/unittests/quick/general/resources/network.json
+++ b/tests/quick/general/resources/network.json
@@ -6,13 +6,13 @@
 			"Alice":
 			{
 				"app_socket": ["localhost", 8000],
-				"cqc_socket": ["localhost", 8001],
+				"qnodeos_socket": ["localhost", 8001],
 				"vnode_socket": ["localhost", 8002]
 			},
 			"Bob":
 			{
 				"app_socket": ["localhost", 8003],
-				"cqc_socket": ["localhost", 8004],
+				"qnodeos_socket": ["localhost", 8004],
 				"vnode_socket": ["localhost", 8005]
 			}
 		},
diff --git a/simulaqron/tests/unittests/quick/general/resources/sockets.cfg b/tests/quick/general/resources/sockets.cfg
similarity index 100%
rename from simulaqron/tests/unittests/quick/general/resources/sockets.cfg
rename to tests/quick/general/resources/sockets.cfg
diff --git a/simulaqron/tests/unittests/quick/general/resources/test.json b/tests/quick/general/resources/test.json
similarity index 91%
rename from simulaqron/tests/unittests/quick/general/resources/test.json
rename to tests/quick/general/resources/test.json
index 3af2d69..ea36bb0 100644
--- a/simulaqron/tests/unittests/quick/general/resources/test.json
+++ b/tests/quick/general/resources/test.json
@@ -6,7 +6,7 @@
                     "localhost",
                     8000
                 ],
-                "cqc_socket": [
+                "qnodeos_socket": [
                     "localhost",
                     8001
                 ],
@@ -20,7 +20,7 @@
                     "localhost",
                     8003
                 ],
-                "cqc_socket": [
+                "qnodeos_socket": [
                     "localhost",
                     8004
                 ],
@@ -39,7 +39,7 @@
                     "localhost",
                     8006
                 ],
-                "cqc_socket": [
+                "qnodeos_socket": [
                     "localhost",
                     8007
                 ],
diff --git a/simulaqron/tests/unittests/quick/general/test_hostConfig.py b/tests/quick/general/test_host_config.py
similarity index 87%
rename from simulaqron/tests/unittests/quick/general/test_hostConfig.py
rename to tests/quick/general/test_host_config.py
index 4ad6533..8142987 100644
--- a/simulaqron/tests/unittests/quick/general/test_hostConfig.py
+++ b/tests/quick/general/test_host_config.py
@@ -1,7 +1,7 @@
 import unittest
 import os
 
-from simulaqron.general.hostConfig import NetworksConfigConstructor, socketsConfig
+from simulaqron.general.host_config import NetworksConfigConstructor, SocketsConfig
 
 PATH_TO_HERE = os.path.abspath(os.path.dirname(__file__))
 
@@ -30,10 +30,10 @@ def test_read_write(self):
 class TestSocketsConfig(unittest.TestCase):
     def test_load_file(self):
         file_path1 = os.path.join(PATH_TO_HERE, "resources", "sockets.cfg")
-        conf1 = socketsConfig(file_path1)
+        conf1 = SocketsConfig(file_path1)
 
         file_path2 = os.path.join(PATH_TO_HERE, "resources", "network.json")
-        conf2 = socketsConfig(file_path2, config_type="cqc")
+        conf2 = SocketsConfig(file_path2, config_type="qnodeos")
 
         for node_name, host in conf1.hostDict.items():
             self.assertEqual(host.port, conf2.hostDict[node_name].port)
diff --git a/simulaqron/tests/unittests/quick/network/test_Network.py b/tests/quick/network/test_network.py
similarity index 97%
rename from simulaqron/tests/unittests/quick/network/test_Network.py
rename to tests/quick/network/test_network.py
index 0dd767d..b609f82 100644
--- a/simulaqron/tests/unittests/quick/network/test_Network.py
+++ b/tests/quick/network/test_network.py
@@ -87,7 +87,7 @@ def test_start(self):
         self.assertEqual(len(network.processes), 2 * len(self.nodes))
         for p in network.processes:
             self.assertFalse(p.is_alive())
-        network.start()
+        network.start(wait_until_running=True)
         self.assertTrue(network.running)
         for p in network.processes:
             self.assertTrue(p.is_alive())
@@ -100,7 +100,7 @@ def test_stop(self):
 
     def test_start_stop(self):
         network = Network(force=True)
-        network.start()
+        network.start(wait_until_running=True)
         for p in network.processes:
             self.assertTrue(p.is_alive())
         network.stop()
@@ -125,7 +125,7 @@ def test_no_wait(self):
 
     def test_del(self):
         network = Network(force=True)
-        network.start()
+        network.start(wait_until_running=True)
         del network
 
 
diff --git a/tests/quick/network_topology/test_restricted_topology.py b/tests/quick/network_topology/test_restricted_topology.py
new file mode 100644
index 0000000..36df702
--- /dev/null
+++ b/tests/quick/network_topology/test_restricted_topology.py
@@ -0,0 +1,69 @@
+import unittest
+import logging
+from functools import partial
+
+from netqasm.logging.glob import set_log_level
+from netqasm.sdk import EPRSocket
+from netqasm.runtime.app_config import default_app_config
+
+from simulaqron.network import Network
+from simulaqron.settings import simulaqron_settings
+from simulaqron.sdk.connection import SimulaQronConnection
+from simulaqron.run import run_applications
+
+
+@unittest.skip("Restricted topology is not yet supported in the new version of simulaqron")
+class TestRestrictedTopology(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        simulaqron_settings.default_settings()
+        simulaqron_settings._read_user = False
+        simulaqron_settings.log_level = logging.CRITICAL
+        nodes = ["Alice", "Bob", "Charlie"]
+        cls.network = Network(nodes=nodes, topology="path", force=True)
+        cls.network.start()
+
+        cls.edges = [("Alice", "Bob"), ("Bob", "Charlie")]
+        cls.non_edges = [(node, node) for node in nodes] + [("Alice", "Charlie")]
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.network.stop()
+        simulaqron_settings.default_settings()
+
+    def test_EPR(self):
+        outcomes = []
+
+        def create_func(name, remote_name):
+            epr_socket = EPRSocket(remote_name)
+            with SimulaQronConnection(name, epr_sockets=[epr_socket]):
+                q = epr_socket.create()[0]
+                m = q.measure()
+                outcomes.append(m)
+
+        def recv_func(name, remote_name):
+            epr_socket = EPRSocket(remote_name)
+            with SimulaQronConnection(name, epr_sockets=[epr_socket]):
+                q = epr_socket.create()[0]
+                m = q.measure()
+                outcomes.append(m)
+
+        for create_name, recv_name in self.edges:
+            run_applications([
+                default_app_config(create_name, partial(create_func, create_name, recv_name)),
+                default_app_config(recv_name, partial(create_func, recv_name, create_name)),
+            ], use_app_config=False)
+            self.assertEqual(sum(outcomes) % 2, 0)
+            return
+
+        for sender_name, receiver_name in self.non_edges:
+            with self.assertRaises(RuntimeError):  # TODO correct error
+                run_applications([
+                    default_app_config(create_name, partial(create_func, create_name, recv_name)),
+                    default_app_config(recv_name, partial(create_func, recv_name, create_name)),
+                ], use_app_config=False)
+
+
+if __name__ == "__main__":
+    set_log_level("DEBUG")
+    unittest.main()
diff --git a/simulaqron/tests/unittests/quick/stabilizerStates/test_stabilizerStates.py b/tests/quick/stabilizer_states/test_stabilizer_states.py
similarity index 99%
rename from simulaqron/tests/unittests/quick/stabilizerStates/test_stabilizerStates.py
rename to tests/quick/stabilizer_states/test_stabilizer_states.py
index bb7839c..4268592 100644
--- a/simulaqron/tests/unittests/quick/stabilizerStates/test_stabilizerStates.py
+++ b/tests/quick/stabilizer_states/test_stabilizer_states.py
@@ -2,7 +2,7 @@
 import numpy as np
 import networkx as nx
 
-from simulaqron.toolbox.stabilizerStates import StabilizerState
+from simulaqron.toolbox.stabilizer_states import StabilizerState
 
 
 class TestStabilizerStates(unittest.TestCase):
diff --git a/simulaqron/tests/unittests/quick/test_settings.py b/tests/quick/test_settings.py
similarity index 100%
rename from simulaqron/tests/unittests/quick/test_settings.py
rename to tests/quick/test_settings.py
diff --git a/simulaqron/tests/unittests/slow/merges/configs/Alice.cfg b/tests/slow/merges/configs/Alice.cfg
similarity index 100%
rename from simulaqron/tests/unittests/slow/merges/configs/Alice.cfg
rename to tests/slow/merges/configs/Alice.cfg
diff --git a/simulaqron/tests/unittests/slow/merges/configs/AliceBob.cfg b/tests/slow/merges/configs/AliceBob.cfg
similarity index 100%
rename from simulaqron/tests/unittests/slow/merges/configs/AliceBob.cfg
rename to tests/slow/merges/configs/AliceBob.cfg
diff --git a/simulaqron/tests/unittests/slow/merges/configs/AliceBobCharlie.cfg b/tests/slow/merges/configs/AliceBobCharlie.cfg
similarity index 100%
rename from simulaqron/tests/unittests/slow/merges/configs/AliceBobCharlie.cfg
rename to tests/slow/merges/configs/AliceBobCharlie.cfg
diff --git a/simulaqron/tests/unittests/slow/merges/configs/network.json b/tests/slow/merges/configs/network.json
similarity index 76%
rename from simulaqron/tests/unittests/slow/merges/configs/network.json
rename to tests/slow/merges/configs/network.json
index d06cf5a..715086f 100644
--- a/simulaqron/tests/unittests/slow/merges/configs/network.json
+++ b/tests/slow/merges/configs/network.json
@@ -4,43 +4,43 @@
             "Alice": {
                 "app_socket": [
                     "localhost",
-                    8000
+                    8001
                 ],
-                "cqc_socket": [
+                "qnodeos_socket": [
                     "localhost",
                     8003
                 ],
                 "vnode_socket": [
                     "localhost",
-                    8004
+                    8014
                 ]
             },
             "Bob": {
                 "app_socket": [
                     "localhost",
-                    8008
+                    8015
                 ],
-                "cqc_socket": [
+                "qnodeos_socket": [
                     "localhost",
-                    8014
+                    8016
                 ],
                 "vnode_socket": [
                     "localhost",
-                    8017
+                    8021
                 ]
             },
             "Charlie": {
                 "app_socket": [
                     "localhost",
-                    8026
+                    8024
                 ],
-                "cqc_socket": [
+                "qnodeos_socket": [
                     "localhost",
-                    8029
+                    8025
                 ],
                 "vnode_socket": [
                     "localhost",
-                    8036
+                    8032
                 ]
             }
         },
diff --git a/simulaqron/tests/unittests/slow/merges/test_merges.py b/tests/slow/merges/test_merges.py
similarity index 94%
rename from simulaqron/tests/unittests/slow/merges/test_merges.py
rename to tests/slow/merges/test_merges.py
index 5034bd8..bf2f730 100644
--- a/simulaqron/tests/unittests/slow/merges/test_merges.py
+++ b/tests/slow/merges/test_merges.py
@@ -8,11 +8,11 @@
 from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks
 
-from simulaqron.general.hostConfig import socketsConfig
+from simulaqron.general.host_config import SocketsConfig
 from simulaqron.local.setup import setup_local, assemble_qubit
 from simulaqron.network import Network
-from simulaqron.settings import simulaqron_settings
-from simulaqron.toolbox.stabilizerStates import StabilizerState
+from simulaqron.settings import simulaqron_settings, SimBackend
+from simulaqron.toolbox.stabilizer_states import StabilizerState
 
 
 class localNode(pb.Root):
@@ -49,7 +49,7 @@ def remote_receive_two_qubits(self, virtualNum):
             return True
         else:
             self.q2 = yield self.virtRoot.callRemote("get_virtual_ref", virtualNum)
-            correct = yield self.got_both()
+            correct = yield from self.got_both()
             return correct
 
     @inlineCallbacks
@@ -67,24 +67,24 @@ def got_both(self):
         yield self.q1.callRemote("apply_H")
         yield self.q1.callRemote("cnot_onto", self.q2)
 
-        if simulaqron_settings.backend == "qutip":
+        if simulaqron_settings.sim_backend == SimBackend.QUTIP.value:
             # Output state
             (realRho, imagRho) = yield self.virtRoot.callRemote("get_multiple_qubits", [self.q1, self.q2])
             rho = assemble_qubit(realRho, imagRho)
             expectedRho = [[0.5, 0, 0, 0.5], [0, 0, 0, 0], [0, 0, 0, 0], [0.5, 0, 0, 0.5]]
             correct = np.all(np.isclose(rho, expectedRho))
-        elif simulaqron_settings.backend == "projectq":
+        elif simulaqron_settings.sim_backend == SimBackend.PROJECTQ.value:
             (realvec, imagvec) = yield self.virtRoot.callRemote("get_register_RI", self.q1)
             state = [r + (1j * j) for r, j in zip(realvec, imagvec)]
             expectedState = [1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]
             correct = np.all(np.isclose(state, expectedState))
-        elif simulaqron_settings.backend == "stabilizer":
+        elif simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
             (array, _) = yield self.virtRoot.callRemote("get_register_RI", self.q1)
             state = StabilizerState(array)
             expectedState = StabilizerState([[1, 1, 0, 0], [0, 0, 1, 1]])
             correct = state == expectedState
         else:
-            ValueError("Unknown backend {}".format(simulaqron_settings.backend))
+            ValueError("Unknown backend {}".format(simulaqron_settings.sim_backend))
 
         return bool(correct)
 
@@ -115,24 +115,24 @@ def remote_receive_one_qubit(self, virtualNum, cnot_direction=0):
             yield q.callRemote("apply_H")
             yield q.callRemote("cnot_onto", qA)
 
-        if simulaqron_settings.backend == "qutip":
+        if simulaqron_settings.sim_backend == SimBackend.QUTIP.value:
             # Output state
             (realRho, imagRho) = yield self.virtRoot.callRemote("get_multiple_qubits", [qA, q])
             rho = assemble_qubit(realRho, imagRho)
             expectedRho = [[0.5, 0, 0, 0.5], [0, 0, 0, 0], [0, 0, 0, 0], [0.5, 0, 0, 0.5]]
             correct = np.all(np.isclose(rho, expectedRho))
-        elif simulaqron_settings.backend == "projectq":
+        elif simulaqron_settings.sim_backend == SimBackend.PROJECTQ.value:
             (realvec, imagvec) = yield self.virtRoot.callRemote("get_register_RI", qA)
             state = [r + (1j * j) for r, j in zip(realvec, imagvec)]
             expectedState = [1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]
             correct = np.all(np.isclose(state, expectedState))
-        elif simulaqron_settings.backend == "stabilizer":
+        elif simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
             (array, _) = yield self.virtRoot.callRemote("get_register_RI", qA)
             state = StabilizerState(array)
             expectedState = StabilizerState([[1, 1, 0, 0], [0, 0, 1, 1]])
             correct = state == expectedState
         else:
-            ValueError("Unknown backend {}".format(simulaqron_settings.backend))
+            ValueError("Unknown backend {}".format(simulaqron_settings.sim_backend))
 
         return bool(correct)
 
@@ -173,8 +173,8 @@ def setup_node(name, node_code, classical_net_file, send_end):
         classicalFile = os.path.join(os.path.dirname(__file__), "configs", classical_net_file)
 
         # Read configuration files for the virtual quantum, as well as the classical network
-        virtualNet = socketsConfig(virtualFile)
-        classicalNet = socketsConfig(classicalFile)
+        virtualNet = SocketsConfig(virtualFile)
+        classicalNet = SocketsConfig(classicalFile)
 
         # Check if we should run a local classical server. If so, initialize the code
         # to handle remote connections on the classical communication network
@@ -243,24 +243,24 @@ def alice(cls, qReg, virtRoot, myName, classicalNet, send_end):
         yield qA.callRemote("apply_H")
         yield qA.callRemote("cnot_onto", qB)
 
-        if simulaqron_settings.backend == "qutip":
+        if simulaqron_settings.sim_backend == SimBackend.QUTIP.value:
             # Output state
             (realRho, imagRho) = yield virtRoot.callRemote("get_multiple_qubits", [qA, qB])
             rho = assemble_qubit(realRho, imagRho)
             expectedRho = [[0.5, 0, 0, 0.5], [0, 0, 0, 0], [0, 0, 0, 0], [0.5, 0, 0, 0.5]]
             correct = np.all(np.isclose(rho, expectedRho))
-        elif simulaqron_settings.backend == "projectq":
+        elif simulaqron_settings.sim_backend == SimBackend.PROJECTQ.value:
             (realvec, imagvec, _, _, _) = yield virtRoot.callRemote("get_register", qA)
             state = [r + (1j * j) for r, j in zip(realvec, imagvec)]
             expectedState = [1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]
             correct = np.all(np.isclose(state, expectedState))
-        elif simulaqron_settings.backend == "stabilizer":
+        elif simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
             (array, _, _, _, _) = yield virtRoot.callRemote("get_register", qA)
             state = StabilizerState(array)
             expectedState = StabilizerState([[1, 1, 0, 0], [0, 0, 1, 1]])
             correct = state == expectedState
         else:
-            ValueError("Unknown backend {}".format(simulaqron_settings.backend))
+            ValueError("Unknown backend {}".format(simulaqron_settings.sim_backend))
 
         send_end.send(correct)
 
@@ -296,24 +296,24 @@ def alice(cls, qReg, virtRoot, myName, classicalNet, send_end):
         yield qA.callRemote("apply_H")
         yield qA.callRemote("cnot_onto", qB)
 
-        if simulaqron_settings.backend == "qutip":
+        if simulaqron_settings.sim_backend == SimBackend.QUTIP.value:
             # Output state
             (realRho, imagRho) = yield virtRoot.callRemote("get_multiple_qubits", [qA, qB])
             rho = assemble_qubit(realRho, imagRho)
             expectedRho = [[0.5, 0, 0, 0.5], [0, 0, 0, 0], [0, 0, 0, 0], [0.5, 0, 0, 0.5]]
             correct = np.all(np.isclose(rho, expectedRho))
-        elif simulaqron_settings.backend == "projectq":
+        elif simulaqron_settings.sim_backend == SimBackend.PROJECTQ.value:
             (realvec, imagvec, _, _, _) = yield virtRoot.callRemote("get_register", qA)
             state = [r + (1j * j) for r, j in zip(realvec, imagvec)]
             expectedState = [1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]
             correct = np.all(np.isclose(state, expectedState))
-        elif simulaqron_settings.backend == "stabilizer":
+        elif simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
             (array, _, _, _, _) = yield virtRoot.callRemote("get_register", qA)
             state = StabilizerState(array)
             expectedState = StabilizerState([[1, 1, 0, 0], [0, 0, 1, 1]])
             correct = state == expectedState
         else:
-            ValueError("Unknown backend {}".format(simulaqron_settings.backend))
+            ValueError("Unknown backend {}".format(simulaqron_settings.sim_backend))
 
         send_end.send(correct)
 
diff --git a/simulaqron/tests/unittests/slow/optional_noise/test_optional_noise.py b/tests/slow/optional_noise/test_optional_noise.py
similarity index 61%
rename from simulaqron/tests/unittests/slow/optional_noise/test_optional_noise.py
rename to tests/slow/optional_noise/test_optional_noise.py
index 1b0bc3b..ef8a60f 100644
--- a/simulaqron/tests/unittests/slow/optional_noise/test_optional_noise.py
+++ b/tests/slow/optional_noise/test_optional_noise.py
@@ -9,48 +9,50 @@
 import unittest
 import sys
 
-from cqc.pythonLib import CQCConnection, qubit
+from simulaqron.sdk.connection import SimulaQronConnection
+from netqasm.sdk import Qubit
 from simulaqron.network import Network
 from simulaqron.settings import simulaqron_settings
 
 
-def prep_z0(cqc):
-    q = qubit(cqc)
+def prep_z0(conn):
+    q = Qubit(conn)
     return q
 
 
-def prep_z1(cqc):
-    q = qubit(cqc)
+def prep_z1(conn):
+    q = Qubit(conn)
     q.X()
     return q
 
 
-def prep_x0(cqc):
-    q = qubit(cqc)
+def prep_x0(conn):
+    q = Qubit(conn)
     q.H()
     return q
 
 
-def prep_x1(cqc):
-    q = qubit(cqc)
+def prep_x1(conn):
+    q = Qubit(conn)
     q.X()
     q.H()
     return q
 
 
-def prep_y0(cqc):
-    q = qubit(cqc)
+def prep_y0(conn):
+    q = Qubit(conn)
     q.K()
     return q
 
 
-def prep_y1(cqc):
-    q = qubit(cqc)
+def prep_y1(conn):
+    q = Qubit(conn)
     q.X()
     q.K()
     return q
 
 
+@unittest.skip("Optional noise is currenlty not supported in the new version of simulaqron")
 class TestOptionalNoise(unittest.TestCase):
     @classmethod
     def setUpClass(cls):
@@ -70,44 +72,44 @@ def tearDownClass(cls):
         simulaqron_settings.default_settings()
 
     def test_z0(self):
-        with CQCConnection("Alice") as cqc:
+        with SimulaQronConnection("Alice") as conn:
             sys.stdout.write("Testing noise on state |0>")
-            ans = cqc.test_preparation(prep_z0, exp_values=self.exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_z0, exp_values=self.exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def test_z1(self):
-        with CQCConnection("Alice") as cqc:
+        with SimulaQronConnection("Alice") as conn:
             sys.stdout.write("Testing noise on state |1>")
-            ans = cqc.test_preparation(prep_z1, exp_values=self.exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_z1, exp_values=self.exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def test_x0(self):
-        with CQCConnection("Alice") as cqc:
+        with SimulaQronConnection("Alice") as conn:
             sys.stdout.write("Testing noise on state (|0>+|1>)/sqrt(2)")
-            ans = cqc.test_preparation(prep_x0, exp_values=self.exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_x0, exp_values=self.exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def test_x1(self):
-        with CQCConnection("Alice") as cqc:
+        with SimulaQronConnection("Alice") as conn:
             sys.stdout.write("Testing noise on state (|0>-|1>)/sqrt(2)")
-            ans = cqc.test_preparation(prep_x1, exp_values=self.exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_x1, exp_values=self.exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def test_y0(self):
-        with CQCConnection("Alice") as cqc:
+        with SimulaQronConnection("Alice") as conn:
             sys.stdout.write("Testing noise on state (|0>+i|1>)/sqrt(2)")
-            ans = cqc.test_preparation(prep_y0, exp_values=self.exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_y0, exp_values=self.exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def test_y1(self):
-        with CQCConnection("Alice") as cqc:
+        with SimulaQronConnection("Alice") as conn:
             sys.stdout.write("Testing noise on state (|0>-i|1>)/sqrt(2)")
-            ans = cqc.test_preparation(prep_y1, exp_values=self.exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_y1, exp_values=self.exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
diff --git a/simulaqron/tests/unittests/slow/pythonLib/test_single_qubit.py b/tests/slow/sdk/test_single_qubit.py
similarity index 61%
rename from simulaqron/tests/unittests/slow/pythonLib/test_single_qubit.py
rename to tests/slow/sdk/test_single_qubit.py
index 3ec1cce..4f0a291 100644
--- a/simulaqron/tests/unittests/slow/pythonLib/test_single_qubit.py
+++ b/tests/slow/sdk/test_single_qubit.py
@@ -31,9 +31,12 @@
 from scipy.linalg import expm
 import sys
 
-from cqc.pythonLib import CQCConnection, qubit, CQCUnsuppError
-from simulaqron.settings import simulaqron_settings
+from netqasm.sdk.qubit import Qubit
+
+from simulaqron.settings import simulaqron_settings, SimBackend
 from simulaqron.network import Network
+from simulaqron.sdk.connection import SimulaQronConnection
+from simulaqron.general import SimUnsupportedError
 
 
 def calc_exp_values(q):
@@ -61,14 +64,14 @@ def calc_exp_values(q):
     return (p_x, p_y, p_z)
 
 
-def prep_I_CQC(cqc):
-    q = qubit(cqc)
+def prep_I(conn):
+    q = Qubit(conn)
     q.I()
     return q
 
 
-def prep_X_CQC(cqc):
-    q = qubit(cqc)
+def prep_X(conn):
+    q = Qubit(conn)
     q.X()
     return q
 
@@ -78,8 +81,8 @@ def prep_X_state():
     return q
 
 
-def prep_Y_CQC(cqc):
-    q = qubit(cqc)
+def prep_Y(conn):
+    q = Qubit(conn)
     q.Y()
     return q
 
@@ -89,8 +92,8 @@ def prep_Y_state():
     return q
 
 
-def prep_Z_CQC(cqc):
-    q = qubit(cqc)
+def prep_Z(conn):
+    q = Qubit(conn)
     q.Z()
     return q
 
@@ -100,8 +103,8 @@ def prep_Z_state():
     return q
 
 
-def prep_H_CQC(cqc):
-    q = qubit(cqc)
+def prep_H(conn):
+    q = Qubit(conn)
     q.H()
     return q
 
@@ -111,8 +114,8 @@ def prep_H_state():
     return q
 
 
-def prep_T_CQC(cqc):
-    q = qubit(cqc)
+def prep_T(conn):
+    q = Qubit(conn)
     q.T()
     return q
 
@@ -122,8 +125,8 @@ def prep_T_state():
     return q
 
 
-def prep_K_CQC(cqc):
-    q = qubit(cqc)
+def prep_K(conn):
+    q = Qubit(conn)
     q.K()
     return q
 
@@ -133,38 +136,38 @@ def prep_K_state():
     return q
 
 
-def prep_rotx1_CQC(cqc):  # pi/8
-    q = qubit(cqc)
+def prep_rotx1(conn):  # pi/8
+    q = Qubit(conn)
     q.rot_X(16)
     return q
 
 
-def prep_rotx2_CQC(cqc):  # 5*pi/8
-    q = qubit(cqc)
+def prep_rotx2(conn):  # 5*pi/8
+    q = Qubit(conn)
     q.rot_X(80)
     return q
 
 
-def prep_roty1_CQC(cqc):  # pi/8
-    q = qubit(cqc)
+def prep_roty1(conn):  # pi/8
+    q = Qubit(conn)
     q.rot_Y(16)
     return q
 
 
-def prep_roty2_CQC(cqc):  # 5*pi/8
-    q = qubit(cqc)
+def prep_roty2(conn):  # 5*pi/8
+    q = Qubit(conn)
     q.rot_Y(80)
     return q
 
 
-def prep_rotz1_CQC(cqc):  # pi/8
-    q = qubit(cqc)
+def prep_rotz1(conn):  # pi/8
+    q = Qubit(conn)
     q.rot_Z(16)
     return q
 
 
-def prep_rotz2_CQC(cqc):  # 5*pi/8
-    q = qubit(cqc)
+def prep_rotz2(conn):  # 5*pi/8
+    q = Qubit(conn)
     q.rot_Z(80)
     return q
 
@@ -179,8 +182,8 @@ def prep_rot_state(n, a):
     return np.dot(R, q)
 
 
-def prep_reset_CQC(cqc):
-    q = qubit(cqc)
+def prep_reset(conn):
+    q = Qubit(conn)
     q.H()
     q.reset()
     return q
@@ -191,11 +194,11 @@ def prep_I_state():
     return q
 
 
-#####################################################################################################
+@unittest.skip("We can test these things better when we have implemented a get_qubit_state function for simulaqron")
 class SingleQubitGateTest(unittest.TestCase):
     @classmethod
     def setUpClass(cls):
-        cls.iterations = 100
+        cls.iterations = 1
         sys.stdout.write("Testing single qubit gates gates with {} iterations \r\n".format(cls.iterations))
 
         simulaqron_settings.default_settings()
@@ -207,157 +210,148 @@ def tearDownClass(cls):
         cls.network.stop()
         simulaqron_settings.default_settings()
 
-    def testIGate(self):
-        with CQCConnection("Alice", appID=0) as cqc:
-            # Test I
-            sys.stdout.write("Testing I gate:")
-            exp_values = calc_exp_values(prep_I_state())
-            ans = cqc.test_preparation(prep_I_CQC, exp_values, iterations=self.iterations)
-            sys.stdout.write("\r")
-            self.assertTrue(ans)
-
     def testXGate(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test X
             sys.stdout.write("Testing X gate:")
             exp_values = calc_exp_values(prep_X_state())
-            ans = cqc.test_preparation(prep_X_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_X, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testYGate(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test Y
             sys.stdout.write("Testing Y gate:")
             exp_values = calc_exp_values(prep_Y_state())
-            ans = cqc.test_preparation(prep_Y_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_Y, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testZGate(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test Z
             sys.stdout.write("Testing Z gate:")
             exp_values = calc_exp_values(prep_Z_state())
-            ans = cqc.test_preparation(prep_Z_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_Z, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testHGate(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test H
             sys.stdout.write("Testing H gate:")
             exp_values = calc_exp_values(prep_H_state())
-            ans = cqc.test_preparation(prep_H_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_H, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testTGate(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test T
             sys.stdout.write("Testing T gate:")
             exp_values = calc_exp_values(prep_T_state())
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(prep_T_CQC, exp_values, iterations=self.iterations, progress=False)
+            if simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+                with self.assertRaises(SimUnsupportedError):
+                    conn.test_preparation(prep_T, exp_values, iterations=self.iterations, progress=False)
             else:
-                ans = cqc.test_preparation(prep_T_CQC, exp_values, iterations=self.iterations)
+                ans = conn.test_preparation(prep_T, exp_values, iterations=self.iterations)
                 sys.stdout.write("\r")
                 self.assertTrue(ans)
 
     def testKGate(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test K
             sys.stdout.write("Testing K gate:")
             exp_values = calc_exp_values(prep_K_state())
-            ans = cqc.test_preparation(prep_K_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_K, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testXpi8Rot(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test ROT_X pi/8
             sys.stdout.write("Testing rotation (X,pi/8) gate:")
             exp_values = calc_exp_values(prep_rot_state([1, 0, 0], np.pi / 8))
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(prep_rotx1_CQC, exp_values, iterations=self.iterations, progress=False)
+            if simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+                with self.assertRaises(SimUnsupportedError):
+                    conn.test_preparation(prep_rotx1, exp_values, iterations=self.iterations, progress=False)
             else:
-                ans = cqc.test_preparation(prep_rotx1_CQC, exp_values, iterations=self.iterations)
+                ans = conn.test_preparation(prep_rotx1, exp_values, iterations=self.iterations)
                 sys.stdout.write("\r")
                 self.assertTrue(ans)
 
     def testX5pi8Rot(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test ROT_X 5*pi/8
             sys.stdout.write("Testing rotation (X,5*pi/8) gate:")
             exp_values = calc_exp_values(prep_rot_state([1, 0, 0], 5 * np.pi / 8))
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(prep_rotx2_CQC, exp_values, iterations=self.iterations, progress=False)
+            if simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+                with self.assertRaises(SimUnsupportedError):
+                    conn.test_preparation(prep_rotx2, exp_values, iterations=self.iterations, progress=False)
             else:
-                ans = cqc.test_preparation(prep_rotx2_CQC, exp_values, iterations=self.iterations)
+                ans = conn.test_preparation(prep_rotx2, exp_values, iterations=self.iterations)
                 sys.stdout.write("\r")
                 self.assertTrue(ans)
 
     def testYpi8Rot(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test ROT_Y pi/8
             sys.stdout.write("Testing rotation (Y,pi/8) gate:")
             exp_values = calc_exp_values(prep_rot_state([0, 1, 0], np.pi / 8))
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(prep_roty1_CQC, exp_values, iterations=self.iterations, progress=False)
+            if simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+                with self.assertRaises(SimUnsupportedError):
+                    conn.test_preparation(prep_roty1, exp_values, iterations=self.iterations, progress=False)
             else:
-                ans = cqc.test_preparation(prep_roty1_CQC, exp_values, iterations=self.iterations)
+                ans = conn.test_preparation(prep_roty1, exp_values, iterations=self.iterations)
                 sys.stdout.write("\r")
                 self.assertTrue(ans)
 
     def testY5pi8Rot(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test ROT_Y 5*pi/8
             sys.stdout.write("Testing rotation (Y,5*pi/8) gate:")
             exp_values = calc_exp_values(prep_rot_state([0, 1, 0], 5 * np.pi / 8))
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(prep_roty2_CQC, exp_values, iterations=self.iterations, progress=False)
+            if simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+                with self.assertRaises(SimUnsupportedError):
+                    conn.test_preparation(prep_roty2, exp_values, iterations=self.iterations, progress=False)
             else:
-                ans = cqc.test_preparation(prep_roty2_CQC, exp_values, iterations=self.iterations)
+                ans = conn.test_preparation(prep_roty2, exp_values, iterations=self.iterations)
                 sys.stdout.write("\r")
                 self.assertTrue(ans)
 
     def testZpi8Rot(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test ROT_Z pi/8
             sys.stdout.write("Testing rotation (Z,pi/8) gate:")
             exp_values = calc_exp_values(prep_rot_state([0, 0, 1], np.pi / 8))
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(prep_rotz1_CQC, exp_values, iterations=self.iterations, progress=False)
+            if simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+                with self.assertRaises(SimUnsupportedError):
+                    conn.test_preparation(prep_rotz1, exp_values, iterations=self.iterations, progress=False)
             else:
-                ans = cqc.test_preparation(prep_rotz1_CQC, exp_values, iterations=self.iterations)
+                ans = conn.test_preparation(prep_rotz1, exp_values, iterations=self.iterations)
                 sys.stdout.write("\r")
                 self.assertTrue(ans)
 
     def testZ5pi8Rot(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test ROT_Z 5*pi/8
             sys.stdout.write("Testing rotation (Z,5*pi/8) gate:")
             exp_values = calc_exp_values(prep_rot_state([0, 0, 1], 5 * np.pi / 8))
-            if simulaqron_settings.backend == "stabilizer":
-                with self.assertRaises(CQCUnsuppError):
-                    cqc.test_preparation(prep_rotz2_CQC, exp_values, iterations=self.iterations, progress=False)
+            if simulaqron_settings.sim_backend == SimBackend.STABILIZER.value:
+                with self.assertRaises(SimUnsupportedError):
+                    conn.test_preparation(prep_rotz2, exp_values, iterations=self.iterations, progress=False)
             else:
-                ans = cqc.test_preparation(prep_rotz2_CQC, exp_values, iterations=self.iterations)
+                ans = conn.test_preparation(prep_rotz2, exp_values, iterations=self.iterations)
                 sys.stdout.write("\r")
                 self.assertTrue(ans)
 
     def testReset(self):
-        with CQCConnection("Alice", appID=0) as cqc:
+        with SimulaQronConnection("Alice") as conn:
             # Test RESET
             sys.stdout.write("Testing RESET:")
             exp_values = calc_exp_values(prep_I_state())
-            ans = cqc.test_preparation(prep_reset_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_reset, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
diff --git a/simulaqron/tests/unittests/slow/pythonLib/test_two_qubit.py b/tests/slow/sdk/test_two_qubit.py
similarity index 72%
rename from simulaqron/tests/unittests/slow/pythonLib/test_two_qubit.py
rename to tests/slow/sdk/test_two_qubit.py
index b25a67b..3d26d8a 100644
--- a/simulaqron/tests/unittests/slow/pythonLib/test_two_qubit.py
+++ b/tests/slow/sdk/test_two_qubit.py
@@ -30,7 +30,8 @@
 import unittest
 import numpy as np
 
-from cqc.pythonLib import CQCConnection, qubit
+from simulaqron.sdk.connection import SimulaQronConnection
+from netqasm.sdk import Qubit
 from simulaqron.network import Network
 from simulaqron.settings import simulaqron_settings
 
@@ -59,27 +60,27 @@ def calc_exp_values(q):
     return p_x, p_y, p_z
 
 
-def prep_CNOT_control_CQC(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
+def prep_CNOT_control(conn):
+    q1 = Qubit(conn)
+    q2 = Qubit(conn)
     q1.H()
     q1.cnot(q2)
     q2.measure()
     return q1
 
 
-def prep_CNOT_target_CQC(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
+def prep_CNOT_target(conn):
+    q1 = Qubit(conn)
+    q2 = Qubit(conn)
     q1.H()
     q1.cnot(q2)
     q1.measure()
     return q2
 
 
-def prep_CPHASE_control_CQC(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
+def prep_CPHASE_control(conn):
+    q1 = Qubit(conn)
+    q2 = Qubit(conn)
     q1.H()
     q2.H()
     q1.cphase(q2)
@@ -88,9 +89,9 @@ def prep_CPHASE_control_CQC(cqc):
     return q1
 
 
-def prep_CPHASE_target_CQC(cqc):
-    q1 = qubit(cqc)
-    q2 = qubit(cqc)
+def prep_CPHASE_target(conn):
+    q1 = Qubit(conn)
+    q2 = Qubit(conn)
     q1.H()
     q2.H()
     q1.cphase(q2)
@@ -99,29 +100,29 @@ def prep_CPHASE_target_CQC(cqc):
     return q2
 
 
-def prep_EPR1_CQC(cqc):
-    with CQCConnection("Alice", appID=1) as Alice:
+def prep_EPR1(conn):
+    with SimulaQronConnection("Alice", appID=1) as Alice:
         qA = Alice.createEPR("Bob")
-        qB = cqc.recvEPR()
+        qB = conn.recvEPR()
         qA.measure()
     return qB
 
 
-def prep_EPR2_CQC(cqc):
-    with CQCConnection("Alice", appID=1) as Alice:
-        qB = cqc.createEPR("Alice", remote_appID=1)
+def prep_EPR2(conn):
+    with SimulaQronConnection("Alice", appID=1) as Alice:
+        qB = conn.createEPR("Alice", remote_appID=1)
         qA = Alice.recvEPR()
         qA.measure()
     return qB
 
 
-def prep_send_CQC(cqc):
-    with CQCConnection("Alice", appID=1) as Alice:
-        qA = qubit(cqc)
-        qB = qubit(cqc)
+def prep_send(conn):
+    with SimulaQronConnection("Alice", appID=1) as Alice:
+        qA = Qubit(conn)
+        qB = Qubit(conn)
         qA.H()
         qA.cnot(qB)
-        cqc.sendQubit(qA, "Alice", remote_appID=1)
+        conn.sendQubit(qA, "Alice", remote_appID=1)
         qA = Alice.recvQubit()
         m = qA.measure()
         if m == 1:
@@ -130,12 +131,12 @@ def prep_send_CQC(cqc):
     return qB
 
 
-def prep_recv_CQC(cqc):
-    with CQCConnection("Alice", appID=1) as Alice:
-        qA = qubit(Alice)
+def prep_recv(conn):
+    with SimulaQronConnection("Alice", appID=1) as Alice:
+        qA = Qubit(Alice)
         qA.H()
         Alice.sendQubit(qA, "Bob")
-        qB = cqc.recvQubit()
+        qB = conn.recvQubit()
     return qB
 
 
@@ -151,7 +152,7 @@ def prep_H_state():
     return np.dot(q2, np.transpose(np.conj(q2)))
 
 
-#####################################################################################################
+@unittest.skip("We can test these things better when we have implemented a get_qubit_state function for simulaqron")
 class TwoQubitGateTest(unittest.TestCase):
     @classmethod
     def setUpClass(cls):
@@ -168,74 +169,74 @@ def tearDownClass(cls):
         simulaqron_settings.default_settings()
 
     def testCNOTControl(self):
-        with CQCConnection("Bob", appID=0) as cqc:
+        with SimulaQronConnection("Bob", appID=0) as conn:
             # Test CNOT control
             sys.stdout.write("Testing CNOT control:")
             exp_values = calc_exp_values(prep_mixed_state())
-            ans = cqc.test_preparation(prep_CNOT_control_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_CNOT_control, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testCNOTTarget(self):
-        with CQCConnection("Bob", appID=0) as cqc:
+        with SimulaQronConnection("Bob", appID=0) as conn:
             # Test CNOT target
             sys.stdout.write("Testing CNOT target:")
             exp_values = calc_exp_values(prep_mixed_state())
-            ans = cqc.test_preparation(prep_CNOT_target_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_CNOT_target, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testCPHASEControl(self):
-        with CQCConnection("Bob", appID=0) as cqc:
+        with SimulaQronConnection("Bob", appID=0) as conn:
             # Test CPHASE control
             sys.stdout.write("Testing CPHASE control:")
             exp_values = calc_exp_values(prep_mixed_state())
-            ans = cqc.test_preparation(prep_CPHASE_control_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_CPHASE_control, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testCPHASETarget(self):
-        with CQCConnection("Bob", appID=0) as cqc:
+        with SimulaQronConnection("Bob", appID=0) as conn:
             # Test CPHASE target
             sys.stdout.write("Testing CPHASE target:")
             exp_values = calc_exp_values(prep_mixed_state())
-            ans = cqc.test_preparation(prep_CPHASE_target_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_CPHASE_target, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testEPR1(self):
-        with CQCConnection("Bob", appID=0) as cqc:
+        with SimulaQronConnection("Bob", appID=0) as conn:
             # Test EPR1
             sys.stdout.write("Testing EPR1:")
             exp_values = calc_exp_values(prep_mixed_state())
-            ans = cqc.test_preparation(prep_EPR1_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_EPR1, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testEPR2(self):
-        with CQCConnection("Bob", appID=0) as cqc:
+        with SimulaQronConnection("Bob", appID=0) as conn:
             # Test EPR2
             sys.stdout.write("Testing EPR2:")
             exp_values = calc_exp_values(prep_mixed_state())
-            ans = cqc.test_preparation(prep_EPR2_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_EPR2, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testSendControl(self):
-        with CQCConnection("Bob", appID=0) as cqc:
+        with SimulaQronConnection("Bob", appID=0) as conn:
             # Test send control
             sys.stdout.write("Testing send:")
             exp_values = calc_exp_values(prep_H_state())
-            ans = cqc.test_preparation(prep_send_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_send, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)
 
     def testRevTarget(self):
-        with CQCConnection("Bob", appID=0) as cqc:
+        with SimulaQronConnection("Bob", appID=0) as conn:
             # Test recv target
             sys.stdout.write("Testing recv:")
             exp_values = calc_exp_values(prep_H_state())
-            ans = cqc.test_preparation(prep_recv_CQC, exp_values, iterations=self.iterations)
+            ans = conn.test_preparation(prep_recv, exp_values, iterations=self.iterations)
             sys.stdout.write("\r")
             self.assertTrue(ans)