Repository: torressa/cspy (language: python)

Issue:
False 2-cycle elimination #108
Basic implementation for feasible extension check if 2-cycles elimination is enabled in RCSPP. Fix for #108. Commit adds a parameter two_cycle_elimination to disable/enable 2-cycle elimination.


Discussion / hints:
Sick! Thanks, @felicze, very neat!
I will run some benchmarks and probably make a release for this (and try to fix the Windows CI).

Fix patch:
diff --git a/.github/workflows/dotnet_release.yml b/.github/workflows/dotnet_release.yml
index 5e8eb08..8454168 100644
--- a/.github/workflows/dotnet_release.yml
+++ b/.github/workflows/dotnet_release.yml
@@ -31,7 +31,7 @@ jobs:
       continue-on-error: true
 
   windows-build:
-    runs-on: windows-latest
+    runs-on: windows-2019
     steps:
     - uses: actions/checkout@v2
     - name: Install SWIG 4.0.2
diff --git a/.github/workflows/pypi_release.yml b/.github/workflows/pypi_release.yml
index d32c924..bfcf55b 100644
--- a/.github/workflows/pypi_release.yml
+++ b/.github/workflows/pypi_release.yml
@@ -27,11 +27,11 @@ jobs:
       fail-fast: false
       matrix:
         python: # Update this list whenever the docker image is updated (check /opt/python/)
-          - cp36-cp36m
           - cp37-cp37m
           - cp38-cp38
           - cp39-cp39
           - cp310-cp310
+          - cp311-cp311
     steps:
       - uses: actions/checkout@v2
       - name: Install python3-devel
@@ -74,8 +74,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [macos-10.15, macos-11] # 10.14 and 11 don't work
-        python-version: [3.6, 3.7, 3.8, 3.9, "3.10"]
+        os: [macos-10.15, macos-11]
+        python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
     env:
       TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
       TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
@@ -107,14 +107,14 @@ jobs:
         continue-on-error: true
 
   windows-build:
-    runs-on: windows-latest
+    runs-on: windows-2019
     env:
       TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
       TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
     strategy:
       fail-fast: false
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9, "3.10"]
+        python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
     steps:
       - uses: actions/checkout@v2
       - name: Set up Python ${{ matrix.python-version }}
diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
index 927a749..54960a8 100644
--- a/.github/workflows/python.yml
+++ b/.github/workflows/python.yml
@@ -8,7 +8,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9, "3.10"]
+        python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
     steps:
       - uses: actions/checkout@v2
       - name: Set up Python ${{ matrix.python-version }}
@@ -32,7 +32,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9, "3.10"]
+        python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
         os: [macos-11, macos-10.15]
     env:
       SYSTEM_VERSION_COMPAT: 0
diff --git a/.github/workflows/windows_cpp.yml b/.github/workflows/windows_cpp.yml
index 1bba845..0ba82c8 100644
--- a/.github/workflows/windows_cpp.yml
+++ b/.github/workflows/windows_cpp.yml
@@ -5,7 +5,7 @@ on: [push, pull_request]
 jobs:
   # Building using the github runner environement directly.
   build:
-    runs-on: windows-latest
+    runs-on: windows-2019
     steps:
     - uses: actions/checkout@v2
     - name: Check swig
@@ -13,7 +13,7 @@ jobs:
     - name: Check cmake
       run: cmake --version
     - name: Configure
-      run: cmake -S. -Bbuild -G "Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DBUILD_PYTHON=OFF
+      run: cmake -S. -Bbuild -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DBUILD_PYTHON=OFF
     - name: Build
       run: cmake --build build --config Release --target ALL_BUILD -- /verbosity:normal /maxcpucount
     - name: Test (cd into build)
diff --git a/.github/workflows/windows_dotnet.yml b/.github/workflows/windows_dotnet.yml
index 3173334..3a75167 100644
--- a/.github/workflows/windows_dotnet.yml
+++ b/.github/workflows/windows_dotnet.yml
@@ -5,7 +5,7 @@ on: [push, pull_request]
 jobs:
   # Building using the github runner environement directly.
   build:
-    runs-on: windows-latest
+    runs-on: windows-2019
     steps:
     - uses: actions/checkout@v2
     - name: Check swig
@@ -13,7 +13,7 @@ jobs:
     - name: Check cmake
       run: cmake --version
     - name: Configure
-      run: cmake -S. -Bbuild -G "Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DBUILD_DOTNET=ON
+      run: cmake -S. -Bbuild -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DBUILD_DOTNET=ON
     - name: Build
       run: cmake --build build --config Release --target ALL_BUILD -- /verbosity:normal /maxcpucount
     - name: Test
diff --git a/.github/workflows/windows_python.yml b/.github/workflows/windows_python.yml
index c494d12..07e772a 100644
--- a/.github/workflows/windows_python.yml
+++ b/.github/workflows/windows_python.yml
@@ -5,10 +5,10 @@ on: [push, pull_request]
 jobs:
   # Building using the github runner environement directly.
   build:
-    runs-on: windows-latest
+    runs-on: windows-2019
     strategy:
       matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9, "3.10"]
+        python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
     steps:
     - uses: actions/checkout@v2
     - name: Set up Python ${{ matrix.python-version }}
@@ -20,7 +20,7 @@ jobs:
     - name: Check cmake
       run: cmake --version
     - name: Configure
-      run: cmake -S. -Bbuild -G "Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=Release -DBUILD_PYTHON=ON -DBUILD_TESTING=ON -DPython3_ROOT_DIR=$env:pythonLocation -DPython_ROOT_DIR=$env:pythonLocation -DPython3_FIND_STRATEGY=LOCATION -DPython3_FIND_REGISTRY=LAST
+      run: cmake -S. -Bbuild -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Release -DBUILD_PYTHON=ON -DBUILD_TESTING=ON -DPython3_ROOT_DIR=$env:pythonLocation -DPython_ROOT_DIR=$env:pythonLocation -DPython3_FIND_STRATEGY=LOCATION -DPython3_FIND_REGISTRY=LAST
     - name: Build
       run: cmake --build build --config Release --target ALL_BUILD -- /verbosity:normal /maxcpucount
     - name: Run python tests
diff --git a/.gitignore b/.gitignore
index 06de3b9..8427a6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ xml
 *.o
 *.so
 *.sw*
+*.DS_Store
diff --git a/.style.yapf b/.style.yapf
deleted file mode 100644
index b84a562..0000000
--- a/.style.yapf
+++ /dev/null
@@ -1,3 +0,0 @@
-[style]
-# YAPF uses the chromium style
-based_on_style = google
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d7a5b02..8e31886 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [unreleased]
 
+## [v1.0.3]
+
+### Fixed
+
+ - Fixed #108: Non-elementary checks for 2-cycles (`i->j->i` are not allowed).
+   Thanks @felizce
+
 ## [v1.0.2]
 
 ### Changed
@@ -291,7 +298,8 @@ path using `networkx.shortest_simple_paths`.
 - Docstring modifications to include maths.
 - Updated README.
 
-[unreleased]: https://github.com/torressa/cspy/compare/v1.0.2...HEAD
+[unreleased]: https://github.com/torressa/cspy/compare/v1.0.3...HEAD
+[v1.0.3]: https://github.com/torressa/cspy/compare/v1.0.3...v1.0.3
 [v1.0.2]: https://github.com/torressa/cspy/compare/v1.0.1...v1.0.2
 [v1.0.1]: https://github.com/torressa/cspy/compare/v1.0.0...v1.0.1
 [v1.0.0]: https://github.com/torressa/cspy/compare/v1.0.0-alpha...v1.0.0
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d2626af..16b91f2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,7 +42,7 @@ endif()
 
 project(
   cspy
-  VERSION 1.0.2
+  VERSION 1.0.3
   LANGUAGES CXX)
 message(STATUS "project: ${PROJECT_NAME}")
 message(STATUS "version: ${PROJECT_VERSION}")
diff --git a/cmake/dotnet.cmake b/cmake/dotnet.cmake
index 6578920..94125f0 100644
--- a/cmake/dotnet.cmake
+++ b/cmake/dotnet.cmake
@@ -1,4 +1,3 @@
-# CMake from https://github.com/Mizux/dotnet-native LICENSE: Apache License 2.0
 if(NOT BUILD_DOTNET)
   return()
 endif()
@@ -7,6 +6,17 @@ set(CMAKE_SWIG_FLAGS)
 find_package(SWIG REQUIRED)
 include(UseSWIG)
 
+# .Net Core 3.1 LTS is not available for osx arm64
+if(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)")
+  set(USE_DOTNET_CORE_31 OFF)
+else()
+  option(USE_DOTNET_CORE_31 "Use .Net Core 3.1 LTS support" ON)
+endif()
+message(STATUS ".Net: Use .Net Core 3.1 LTS support: ${USE_DOTNET_CORE_31}")
+
+option(USE_DOTNET_6 "Use .Net 6.0 LTS support" ON)
+message(STATUS ".Net: Use .Net 6.0 LTS support: ${USE_DOTNET_6}")
+
 # if(${SWIG_VERSION} VERSION_GREATER_EQUAL 4) list(APPEND CMAKE_SWIG_FLAGS
 # "-doxygen") endif()
 
@@ -44,19 +54,42 @@ elseif(UNIX)
 endif()
 
 list(APPEND CMAKE_SWIG_FLAGS ${FLAGS} "-I${PROJECT_SOURCE_DIR}")
+target_link_libraries(cspy-dotnet-native PRIVATE BiDirectionalDotnet)
 
 # Needed by dotnet/CMakeLists.txt
-set(DOTNET_PACKAGE cspy.DN)
+set(DOTNET_PACKAGE cspy.Dotnet)
 set(DOTNET_PACKAGES_DIR "${PROJECT_BINARY_DIR}/dotnet/packages")
+
+# Runtime IDentifier see:
+# https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)")
+  set(DOTNET_PLATFORM arm64)
+else()
+  set(DOTNET_PLATFORM x64)
+endif()
+
+# see: https://docs.microsoft.com/en-us/dotnet/standard/frameworks
+if(USE_DOTNET_CORE_31 AND USE_DOTNET_6)
+  set(DOTNET_TFM "<TargetFrameworks>netcoreapp3.1;net6.0</TargetFrameworks>")
+elseif(USE_DOTNET_6)
+  set(DOTNET_TFM "<TargetFramework>net6.0</TargetFramework>")
+elseif(USE_DOTNET_CORE_31)
+  set(DOTNET_TFM "<TargetFramework>netcoreapp3.1</TargetFramework>")
+else()
+  message(FATAL_ERROR "No .Net SDK selected !")
+endif()
+
 if(APPLE)
-  set(RUNTIME_IDENTIFIER osx-x64)
+  set(RUNTIME_IDENTIFIER osx-${DOTNET_PLATFORM})
 elseif(UNIX)
-  set(RUNTIME_IDENTIFIER linux-x64)
+  set(RUNTIME_IDENTIFIER linux-${DOTNET_PLATFORM})
 elseif(WIN32)
-  set(RUNTIME_IDENTIFIER win-x64)
+  set(RUNTIME_IDENTIFIER win-${DOTNET_PLATFORM})
 else()
   message(FATAL_ERROR "Unsupported system !")
 endif()
+message(STATUS ".Net RID: ${RUNTIME_IDENTIFIER}")
+
 set(DOTNET_NATIVE_PROJECT ${DOTNET_PACKAGE}.runtime.${RUNTIME_IDENTIFIER})
 set(DOTNET_PROJECT ${DOTNET_PACKAGE})
 
@@ -75,7 +108,7 @@ message(STATUS ".Net runtime project: ${DOTNET_NATIVE_PROJECT}")
 set(DOTNET_NATIVE_PATH ${PROJECT_BINARY_DIR}/dotnet/${DOTNET_NATIVE_PROJECT})
 message(STATUS ".Net runtime project build path: ${DOTNET_NATIVE_PATH}")
 
-# *.csproj.in contains: CMake variable(s) (@PROJECT_NAME@) that configure_file()
+# *.csproj.in contains: CMake variable(s) (@pROJECT_NAME@) that configure_file()
 # can manage and generator expression ($<TARGET_FILE:...>) that file(GENERATE)
 # can manage.
 configure_file(${PROJECT_SOURCE_DIR}/dotnet/${DOTNET_PACKAGE}.runtime.csproj.in
@@ -128,12 +161,12 @@ add_custom_target(
   DEPENDS ${DOTNET_PATH}/${DOTNET_PROJECT}.csproj
   COMMAND ${DOTNET_EXECUTABLE} build -c Release
           ${DOTNET_PROJECT}/${DOTNET_PROJECT}.csproj
-  COMMAND ${DOTNET_EXECUTABLE} pack -c Release
+  COMMAND ${DOTNET_EXECUTABLE} pack --no-build -c Release
           ${DOTNET_PROJECT}/${DOTNET_PROJECT}.csproj
   BYPRODUCTS dotnet/${DOTNET_PROJECT}/bin dotnet/${DOTNET_PROJECT}/obj
              dotnet/packages
   WORKING_DIRECTORY dotnet)
-add_dependencies(dotnet_package dotnet_native_package)
+add_dependencies(dotnet_package dotnet_native_package cspy-dotnet-native)
 
 # ##############################################################################
 # .Net Test  ##
@@ -165,7 +198,8 @@ function(add_dotnet_test FILE_NAME)
   if(BUILD_TESTING)
     add_test(
       NAME dotnet_${TEST_NAME}
-      COMMAND ${DOTNET_EXECUTABLE} test --no-build -c Release
+      COMMAND ${DOTNET_EXECUTABLE} test --no-build --framework net6.0 -c Release
+              ${TEST_NAME}.csproj
       WORKING_DIRECTORY ${DOTNET_TEST_PATH})
   endif()
 
diff --git a/dotnet/Directory.Build.props.in b/dotnet/Directory.Build.props.in
index c2c3976..6bcdb7a 100644
--- a/dotnet/Directory.Build.props.in
+++ b/dotnet/Directory.Build.props.in
@@ -7,7 +7,7 @@
       <MinClientVersion>4.1.0</MinClientVersion>
       <NeutralLanguage>en-UK</NeutralLanguage>
       <Authors>David Torres Sanchez</Authors>
-      <Company>Lancaster Univeristy</Company>
+      <Company>Lancaster University</Company>
       <Copyright>Copyright (c) 2021. David Torres Sanchez</Copyright>
        <!-- Pack Option -->
        <PackageTags>native;sample</PackageTags>
diff --git a/dotnet/Test.csproj.in b/dotnet/Test.csproj.in
index 3948cd6..f42341b 100644
--- a/dotnet/Test.csproj.in
+++ b/dotnet/Test.csproj.in
@@ -1,8 +1,8 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <LangVersion>7.3</LangVersion>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <LangVersion>8.0</LangVersion>
+    @DOTNET_TFM@
     <EnableDefaultItems>false</EnableDefaultItems>
     <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
     <!-- see https://github.com/dotnet/docs/issues/12237 -->
@@ -33,9 +33,9 @@
 
   <ItemGroup>
     <Compile Include="@FILE_NAME@" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
-    <PackageReference Include="xunit" Version="2.3.1" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
-    <PackageReference Include="@DOTNET_PROJECT@" Version="@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.*" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
+    <PackageReference Include="xunit" Version="2.4.1" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
+    <PackageReference Include="@DOTNET_PROJECT@" Version="@PROJECT_VERSION@" />
   </ItemGroup>
 </Project>
diff --git a/dotnet/cspy.DN.csproj.in b/dotnet/cspy.Dotnet.csproj.in
similarity index 81%
rename from dotnet/cspy.DN.csproj.in
rename to dotnet/cspy.Dotnet.csproj.in
index 810c1ea..d31a7c7 100644
--- a/dotnet/cspy.DN.csproj.in
+++ b/dotnet/cspy.Dotnet.csproj.in
@@ -1,8 +1,8 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <OutputType>Library</OutputType>
-    <TargetFrameworks>netstandard2.1;netstandard2.0</TargetFrameworks>
-    <RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
+    @DOTNET_TFM@
+    <RuntimeIdentifier>@RUNTIME_IDENTIFIER@</RuntimeIdentifier>
     <AssemblyName>@DOTNET_PROJECT@</AssemblyName>
     <Version>@PROJECT_VERSION@</Version>
 
@@ -26,16 +26,19 @@
     <ItemGroup>
       <RuntimeLinux Include="@DOTNET_PACKAGES_DIR@/@DOTNET_PROJECT@.runtime.linux-x64.*.nupkg"/>
       <RuntimeOsx Include="@DOTNET_PACKAGES_DIR@/@DOTNET_PROJECT@.runtime.osx-x64.*.nupkg"/>
+      <RuntimeOsxArm Include="@DOTNET_PACKAGES_DIR@/@DOTNET_PROJECT@.runtime.osx-arm64.*.nupkg"/>
       <RuntimeWin Include="@DOTNET_PACKAGES_DIR@/@DOTNET_PROJECT@.runtime.win-x64.*.nupkg"/>
     </ItemGroup>
     <Message Importance="high" Text="Package @DOTNET_PROJECT@.runtime.linux-x64: found" Condition="Exists('@(RuntimeLinux)')"/>
     <Message Importance="high" Text="-------  $(OS)  "/>
     <Message Importance="high" Text="Package @DOTNET_PROJECT@.runtime.linux-x64: not found" Condition="!Exists('@(RuntimeLinux)')"/>
+    <Message Importance="high" Text="Package @DOTNET_PROJECT@.runtime.osx-arm64: found" Condition="Exists('@(RuntimeOsxArm)')"/>
+    <Message Importance="high" Text="Package @DOTNET_PROJECT@.runtime.osx-arm64: not found" Condition="!Exists('@(RuntimeOsxArm)')"/>
     <Message Importance="high" Text="Package @DOTNET_PROJECT@.runtime.osx-x64: found" Condition="Exists('@(RuntimeOsx)')"/>
     <Message Importance="high" Text="Package @DOTNET_PROJECT@.runtime.osx-x64: not found" Condition="!Exists('@(RuntimeOsx)')"/>
     <Message Importance="high" Text="Package @DOTNET_PROJECT@.runtime.win-x64: found" Condition="Exists('@(RuntimeWin)')"/>
     <Message Importance="high" Text="Package @DOTNET_PROJECT@.runtime.win-x64: not found" Condition="!Exists('@(RuntimeWin)')"/>
-    <Error Text="You must locally 'dotnet pack' at least one @DOTNET_PROJECT@.runtime.{rid} Project." Condition="!Exists('@(RuntimeLinux)') AND !Exists('@(RuntimeOsx)') AND !Exists('@(RuntimeWin)')"/>
+    <Error Text="You must locally 'dotnet pack' at least one @DOTNET_PROJECT@.runtime.{rid} Project." Condition="!Exists('@(RuntimeLinux)') AND !Exists('@(RuntimeOsx)') AND !Exists('@(RuntimeWin)') AND !Exists('@(RuntimeOsxArm)')"/>
   </Target>
 
   <!-- Need to add required runtime dependencies so Meta-Project will pull runtime Nuget(s) -->
@@ -47,9 +50,11 @@
   <ItemGroup>
     <RuntimeLinux Include="@DOTNET_PACKAGES_DIR@/@DOTNET_PROJECT@.runtime.linux-x64.*.nupkg"/>
     <RuntimeOsx Include="@DOTNET_PACKAGES_DIR@/@DOTNET_PROJECT@.runtime.osx-x64.*.nupkg"/>
+    <RuntimeOsxArm Include="@DOTNET_PACKAGES_DIR@/@DOTNET_PROJECT@.runtime.osx-arm64.*.nupkg"/>
     <RuntimeWin Include="@DOTNET_PACKAGES_DIR@/@DOTNET_PROJECT@.runtime.win-x64.*.nupkg"/>
     <PackageReference Include="@DOTNET_PROJECT@.runtime.linux-x64" Version="@PROJECT_VERSION@" Condition="Exists('@(RuntimeLinux)')"/>
     <PackageReference Include="@DOTNET_PROJECT@.runtime.osx-x64"   Version="@PROJECT_VERSION@" Condition="Exists('@(RuntimeOsx)')"  />
+    <PackageReference Include="@DOTNET_PROJECT@.runtime.osx-arm64"   Version="@PROJECT_VERSION@" Condition="Exists('@(RuntimeOsxArm)')"  />
     <PackageReference Include="@DOTNET_PROJECT@.runtime.win-x64"   Version="@PROJECT_VERSION@" Condition="Exists('@(RuntimeWin)')"  />
   </ItemGroup>
 </Project>
diff --git a/dotnet/cspy.DN.runtime.csproj.in b/dotnet/cspy.Dotnet.runtime.csproj.in
similarity index 67%
rename from dotnet/cspy.DN.runtime.csproj.in
rename to dotnet/cspy.Dotnet.runtime.csproj.in
index d2c5684..45798c7 100644
--- a/dotnet/cspy.DN.runtime.csproj.in
+++ b/dotnet/cspy.Dotnet.runtime.csproj.in
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <OutputType>Library</OutputType>
-    <TargetFrameworks>netstandard2.1;netstandard2.0</TargetFrameworks>
+    @DOTNET_TFM@
     <RuntimeIdentifier>@RUNTIME_IDENTIFIER@</RuntimeIdentifier>
     <AssemblyName>cspy-dotnet-native</AssemblyName>
     <Version>@PROJECT_VERSION@</Version>
@@ -11,7 +11,7 @@
     <IncludeBuildOutput>false</IncludeBuildOutput>
 
     <!-- Pack Option -->
-    <Title>@DOTNET@ @RUNTIME_IDENTIFIER@ v@PROJECT_VERSION@</Title>
+    <Title>@DOTNET_PACKAGE@ @RUNTIME_IDENTIFIER@ v@PROJECT_VERSION@</Title>
     <PackageId>@DOTNET_NATIVE_PROJECT@</PackageId>
 
     <!-- Signing -->
@@ -22,12 +22,10 @@
 
   <ItemGroup>
     <!-- Native library must be in native directory... -->
-    <Content Include="$<TARGET_FILE:BiDirectionalCpp>">
-      <PackagePath>runtimes/@RUNTIME_IDENTIFIER@/native/%(Filename)%(Extension)</PackagePath>
-      <Pack>true</Pack>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="$<TARGET_FILE:cspy-dotnet-native>">
+    <Content Include="
+          $<TARGET_FILE:cspy-dotnet-native>
+          $<$<STREQUAL:$<TARGET_PROPERTY:BiDirectionalCpp,TYPE>,SHARED_LIBRARY>:;$<TARGET_SONAME_FILE:BiDirectionalCpp>>
+        ">
       <PackagePath>runtimes/@RUNTIME_IDENTIFIER@/native/%(Filename)%(Extension)</PackagePath>
       <Pack>true</Pack>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
diff --git a/src/cc/bidirectional.h b/src/cc/bidirectional.h
index b5382d7..fce4de0 100644
--- a/src/cc/bidirectional.h
+++ b/src/cc/bidirectional.h
@@ -109,6 +109,10 @@ class BiDirectional {
     params_ptr_->setElementary(elementary_in);
   }
   /// @see bidirectional::Params
+  void setTwoCycleElimination(const bool& two_cycle_elimination_in) {
+    params_ptr_->setTwoCycleElimination(two_cycle_elimination_in);
+  }
+  /// @see bidirectional::Params
   void setBoundsPruning(const bool& bounds_pruning_in) {
     params_ptr_->setBoundsPruning(bounds_pruning_in);
   }
diff --git a/src/cc/dotnet/CMakeLists.txt b/src/cc/dotnet/CMakeLists.txt
index fd41118..cd2b6c4 100644
--- a/src/cc/dotnet/CMakeLists.txt
+++ b/src/cc/dotnet/CMakeLists.txt
@@ -1,6 +1,7 @@
 set_property(SOURCE bidirectional.i PROPERTY CPLUSPLUS ON)
-set_property(SOURCE bidirectional.i PROPERTY COMPILE_OPTIONS -dllimport
-                                             cspy-dotnet-native)
+set_property(
+  SOURCE bidirectional.i PROPERTY COMPILE_OPTIONS -namespace ${DOTNET_PROJECT}
+                                  -dllimport cspy-dotnet-native)
 
 swig_add_library(
   BiDirectionalDotnet
diff --git a/src/cc/dotnet/bidirectional.i b/src/cc/dotnet/bidirectional.i
index a450048..7f7fd1d 100644
--- a/src/cc/dotnet/bidirectional.i
+++ b/src/cc/dotnet/bidirectional.i
@@ -49,6 +49,7 @@ using namespace bidirectional;
 %rename("%s") bidirectional::BiDirectional::setFindCriticalRes;
 %rename("%s") bidirectional::BiDirectional::setCriticalRes;
 %rename("%s") bidirectional::BiDirectional::setREFCallback;
+%rename("%s") bidirectional::BiDirectional::setTwoCycleElimination;
 /* Expose getters */
 %rename("%s") bidirectional::BiDirectional::getPath;
 %rename("%s") bidirectional::BiDirectional::getTotalCost;
diff --git a/src/cc/labelling.cc b/src/cc/labelling.cc
index 74775f1..1a95b31 100644
--- a/src/cc/labelling.cc
+++ b/src/cc/labelling.cc
@@ -156,7 +156,52 @@ bool Label::checkStPath(const int& source_id, const int& sink_id) const {
 }
 
 bool Label::checkPathExtension(const int& user_id) const {
-  return (partial_path.end()[-2] != user_id && partial_path.back() != user_id);
+  if (partial_path.back() == user_id) {
+    return false;
+  };
+
+  if (params_ptr->two_cycle_elimination && partial_path.size() > 1 &&
+      getPredecessorId() == user_id) {
+    return false;
+  }
+
+  return true;
+}
+
+bool Label::checkSameFeasibleExtensionTwoCycleSimple(const Label& other) const {
+  return other.partial_path.size() > 1 && partial_path.size() > 1 &&
+         other.getPredecessorId() == getPredecessorId();
+}
+
+bool Label::checkSameFeasibleExtensionElementary(const Label& other) const {
+  if (unreachable_nodes.size() == 0)
+    return false;
+
+  if (other.unreachable_nodes.size() == 0)
+    return false;
+
+  if (!std::includes(
+          other.unreachable_nodes.begin(),
+          other.unreachable_nodes.end(),
+          unreachable_nodes.begin(),
+          unreachable_nodes.end())) {
+    return false;
+  }
+
+  return true;
+}
+
+bool Label::checkSameFeasibleExtension(const Label& other) const {
+  if (params_ptr->elementary) {
+    return checkSameFeasibleExtensionElementary(other);
+  }
+
+  if (params_ptr->two_cycle_elimination) {
+    return checkSameFeasibleExtensionTwoCycleSimple(other);
+  }
+
+  // always same extension if RCSPP without cycle-elimination
+  return true;
 }
 
 bool Label::checkDominance(
@@ -203,19 +248,12 @@ bool Label::checkDominance(
       }
     }
   }
-  // Check for the elementary case
-  if (params_ptr->elementary && unreachable_nodes.size() > 0 &&
-      other.unreachable_nodes.size() > 0) {
-    // check other.unreachable_nodes \subset unreachable_nodes (strict)
-    if (!std::includes(
-            other.unreachable_nodes.begin(),
-            other.unreachable_nodes.end(),
-            unreachable_nodes.begin(),
-            unreachable_nodes.end())) {
-      return false;
-    }
+
+  if (!checkSameFeasibleExtension(other)) {
+    return false;
   }
-  //  this dominates other
+
+  // this dominates other
   return true;
 }
 
@@ -403,32 +441,35 @@ bool mergePreCheck(
     const labelling::Label&    fwd_label,
     const labelling::Label&    bwd_label,
     const std::vector<double>& max_res) {
-  bool result = true;
   if (fwd_label.vertex.lemon_id == -1 || bwd_label.vertex.lemon_id == -1)
     return false;
 
   // Merge paths
-  std::vector<int> path     = fwd_label.partial_path;
-  std::vector<int> path_bwd = bwd_label.partial_path;
-
-  std::reverse(path_bwd.begin(), path_bwd.end());
-  path.insert(path.end(), path_bwd.begin(), path_bwd.end());
+  std::vector<int> path = fwd_label.partial_path;
+  path.insert(
+      path.end(),
+      bwd_label.partial_path.rbegin(),
+      bwd_label.partial_path.rend());
 
   if (fwd_label.params_ptr->elementary) {
     std::sort(path.begin(), path.end());
     const bool& contains_duplicates =
         std::adjacent_find(path.begin(), path.end()) != path.end();
-    result = !contains_duplicates;
+    return !contains_duplicates;
   }
 
   // Check for 2-cycles.
-  const int size = static_cast<int>(path.size());
-  for (int i = 1; i < size - 1; ++i) {
-    if (path[i - 1] == path[i + 1] || path[i - 1] == path[i])
+  // Check at end of partial path should be sufficient since individual paths
+  // are 2-cycle free
+  if (fwd_label.params_ptr->two_cycle_elimination &&
+      fwd_label.partial_path.size() > 1 && bwd_label.partial_path.size() > 1) {
+    if (fwd_label.getPredecessorId() == bwd_label.partial_path.back())
+      return false;
+    if (bwd_label.getPredecessorId() == fwd_label.partial_path.back())
       return false;
   }
 
-  return result;
+  return true;
 }
 
 Label mergeLabels(
diff --git a/src/cc/labelling.h b/src/cc/labelling.h
index a67e16f..6678e78 100644
--- a/src/cc/labelling.h
+++ b/src/cc/labelling.h
@@ -145,9 +145,42 @@ class Label {
   /// Returns true is the partial path extension is OK.
   bool checkPathExtension(const int& user_id) const;
 
+  /**
+   * Simple check if both lables have the same feasible extension with regard to
+   * 2-cycle elimination.
+   * this and other have same feasible extension if predecessor is the same.
+   * @param[in] other, Label with other label to compare
+   * @return true if this and other have same feasible extension
+   */
+  bool checkSameFeasibleExtensionTwoCycleSimple(const Label& other) const;
+
+  /**
+   * Simple check if both lables have the same feasible extension under
+   * elementary conditions.
+   * this and other have the same feasible extension if unreachable_nodes of
+   * this is subset of unreachable_nodes of other
+   * @param[in] other, Label with other label to compare
+   * @return true if this and other have same feasible extension
+   */
+  bool checkSameFeasibleExtensionElementary(const Label& other) const;
+
+  /**
+   * Check if both lables have the same feasible extension, i.e.,
+   * if they both can extend to the same nodes.
+   * Important for correct dominance check.
+   * Labels with different feasible extension cannot dominate each other.
+   * @param[in] other, Label with other label to compare
+   * @return true if this and other have same feasible extension
+   */
+  bool checkSameFeasibleExtension(const Label& other) const;
+
   /// set phi attribute for merged labels from Righini and Salani (2006)
   void setPhi(const double& phi_in) { phi = phi_in; }
 
+  /// gets the id of the predecessor node
+  /// TODO can be replaced as member of label
+  int getPredecessorId() const { return partial_path.end()[-2]; };
+
   std::string getString() const;
   // operator overloads
   Label&               operator=(const Label& other) = default;
@@ -156,7 +189,7 @@ class Label {
   friend std::ostream& operator<<(std::ostream& os, const Label& label);
   friend bool          operator==(const Label& label1, const Label& label2);
   friend bool          operator!=(const Label& label1, const Label& label2) {
-    return !(label1 == label2);
+             return !(label1 == label2);
   }
 };
 
diff --git a/src/cc/params.h b/src/cc/params.h
index 61c7bf8..52c1844 100644
--- a/src/cc/params.h
+++ b/src/cc/params.h
@@ -36,6 +36,8 @@ class Params {
   double threshold = std::nan("na");
   /// bool with whether output path is required to be elementary
   bool elementary = false;
+  /// bool with whether 2-cycles should be eliminated for non-elementary RCSPP
+  bool two_cycle_elimination = false;
   /// bool with whether lower bounds based on shortest paths are used to prune
   /// labels. Experimental!
   bool bounds_pruning = false;
@@ -69,6 +71,9 @@ class Params {
   void setTimeLimit(const double& time_limit_in) { time_limit = time_limit_in; }
   void setThreshold(const double& threshold_in) { threshold = threshold_in; }
   void setElementary(const bool& elementary_in) { elementary = elementary_in; }
+  void setTwoCycleElimination(const bool& two_cycle_elimination_in) {
+    two_cycle_elimination = two_cycle_elimination_in;
+  };
   void setBoundsPruning(const bool& bounds_pruning_in) {
     bounds_pruning = bounds_pruning_in;
   }
diff --git a/src/cc/python/bidirectional.i b/src/cc/python/bidirectional.i
index 679ee8a..c178b67 100644
--- a/src/cc/python/bidirectional.i
+++ b/src/cc/python/bidirectional.i
@@ -47,6 +47,7 @@ using namespace bidirectional;
 %rename("%s") bidirectional::BiDirectional::setFindCriticalRes;
 %rename("%s") bidirectional::BiDirectional::setCriticalRes;
 %rename("%s") bidirectional::BiDirectional::setREFCallback;
+%rename("%s") bidirectional::BiDirectional::setTwoCycleElimination;
 /* Expose getters */
 %rename("%s") bidirectional::BiDirectional::getPath;
 %rename("%s") bidirectional::BiDirectional::getTotalCost;
diff --git a/src/python/__init__.py b/src/python/__init__.py
index 1d6cf80..a9f4b7a 100644
--- a/src/python/__init__.py
+++ b/src/python/__init__.py
@@ -9,8 +9,14 @@
 name = "cspy"
 
 __all__ = [
-    'BiDirectional', 'REFCallback', 'Tabu', 'GreedyElim', 'PSOLGENT', 'GRASP',
-    'check', 'preprocess_graph'
+    "BiDirectional",
+    "REFCallback",
+    "Tabu",
+    "GreedyElim",
+    "PSOLGENT",
+    "GRASP",
+    "check",
+    "preprocess_graph",
 ]
 
-__version__ = '0.1.0'
+__version__ = "0.1.0"
diff --git a/src/python/algorithms/bidirectional.py b/src/python/algorithms/bidirectional.py
index 3cef3a0..ab454f5 100644
--- a/src/python/algorithms/bidirectional.py
+++ b/src/python/algorithms/bidirectional.py
@@ -84,6 +84,9 @@ class BiDirectional:
         Custom resource extension callback. See `REFs`_ for more details.
         Default : None
 
+    two_cycle_elimination: bool, optional
+        whether 2-cycles should be eliminated for non-elementary RCSPP
+
     .. _REFs : https://cspy.readthedocs.io/en/latest/ref.html
     .. _Tilk 2017: https://www.sciencedirect.com/science/article/pii/S0377221717302035
     .. _Righini and Salani (2006): https://www.sciencedirect.com/science/article/pii/S1572528606000417
@@ -105,6 +108,7 @@ def __init__(
         critical_res: Optional[int] = None,
         # seed: Union[int] = None,
         REF_callback: Optional[REFCallback] = None,
+        two_cycle_elimination: Optional[bool] = False,
     ):
         # Check inputs
         check(G, max_res, min_res, direction, REF_callback, __name__)
@@ -157,6 +161,8 @@ def __init__(
             self.bidirectional_cpp.setREFCallback(REF_callback)
         # if isinstance(seed, int) and seed is not None:
         #     self.bidirectional_cpp.setSeed(seed)
+        if isinstance(two_cycle_elimination, bool) and two_cycle_elimination:
+            self.bidirectional_cpp.setTwoCycleElimination(True)
 
     def run(self):
         "Run the algorithm in series"
diff --git a/src/python/algorithms/grasp.py b/src/python/algorithms/grasp.py
index 635d057..3ae4f50 100644
--- a/src/python/algorithms/grasp.py
+++ b/src/python/algorithms/grasp.py
@@ -72,20 +72,21 @@ class GRASP(PathBase):
 
     """
 
-    def __init__(self,
-                 G: DiGraph,
-                 max_res: List[float],
-                 min_res: List[float],
-                 preprocess: Optional[bool] = False,
-                 max_iter: Optional[int] = 100,
-                 max_localiter: Optional[int] = 10,
-                 time_limit: Optional[int] = None,
-                 threshold: Optional[float] = None,
-                 alpha: Optional[float] = 0.2,
-                 REF_callback=None):
+    def __init__(
+        self,
+        G: DiGraph,
+        max_res: List[float],
+        min_res: List[float],
+        preprocess: Optional[bool] = False,
+        max_iter: Optional[int] = 100,
+        max_localiter: Optional[int] = 10,
+        time_limit: Optional[int] = None,
+        threshold: Optional[float] = None,
+        alpha: Optional[float] = 0.2,
+        REF_callback=None,
+    ):
         # Pass arguments to parent class
-        super().__init__(G, max_res, min_res, preprocess, threshold,
-                         REF_callback)
+        super().__init__(G, max_res, min_res, preprocess, threshold, REF_callback)
         # Algorithm specific attributes
         self.max_iter = max_iter
         self.max_localiter = max_localiter
@@ -96,15 +97,18 @@ def __init__(self,
         self.stop = False
         self.best_path = None
         self.best_solution = None
-        self.nodes = self.G.nodes()
+        self.nodes = sorted(list(self.G.nodes()))
 
     def run(self):
         """
         Calculate shortest path with resource constraints.
         """
         start = time()
-        while (self.it < self.max_iter and not self.stop and
-               not check_time_limit_breached(start, self.time_limit)):
+        while (
+            self.it < self.max_iter
+            and not self.stop
+            and not check_time_limit_breached(start, self.time_limit)
+        ):
             self._algorithm()
             self.it += 1
         if not self.best_solution.path:
@@ -120,14 +124,12 @@ def _construct(self):
         # Construction phase
         while len(solution.path) < len(self.nodes):
             candidates = [i for i in self.nodes if i not in solution.path]
-            weights = deque(
-                map(self._heuristic, repeat(solution.path[-1]), candidates))
+            weights = deque(map(self._heuristic, repeat(solution.path[-1]), candidates))
             # Build Restricted Candidiate List (RCL)
             restriced_candidates = [
                 candidates[i]
                 for i, c in enumerate(weights)
-                if c <= (min(weights) + self.alpha *
-                         (max(weights) - min(weights)))
+                if c <= (min(weights) + self.alpha * (max(weights) - min(weights)))
             ]
             # Select random node from RCL to add to the current solution
             solution.path.append(choice(restriced_candidates))
@@ -137,13 +139,15 @@ def _construct(self):
     def _local_search(self, solution):
         for _ in range(self.max_localiter):  # Local search phase
             # Init candidate solution using random valid path generator
-            candidate = Solution(
-                self._find_alternative_paths(self.G, solution.path), 0)
+            candidate = Solution(self._find_alternative_paths(self.G, solution.path), 0)
             # evaluate candidate solution
             candidate.cost = self._cost_solution(candidate)
             # Update solution with candidate if lower cost and resource feasible
-            if (candidate.path and candidate.cost < solution.cost and
-                    self._check_path(candidate)):
+            if (
+                candidate.path
+                and candidate.cost < solution.cost
+                and self._check_path(candidate)
+            ):
                 solution = candidate
         return solution
 
@@ -157,15 +161,15 @@ def _heuristic(self, i, j):
             if (i, j) not in self.G.edges():
                 return 1e10
             else:
-                return self.G.get_edge_data(i, j)['weight']
+                return self.G.get_edge_data(i, j)["weight"]
         else:
             return 1e10
 
     def _cost_solution(self, solution=None):
         if solution:
             return sum(
-                self._heuristic(i, j)
-                for i, j in zip(solution.path, solution.path[1:]))
+                self._heuristic(i, j) for i, j in zip(solution.path, solution.path[1:])
+            )
         else:
             return 1e11
 
@@ -176,8 +180,12 @@ def _check_path(self, solution=None):
         """
         if solution:
             path, cost = solution.path, solution.cost
-            if (len(path) > 2 and cost < 1e10 and path[0] == 'Source' and
-                    path[-1] == 'Sink'):
+            if (
+                len(path) > 2
+                and cost < 1e10
+                and path[0] == "Source"
+                and path[-1] == "Sink"
+            ):
                 self.st_path = path
                 return self.check_feasibility(return_edge=False)
             else:
@@ -197,15 +205,14 @@ def _find_alternative_paths(G, path, rng=None):
         if poss_edges:
             sample_size = randint(1, len(poss_edges))
             if rng:
-                tmp = np.empty(len(poss_edges), dtype='object')
+                tmp = np.empty(len(poss_edges), dtype="object")
                 tmp[:] = poss_edges
-                selection = rng.choice(tmp, replace=False,
-                                       size=sample_size).tolist()
+                selection = rng.choice(tmp, replace=False, size=sample_size).tolist()
             else:
                 selection = sample(deque(poss_edges), sample_size)
             # will use last value tried with given key
             path_edges = dict([edge for edge in selection if edge in G.edges()])
-            elem = 'Source'  # start point in the new list
+            elem = "Source"  # start point in the new list
             new_list = []
             for _ in path_edges:
                 try:
diff --git a/src/python/algorithms/greedy_elimination.py b/src/python/algorithms/greedy_elimination.py
index a443ba2..26cc5c4 100644
--- a/src/python/algorithms/greedy_elimination.py
+++ b/src/python/algorithms/greedy_elimination.py
@@ -72,19 +72,22 @@ class GreedyElim(PathBase):
         if no resource feasible path is found
     """
 
-    def __init__(self,
-                 G: DiGraph,
-                 max_res: List,
-                 min_res: List,
-                 preprocess: Optional[bool] = False,
-                 algorithm: Optional[str] = "simple",
-                 max_depth: Optional[int] = 1000,
-                 time_limit: Optional[int] = None,
-                 threshold: Optional[float] = None,
-                 REF_callback=None):
+    def __init__(
+        self,
+        G: DiGraph,
+        max_res: List,
+        min_res: List,
+        preprocess: Optional[bool] = False,
+        algorithm: Optional[str] = "simple",
+        max_depth: Optional[int] = 1000,
+        time_limit: Optional[int] = None,
+        threshold: Optional[float] = None,
+        REF_callback=None,
+    ):
         # Pass arguments to parent class
-        super().__init__(G, max_res, min_res, preprocess, threshold,
-                         REF_callback, algorithm)
+        super().__init__(
+            G, max_res, min_res, preprocess, threshold, REF_callback, algorithm
+        )
         # Algorithm specific parameters
         self.max_depth = max_depth
         self.time_limit = time_limit
@@ -100,8 +103,7 @@ def run(self):
         Calculate shortest path with resource constraints.
         """
         start = time()
-        while not self.stop and not check_time_limit_breached(
-                start, self.time_limit):
+        while not self.stop and not check_time_limit_breached(start, self.time_limit):
             self._algorithm()
 
         if not self.best_path:
@@ -127,8 +129,7 @@ def _algorithm(self):
             # Add previously removed edge
             self.add_edge_back(self.last_edge_removed)
             # Remove a predecessor edge instead
-            self.remove_edge(self._get_predecessor_edges(
-                self.last_edge_removed))
+            self.remove_edge(self._get_predecessor_edges(self.last_edge_removed))
 
     def _get_predecessor_edges(self, edge):
         if not self.predecessor_edges:
@@ -137,15 +138,18 @@ def _get_predecessor_edges(self, edge):
                 self.stop = True
                 return edge
             self.predecessor_edges = [
-                e for e in self.G.edges(self.G.nbunch_iter(
-                    [node] + list(self.G.predecessors(node))),
-                                        data=True) if e[1] == node and e != edge
+                e
+                for e in self.G.edges(
+                    self.G.nbunch_iter([node] + list(self.G.predecessors(node))),
+                    data=True,
+                )
+                if e[1] == node and e != edge
             ]
-            self.predecessor_edges.sort(key=lambda x: x[2]['weight'])
+            self.predecessor_edges.sort(key=lambda x: x[2]["weight"])
         next_edge = self.predecessor_edges[-1]
         self.predecessor_edges.pop(-1)
         return next_edge
 
     @staticmethod
     def _edge_extract(edge):
-        return array(edge[2]['res_cost'])
+        return array(edge[2]["res_cost"])
diff --git a/src/python/algorithms/path_base.py b/src/python/algorithms/path_base.py
index c0de7cc..dd70a7d 100644
--- a/src/python/algorithms/path_base.py
+++ b/src/python/algorithms/path_base.py
@@ -5,7 +5,7 @@
 from itertools import filterfalse, tee, chain
 
 from numpy import zeros, array
-from networkx import (shortest_simple_paths, astar_path, negative_edge_cycle)
+from networkx import shortest_simple_paths, astar_path, negative_edge_cycle
 
 from cspy.checking import check
 from cspy.preprocessing import preprocess_graph
@@ -22,20 +22,11 @@ class PathBase:
     e.g. shortest path, feasibility checks, compatible joining
     """
 
-    def __init__(self,
-                 G,
-                 max_res,
-                 min_res,
-                 preprocess,
-                 threshold,
-                 REF_callback,
-                 algorithm=None):
+    def __init__(
+        self, G, max_res, min_res, preprocess, threshold, REF_callback, algorithm=None
+    ):
         # Check inputs
-        check(G,
-              max_res,
-              min_res,
-              REF_callback=REF_callback,
-              algorithm=__name__)
+        check(G, max_res, min_res, REF_callback=REF_callback, algorithm=__name__)
         # Preprocess graph
         self.G = preprocess_graph(G, max_res, min_res, preprocess, REF_callback)
 
@@ -101,17 +92,17 @@ def get_simple_path(self, source, max_depth):
         cost is returned.
         """
         # Create two copies of the simple path generator
-        paths, paths_backup = tee(shortest_simple_paths(self.G, source, 'Sink'),
-                                  2)
+        paths, paths_backup = tee(shortest_simple_paths(self.G, source, "Sink"), 2)
         paths_reduced = True
 
         # Select paths with under threshold if they exist
         try:
             cost_min = self.threshold if self.threshold is not None else 0
             paths_backup = filterfalse(
-                lambda p: sum(self.G[i][j]["weight"]
-                              for i, j in zip(p, p[1:])) >= cost_min,
-                paths_backup)
+                lambda p: sum(self.G[i][j]["weight"] for i, j in zip(p, p[1:]))
+                >= cost_min,
+                paths_backup,
+            )
             first = paths_backup.__next__()
             # add first element back
             paths = chain([first], paths_backup)
@@ -145,23 +136,25 @@ def check_feasibility(self, return_edge=True, save=True):
         """
         shortest_path_edges = deque(iter(zip(self.st_path, self.st_path[1:])))
         shortest_path_edges_w_data = deque()
-        shortest_path_edges_w_data.extend([
-            (e[0], e[1], self.G[e[0]][e[1]]) for e in shortest_path_edges
-        ])
+        shortest_path_edges_w_data.extend(
+            [(e[0], e[1], self.G[e[0]][e[1]]) for e in shortest_path_edges]
+        )
         # init total resources and cost
-        total_res = zeros(self.G.graph['n_res'])
+        total_res = zeros(self.G.graph["n_res"])
         cost = 0
         # Check path for resource feasibility by adding one edge at a time
         for edge in shortest_path_edges_w_data:
-            cost += edge[2]['weight']
+            cost += edge[2]["weight"]
             if isinstance(self.REF, BuiltinFunctionType):
                 total_res += self._edge_extract(edge)
             else:
-                total_res = self.REF(total_res, edge[0], edge[1],
-                                     edge[2]["res_cost"], None, None)
+                total_res = self.REF(
+                    total_res, edge[0], edge[1], edge[2]["res_cost"], None, None
+                )
                 total_res = array(total_res)
-            if not ((all(total_res <= self.max_res) and
-                     all(total_res >= self.min_res))):
+            if not (
+                (all(total_res <= self.max_res) and all(total_res >= self.min_res))
+            ):
                 break
         else:
             # Fesible path found. Save attributes.
@@ -182,22 +175,25 @@ def remove_edge(self, edge):
     def add_edge_back(self, edge):
         LOG.debug("Added edge back {}".format(edge[:2]))
         if "data" in edge[2]:
-            self.G.add_edge(*edge[:2],
-                            res_cost=edge[2]['res_cost'],
-                            weight=edge[2]['weight'],
-                            data=edge[2]['data'])
+            self.G.add_edge(
+                *edge[:2],
+                res_cost=edge[2]["res_cost"],
+                weight=edge[2]["weight"],
+                data=edge[2]["data"]
+            )
         else:
-            self.G.add_edge(*edge[:2],
-                            res_cost=edge[2]['res_cost'],
-                            weight=edge[2]['weight'])
+            self.G.add_edge(
+                *edge[:2], res_cost=edge[2]["res_cost"], weight=edge[2]["weight"]
+            )
 
     def save(self, total_res, cost):
-        if not self.threshold or (self.threshold is not None and
-                                  cost <= self.threshold):
+        if not self.threshold or (
+            self.threshold is not None and cost <= self.threshold
+        ):
             self.best_path = self.st_path
             self.best_path_total_res = total_res
             self.best_path_cost = cost
 
     @staticmethod
     def _edge_extract(edge):
-        return array(edge[2]['res_cost'])
+        return array(edge[2]["res_cost"])
diff --git a/src/python/algorithms/psolgent.py b/src/python/algorithms/psolgent.py
index 3f922a8..121e998 100644
--- a/src/python/algorithms/psolgent.py
+++ b/src/python/algorithms/psolgent.py
@@ -116,28 +116,29 @@ class PSOLGENT(PathBase):
 
     __metaclass__ = ABCMeta
 
-    def __init__(self,
-                 G: DiGraph,
-                 max_res: List[float],
-                 min_res: List[float],
-                 preprocess: Optional[bool] = False,
-                 max_iter: Optional[int] = 100,
-                 max_localiter: Optional[int] = 5,
-                 time_limit: Optional[int] = None,
-                 threshold: Optional[float] = None,
-                 swarm_size: Optional[int] = 50,
-                 member_size: Optional[int] = None,
-                 neighbourhood_size: Optional[int] = 10,
-                 lower_bound: Optional[float] = -1.,
-                 upper_bound: Optional[float] = 1.,
-                 c1: Optional[float] = 1.35,
-                 c2: Optional[float] = 1.35,
-                 c3: Optional[float] = 1.4,
-                 seed: RandomState = None,
-                 REF_callback: Callable = None):
+    def __init__(
+        self,
+        G: DiGraph,
+        max_res: List[float],
+        min_res: List[float],
+        preprocess: Optional[bool] = False,
+        max_iter: Optional[int] = 100,
+        max_localiter: Optional[int] = 5,
+        time_limit: Optional[int] = None,
+        threshold: Optional[float] = None,
+        swarm_size: Optional[int] = 50,
+        member_size: Optional[int] = None,
+        neighbourhood_size: Optional[int] = 10,
+        lower_bound: Optional[float] = -1.0,
+        upper_bound: Optional[float] = 1.0,
+        c1: Optional[float] = 1.35,
+        c2: Optional[float] = 1.35,
+        c3: Optional[float] = 1.4,
+        seed: RandomState = None,
+        REF_callback: Callable = None,
+    ):
         # Pass arguments to parent class
-        super().__init__(G, max_res, min_res, preprocess, threshold,
-                         REF_callback)
+        super().__init__(G, max_res, min_res, preprocess, threshold, REF_callback)
         # Inputs
         self.max_iter = max_iter
         self.max_localiter = max_localiter
@@ -173,22 +174,21 @@ def _pos2path(self, pos, rands):
         return self._update_current_nodes(new_disc)
 
     def run(self):
-        """Calculate shortest path with resource constraints.
-        """
+        """Calculate shortest path with resource constraints."""
         start = time()
         self._init_swarm()
-        while (self.iter < self.max_iter and
-               not check_time_limit_breached(start, self.time_limit)):
+        while self.iter < self.max_iter and not check_time_limit_breached(
+            start, self.time_limit
+        ):
             pos_new = self.pos + self._get_vel()
             new_rands = self.random_state.uniform(0, 1, size=self.swarm_size)
             # Force Source and Sink to be selected
             pos_new[:, [0, -1]] = min(10 * self.lower_bound, np.min(pos_new))
-            paths_new = np.empty(len(pos_new), dtype='object')
-            paths_new[:] = [
-                self._pos2path(p, r) for p, r in zip(pos_new, new_rands)
-            ]
-            self._update_best(self.pos, pos_new, self.rands, new_rands,
-                              self.paths, paths_new)
+            paths_new = np.empty(len(pos_new), dtype="object")
+            paths_new[:] = [self._pos2path(p, r) for p, r in zip(pos_new, new_rands)]
+            self._update_best(
+                self.pos, pos_new, self.rands, new_rands, self.paths, paths_new
+            )
             self.pos = pos_new
             self.rands = new_rands
             self.fitness = self._get_fitness(self.paths)
@@ -200,12 +200,18 @@ def run(self):
             for i in range(self.swarm_size):
                 self._local_best(i)
             if self.iter % 100 == 0:
-                log.info("Iteration: {0}. Current best fit: {1}".format(
-                    self.iter, self.best_fit))
+                log.info(
+                    "Iteration: {0}. Current best fit: {1}".format(
+                        self.iter, self.best_fit
+                    )
+                )
             # Terminate if feasible path found with total cost <= threshold
-            if (self.best_path and self.best_path_cost and
-                    self.threshold is not None and
-                    self.best_path_cost <= self.threshold):
+            if (
+                self.best_path
+                and self.best_path_cost
+                and self.threshold is not None
+                and self.best_path_cost <= self.threshold
+            ):
                 break
             self.iter += 1
         if not self.best_path:
@@ -213,21 +219,19 @@ def run(self):
 
     def _init_swarm(self):
         # Initialises the variables that are altered during the algorithm
-        self.pos = self.random_state.uniform(self.lower_bound,
-                                             self.upper_bound,
-                                             size=(self.swarm_size,
-                                                   self.member_size))
+        self.pos = self.random_state.uniform(
+            self.lower_bound, self.upper_bound, size=(self.swarm_size, self.member_size)
+        )
         self.rands = self.random_state.uniform(0, 1, size=self.swarm_size)
         self.vel = self.random_state.uniform(
             self.lower_bound - self.upper_bound,
             self.upper_bound - self.lower_bound,
-            size=(self.swarm_size, self.member_size))
+            size=(self.swarm_size, self.member_size),
+        )
         # Force Source and Sink to be selected
         self.pos[:, [0, -1]] = min(10 * self.lower_bound, np.min(self.pos))
-        self.paths = np.empty(len(self.pos), dtype='object')
-        self.paths[:] = [
-            self._pos2path(p, r) for p, r in zip(self.pos, self.rands)
-        ]
+        self.paths = np.empty(len(self.pos), dtype="object")
+        self.paths[:] = [self._pos2path(p, r) for p, r in zip(self.pos, self.rands)]
         self.fitness = self._get_fitness(self.paths)
         self.best = np.copy(self.pos)
         self._global_best()
@@ -254,25 +258,26 @@ def _get_vel(self):
         c = self.c1 + self.c2 + self.c3
         chi_1 = 2 / abs(2 - c - sqrt(pow(c, 2) - 4 * c))
         # Returns velocity
-        return (chi_1 * (self.vel +
-                         (self.c1 * np.dot(u1, (self.best - self.pos)))) +
-                (self.c2 * np.dot(u2, (self.global_best - self.pos))) +
-                (self.c3 * np.dot(u3, (self.local_best - self.pos))))
+        return (
+            chi_1 * (self.vel + (self.c1 * np.dot(u1, (self.best - self.pos))))
+            + (self.c2 * np.dot(u2, (self.global_best - self.pos)))
+            + (self.c3 * np.dot(u3, (self.local_best - self.pos)))
+        )
 
     # also implicitly updates self.best_path
-    def _update_best(self, old, new, old_rands, new_rands, old_paths,
-                     new_paths):
+    def _update_best(self, old, new, old_rands, new_rands, old_paths, new_paths):
         # Updates the best objective function values for each member
         old_fitness = np.array(self._get_fitness(old_paths))
         new_fitness = np.array(self._get_fitness(new_paths))
         best = np.zeros(shape=old.shape)  # * np.nan
         best_rands = np.zeros(shape=old_rands.shape)  # * np.nan
-        best_paths = np.zeros(shape=len(old_paths), dtype='object')
+        best_paths = np.zeros(shape=len(old_paths), dtype="object")
         best_fitness = np.zeros(shape=len(old))
         if any(of < nf for of, nf in zip(old_fitness, new_fitness)):
             # replace indices in best with old members if lower fitness
             idx_old = [
-                idx for idx, val in enumerate(zip(old_fitness, new_fitness))
+                idx
+                for idx, val in enumerate(zip(old_fitness, new_fitness))
                 if val[0] < val[1]
             ]
             best[idx_old] = old[idx_old]
@@ -302,8 +307,9 @@ def _update_best(self, old, new, old_rands, new_rands, old_paths,
     def _global_best(self):
         # Updates the current global best across swarm
         if not self.best_fit or self.best_fit > min(self.fitness):
-            self.global_best = np.array([self.pos[np.argmin(self.fitness)]] *
-                                        self.swarm_size)
+            self.global_best = np.array(
+                [self.pos[np.argmin(self.fitness)]] * self.swarm_size
+            )
             self.best_fit = min(self.fitness)  # update best fitness
             self.global_rand = self.rands[np.argmin(self.fitness)]
             self.global_path = self.paths[np.argmin(self.fitness)]
@@ -370,7 +376,8 @@ def _fitness(self, nodes):
     def _get_edges(self, nodes):
         # Creates a list of edges given the nodes selected
         return [
-            edge for edge in self.G.edges(self.G.nbunch_iter(nodes), data=True)
+            edge
+            for edge in self.G.edges(self.G.nbunch_iter(nodes), data=True)
             if edge[0:2] in zip(nodes, nodes[1:])
         ]
 
@@ -394,8 +401,8 @@ def _check_path(self, path, edges):
         """
         if not path:
             return False
-        if len(path) >= 2 and (path[0] == 'Source' and path[-1] == 'Sink'):
-            base_cost = sum(edge[2]['weight'] for edge in edges)
+        if len(path) >= 2 and (path[0] == "Source" and path[-1] == "Sink"):
+            base_cost = sum(edge[2]["weight"] for edge in edges)
             # save st_path to reload later
             old_path = self.st_path
             self.st_path = path[:]
@@ -422,9 +429,11 @@ def _sort_nodes(nodes):
         `return sorted(nodes, key=lambda n: n[1]['pos'][0])`
         edit the sorting function to make use of it.
         """
-        return ['Source'] + \
-               sorted([n for n in nodes if n not in ['Source', 'Sink']]) + \
-               ['Sink']
+        return (
+            ["Source"]
+            + sorted([n for n in nodes if n not in ["Source", "Sink"]])
+            + ["Sink"]
+        )
 
     def _local_search_2opt(self, solution):
         """
@@ -441,13 +450,21 @@ def _2opt_swap(path, i, j):
             2-opt swap only if resulting path is connected in the original
             graph.
             """
-            if ((path[:i + 1][-1], path[j:i:-1][0]) in self.G.edges() and
-                ((len(path[j:i:-1]) > 0 and
-                  all(_ in self.G.edges()
-                      for _ in zip(path[j:i:-1], path[j:i:-1][1:]))) or
-                 len(path[j:i:-1]) == 0) and
-                (path[j:i:-1][-1], path[j + 1:][0]) in self.G.edges()):
-                return path[:i + 1] + path[j:i:-1] + path[j + 1:]
+            if (
+                (path[: i + 1][-1], path[j:i:-1][0]) in self.G.edges()
+                and (
+                    (
+                        len(path[j:i:-1]) > 0
+                        and all(
+                            _ in self.G.edges()
+                            for _ in zip(path[j:i:-1], path[j:i:-1][1:])
+                        )
+                    )
+                    or len(path[j:i:-1]) == 0
+                )
+                and (path[j:i:-1][-1], path[j + 1 :][0]) in self.G.edges()
+            ):
+                return path[: i + 1] + path[j:i:-1] + path[j + 1 :]
             return None
 
         local_iter = 0
@@ -463,8 +480,9 @@ def _2opt_swap(path, i, j):
                     candidate.cost = self._fitness(candidate.path)
                     # Update solution with candidate if lower cost and resource feasible
                     # TODO: difference if we break at first improvement
-                    if ((candidate.cost or candidate.cost == 0) and
-                            candidate.cost < solution.cost):
+                    if (
+                        candidate.cost or candidate.cost == 0
+                    ) and candidate.cost < solution.cost:
                         solution = candidate
                         # break
                 if local_iter == self.max_localiter:
diff --git a/src/python/algorithms/tabu.py b/src/python/algorithms/tabu.py
index fc15aa6..4cc7b20 100644
--- a/src/python/algorithms/tabu.py
+++ b/src/python/algorithms/tabu.py
@@ -71,25 +71,28 @@ class Tabu(PathBase):
 
     """
 
-    def __init__(self,
-                 G: DiGraph,
-                 max_res: List[float],
-                 min_res: List[float],
-                 preprocess: Optional[bool] = False,
-                 algorithm: Optional[str] = "simple",
-                 max_depth: Optional[int] = 1000,
-                 time_limit: Optional[int] = None,
-                 threshold: Optional[float] = None,
-                 REF_callback: Callable = None):
+    def __init__(
+        self,
+        G: DiGraph,
+        max_res: List[float],
+        min_res: List[float],
+        preprocess: Optional[bool] = False,
+        algorithm: Optional[str] = "simple",
+        max_depth: Optional[int] = 1000,
+        time_limit: Optional[int] = None,
+        threshold: Optional[float] = None,
+        REF_callback: Callable = None,
+    ):
         # Pass arguments to PathBase object
-        super().__init__(G, max_res, min_res, preprocess, threshold,
-                         REF_callback, algorithm)
+        super().__init__(
+            G, max_res, min_res, preprocess, threshold, REF_callback, algorithm
+        )
         # Algorithm specific parameters
         self.time_limit = time_limit
         self.max_depth = max_depth
         self.iteration = 0
         self.stop = False
-        self.neighbour = 'Source'
+        self.neighbour = "Source"
         self.neighbourhood = []
         self.tabu_edge = None
         self.edges_to_check = dict(self.G.edges())
@@ -99,8 +102,7 @@ def run(self):
         Calculate shortest path with resource constraints.
         """
         start = time()
-        while not self.stop and not check_time_limit_breached(
-                start, self.time_limit):
+        while not self.stop and not check_time_limit_breached(start, self.time_limit):
             self._algorithm()
             self.iteration += 1
 
@@ -134,9 +136,12 @@ def _update_path(self, neighbour, path):
         elif neighbour in self.st_path:
             # Paths can be joined at neighbour
             self.st_path = [
-                node for node in self.st_path
-                if (node != neighbour and
-                    self.st_path.index(node) < self.st_path.index(neighbour))
+                node
+                for node in self.st_path
+                if (
+                    node != neighbour
+                    and self.st_path.index(node) < self.st_path.index(neighbour)
+                )
             ] + path
         else:
             self._merge_paths(neighbour, path)
@@ -146,7 +151,8 @@ def _merge_paths(self, neighbour, path):
         for node in reversed(branch_path):
             if (node, neighbour) in self.G.edges():
                 self.st_path = [
-                    n for n in branch_path
+                    n
+                    for n in branch_path
                     if (branch_path.index(n) <= branch_path.index(node))
                 ] + path
                 break
@@ -189,11 +195,13 @@ def _get_next_neighbour_edge(self, edge):
                 return edge
             else:
                 nodes_iter = self.G.predecessors(node)
-                self.neighbourhood = deque(e for e in self.G.edges(
-                    self.G.nbunch_iter(nodes_iter), data=True)
-                                           if e[1] == node and e != edge)
+                self.neighbourhood = deque(
+                    e
+                    for e in self.G.edges(self.G.nbunch_iter(nodes_iter), data=True)
+                    if e[1] == node and e != edge
+                )
         # Get the edge in the neighbourhood with greatest weight
-        next_edge = max(self.neighbourhood, key=lambda x: x[2]['weight'])
+        next_edge = max(self.neighbourhood, key=lambda x: x[2]["weight"])
         # delete edge from neighbourhood
         del self.neighbourhood[self.neighbourhood.index(next_edge)]
         return next_edge
diff --git a/src/python/checking.py b/src/python/checking.py
index 04b060e..34480ce 100644
--- a/src/python/checking.py
+++ b/src/python/checking.py
@@ -9,12 +9,9 @@
 LOG = getLogger(__name__)
 
 
-def check(G,
-          max_res=None,
-          min_res=None,
-          direction=None,
-          REF_callback=None,
-          algorithm=None):
+def check(
+    G, max_res=None, min_res=None, direction=None, REF_callback=None, algorithm=None
+):
     """
     Checks whether inputs and the graph are of the appropriate types and
     have the required properties.
@@ -58,13 +55,14 @@ def check(G,
     # Select checks to perform based on the input provided
     if max_res and min_res and direction:
         check_funcs = [
-            _check_res, _check_direction, _check_graph_attr, _check_edge_attr,
-            _check_path
+            _check_res,
+            _check_direction,
+            _check_graph_attr,
+            _check_edge_attr,
+            _check_path,
         ]
     elif max_res and min_res:
-        check_funcs = [
-            _check_res, _check_graph_attr, _check_edge_attr, _check_path
-        ]
+        check_funcs = [_check_res, _check_graph_attr, _check_edge_attr, _check_path]
     else:
         check_funcs = [_check_path]
     # Check all functions in check_funcs
@@ -75,7 +73,7 @@ def check(G,
             errors.append(e)  # if check fails save error message
     if errors:
         # if any check has failed raise an exception with all the errors
-        raise Exception('\n'.join('{}'.format(item) for item in errors))
+        raise Exception("\n".join("{}".format(item) for item in errors))
 
 
 def check_seed(seed, algorithm=None):
@@ -95,8 +93,7 @@ def check_seed(seed, algorithm=None):
         raise TypeError("{} cannot be used to seed".format(seed))
 
 
-def check_time_limit_breached(start_time: float,
-                              time_limit: Union[int, None]) -> bool:
+def check_time_limit_breached(start_time: float, time_limit: Union[int, None]) -> bool:
     """Check time limit.
     :return: True if difference between current time and start time
     exceeds the time limit. False otherwise.
@@ -109,8 +106,12 @@ def check_time_limit_breached(start_time: float,
 def _check_res(G, max_res, min_res, direction, algorithm):
     if isinstance(max_res, list) and isinstance(min_res, list):
         if len(max_res) == len(min_res):
-            if not ((all(isinstance(i, (float, int)) for i in max_res) and
-                     all(isinstance(i, (float, int)) for i in min_res))):
+            if not (
+                (
+                    all(isinstance(i, (float, int)) for i in max_res)
+                    and all(isinstance(i, (float, int)) for i in min_res)
+                )
+            ):
                 raise TypeError("Elements of input lists must be numbers")
         else:
             raise TypeError("Input lists have to be equal length")
@@ -119,15 +120,14 @@ def _check_res(G, max_res, min_res, direction, algorithm):
 
 
 def _check_direction(G, max_res, min_res, direction, algorithm):
-    if direction not in ['forward', 'backward', 'both']:
-        raise TypeError(
-            "Input direction has to be 'forward', 'backward', or 'both'")
+    if direction not in ["forward", "backward", "both"]:
+        raise TypeError("Input direction has to be 'forward', 'backward', or 'both'")
 
 
 def _check_graph_attr(G, max_res, min_res, direction, algorithm):
     """Checks whether input graph has n_res attribute"""
     if isinstance(G, DiGraph):
-        if 'n_res' not in G.graph:
+        if "n_res" not in G.graph:
             raise TypeError("Input graph must have 'n_res' attribute")
     else:
         raise TypeError("Input must be a nx.Digraph()")
@@ -135,21 +135,24 @@ def _check_graph_attr(G, max_res, min_res, direction, algorithm):
 
 def _check_edge_attr(G, max_res, min_res, direction, algorithm):
     """Checks whether edges in input graph have res_cost attribute"""
-    if any('res_cost' not in edge[2] for edge in G.edges(data=True)):
+    if any("res_cost" not in edge[2] for edge in G.edges(data=True)):
         raise TypeError("Input graph must have edges with 'res_cost' attribute")
-    if any(
-            len(edge[2]['res_cost']) != G.graph['n_res']
-            for edge in G.edges(data=True)):
+    if any(len(edge[2]["res_cost"]) != G.graph["n_res"] for edge in G.edges(data=True)):
         raise TypeError(
-            "Edges must have 'res_cost' attribute with length equal to 'n_res'")
-    if any(not len(edge[2]['res_cost']) == len(max_res) == len(min_res)
-           for edge in G.edges(data=True)):
+            "Edges must have 'res_cost' attribute with length equal to 'n_res'"
+        )
+    if any(
+        not len(edge[2]["res_cost"]) == len(max_res) == len(min_res)
+        for edge in G.edges(data=True)
+    ):
         raise TypeError(
-            "Edges must have 'res_cost' attribute with length equal to" +
-            " 'min_res' == 'max_res")
-    if not all(
-            isinstance(edge[2]['res_cost'], ndarray) for edge in G.edges(
-                data=True)) and "bidirectional" not in algorithm:
+            "Edges must have 'res_cost' attribute with length equal to"
+            + " 'min_res' == 'max_res"
+        )
+    if (
+        not all(isinstance(edge[2]["res_cost"], ndarray) for edge in G.edges(data=True))
+        and "bidirectional" not in algorithm
+    ):
         raise TypeError("The edge 'res_cost' attribute must be a numpy.array")
 
 
@@ -158,23 +161,34 @@ def _check_path(G, max_res, min_res, direction, algorithm):
     negative edge cycles in the graph.
     Also covers nodes missing and other standard networkx exceptions"""
     try:
-        if not has_path(G, 'Source', 'Sink'):
+        if not has_path(G, "Source", "Sink"):
             raise NetworkXException("Disconnected Graph")
     except NetworkXException as e:
         raise Exception("An error occurred: {}".format(e))
 
 
 def _check_REF(REF_callback):
-    if REF_callback and not (callable(REF_callback.REF_fwd) or callable(
-            REF_callback.REF_bwd) or callable(REF_callback.REF_join)):
+    if REF_callback and not (
+        callable(REF_callback.REF_fwd)
+        or callable(REF_callback.REF_bwd)
+        or callable(REF_callback.REF_join)
+    ):
         raise TypeError("At least one REF function must be callable")
-    if (REF_callback and callable(REF_callback.REF_fwd) and
-            callable(REF_callback.REF_bwd) and
-            not callable(REF_callback.REF_join)):
+    if (
+        REF_callback
+        and callable(REF_callback.REF_fwd)
+        and callable(REF_callback.REF_bwd)
+        and not callable(REF_callback.REF_join)
+    ):
         LOG.warning("Default criteria used for joining paths.")
-    if (REF_callback and callable(REF_callback.REF_fwd) and
-            not callable(REF_callback.REF_bwd) and
-            not callable(REF_callback.REF_join)):
-        LOG.warning("Forward REF set but not backward REF."
-                    " This may lead to unexpected results.")
+    if (
+        REF_callback
+        and callable(REF_callback.REF_fwd)
+        and not callable(REF_callback.REF_bwd)
+        and not callable(REF_callback.REF_join)
+    ):
+        LOG.warning(
+            "Forward REF set but not backward REF."
+            " This may lead to unexpected results."
+        )
         LOG.warning("Default criteria used for joining paths.")
diff --git a/src/python/preprocessing.py b/src/python/preprocessing.py
index ae688ed..4f5bea4 100644
--- a/src/python/preprocessing.py
+++ b/src/python/preprocessing.py
@@ -29,29 +29,31 @@ def _check_resource(r):
 
         def __get_weight(i, j, attr_dict):
             # returns number to use as weight for the algorithm
-            return attr_dict['res_cost'][r]
+            return attr_dict["res_cost"][r]
 
         # Get paths from source to all other nodes
-        length_s, path_s = single_source_bellman_ford(G,
-                                                      'Source',
-                                                      weight=__get_weight)
-        length_t, path_t = single_source_bellman_ford(G.reverse(copy=True),
-                                                      'Sink',
-                                                      weight=__get_weight)
+        length_s, path_s = single_source_bellman_ford(G, "Source", weight=__get_weight)
+        length_t, path_t = single_source_bellman_ford(
+            G.reverse(copy=True), "Sink", weight=__get_weight
+        )
 
         try:
             # Collect nodes in paths that violate the resource bounds
             # see note above
-            nodes_source.update({
-                path_s[key][-1]: (val, r)
-                for key, val in length_s.items()
-                if (key != 'Source') and (val > max_res[r])
-            })
-            nodes_sink.update({
-                path_t[key][-1]: (val, r)
-                for key, val in length_t.items()
-                if (key != 'Sink') and (val > max_res[r])
-            })
+            nodes_source.update(
+                {
+                    path_s[key][-1]: (val, r)
+                    for key, val in length_s.items()
+                    if (key != "Source") and (val > max_res[r])
+                }
+            )
+            nodes_sink.update(
+                {
+                    path_t[key][-1]: (val, r)
+                    for key, val in length_t.items()
+                    if (key != "Sink") and (val > max_res[r])
+                }
+            )
         except IndexError:  # No nodes violate resource limits
             pass
 
@@ -59,12 +61,11 @@ def __get_weight(i, j, attr_dict):
     nodes_sink = {}
     n_nodes = len(G.nodes())
     # Map function for each resource
-    list(map(_check_resource, range(0, G.graph['n_res'])))
+    list(map(_check_resource, range(0, G.graph["n_res"])))
     if nodes_source or nodes_sink:  # if there are nodes to remove
         # Remove node if either direction is bad; it can't be on shortest path
         nodes_to_remove = [
-                node for node in G.nodes() if (node in nodes_source) or
-                (node in nodes_sink)
+            node for node in G.nodes() if (node in nodes_source) or (node in nodes_sink)
         ]
         # Filter out source or sink
         if "Source" in nodes_to_remove or "Sink" in nodes_to_remove:
@@ -77,7 +78,8 @@ def __get_weight(i, j, attr_dict):
             elif "Source" in nodes_source:
                 unreachable_res = nodes_source["Source"][1]
             raise Exception(
-                "Sink not reachable for resource {}".format(unreachable_res))
+                "Sink not reachable for resource {}".format(unreachable_res)
+            )
         G.remove_nodes_from(nodes_to_remove)
         log.info("Removed {}/{} nodes".format(len(nodes_to_remove), n_nodes))
     return G


Test patch:
diff --git a/test/cc/test_labelling.cc b/test/cc/test_labelling.cc
index 4dfa572..657070c 100644
--- a/test/cc/test_labelling.cc
+++ b/test/cc/test_labelling.cc
@@ -207,6 +207,19 @@ TEST_F(TestLabelling, testRunDominanceBackward) {
   ASSERT_EQ((*labels)[0], label3);
 }
 
+TEST_F(TestLabelling, testTwoCycleExtension) {
+  std::vector<int> path1{0, 6, 7, 8};
+  std::vector<int> path2{0, 1, 3, 4, 8};
+
+  Label label1(-270.0, node, {2.0}, path1, params_ptr.get());
+  Label label2(-260.0, node, {2.0}, path2, params_ptr.get());
+
+  ASSERT_TRUE(label1.checkDominance(label2, bidirectional::FWD));
+
+  params_ptr->two_cycle_elimination = true;
+  ASSERT_FALSE(label1.checkDominance(label2, bidirectional::FWD));
+}
+
 } // namespace labelling
 
 // namespace labelling
diff --git a/test/dotnet/test_simple.cs b/test/dotnet/test_simple.cs
index 372e207..f55a948 100644
--- a/test/dotnet/test_simple.cs
+++ b/test/dotnet/test_simple.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using Xunit;
+using cspy.Dotnet;
 
 public class SimpleTest {
   [Fact]
diff --git a/test/python/graph104.graphml b/test/python/graph104.graphml
new file mode 100644
index 0000000..7b01bd8
--- /dev/null
+++ b/test/python/graph104.graphml
@@ -0,0 +1,2638 @@
+<?xml version='1.0' encoding='utf-8'?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
+  <key attr.name="res_cost" attr.type="double" for="edge" id="d3" />
+  <key attr.name="weight" attr.type="float" for="edge" id="d2" />
+  <key attr.name="n_res" attr.type="long" for="graph" id="d1" />
+  <key attr.name="directed" attr.type="boolean" for="graph" id="d0" />
+  <graph edgedefault="directed">
+    <node id="1" />
+    <node id="5" />
+    <node id="9" />
+    <node id="13" />
+    <node id="17" />
+    <node id="21" />
+    <node id="25" />
+    <node id="29" />
+    <node id="33" />
+    <node id="37" />
+    <node id="41" />
+    <node id="45" />
+    <node id="49" />
+    <node id="53" />
+    <node id="57" />
+    <node id="61" />
+    <node id="65" />
+    <node id="69" />
+    <node id="73" />
+    <node id="77" />
+    <node id="81" />
+    <node id="85" />
+    <node id="89" />
+    <node id="93" />
+    <node id="97" />
+    <node id="Source" />
+    <node id="Sink" />
+    <edge source="1" target="5">
+      <data key="d2">-84.75703296283095</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="9">
+      <data key="d2">-81.71956376066971</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="13">
+      <data key="d2">-64.95804308960763</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="1" target="17">
+      <data key="d2">-61.10702228798753</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="1" target="21">
+      <data key="d2">-67.06796145048892</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="1" target="25">
+      <data key="d2">-173.3445382887317</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="1" target="29">
+      <data key="d2">-58.19383004845152</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="33">
+      <data key="d2">-42.599242617469514</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="1" target="37">
+      <data key="d2">-37.68690720728024</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="1" target="41">
+      <data key="d2">-51.63659026541143</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="45">
+      <data key="d2">-48.14627721831925</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="49">
+      <data key="d2">-51.87825126340907</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="53">
+      <data key="d2">-25.99173764185678</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="1" target="57">
+      <data key="d2">-35.76434703312558</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="1" target="61">
+      <data key="d2">-50.67213785647664</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="65">
+      <data key="d2">-60.839417969292796</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="69">
+      <data key="d2">-55.99967364995024</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="73">
+      <data key="d2">-28.559620704903935</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="77">
+      <data key="d2">-31.615006708335947</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="81">
+      <data key="d2">-37.144105000093546</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="1" target="85">
+      <data key="d2">-130.18079808380452</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="1" target="89">
+      <data key="d2">-68.40841336797624</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="1" target="93">
+      <data key="d2">-170.8705817843684</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="1" target="97">
+      <data key="d2">-66.32810555244097</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="1" target="Sink">
+      <data key="d2">132.3149729953259</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="5" target="1">
+      <data key="d2">-84.75703296283095</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="9">
+      <data key="d2">-83.59687576256715</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="13">
+      <data key="d2">-67.63932022500211</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="5" target="17">
+      <data key="d2">-64.0</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="5" target="21">
+      <data key="d2">-72.30819398704587</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="5" target="25">
+      <data key="d2">-178.55642702948018</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="5" target="29">
+      <data key="d2">-63.3729460886113</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="33">
+      <data key="d2">-47.79810430798162</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="5" target="37">
+      <data key="d2">-42.83009433971698</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="5" target="41">
+      <data key="d2">-56.265744413134</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="45">
+      <data key="d2">-53.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="49">
+      <data key="d2">-56.8941092855063</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="53">
+      <data key="d2">-29.966675920785462</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="5" target="57">
+      <data key="d2">-39.960015987212785</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="5" target="61">
+      <data key="d2">-54.09735385796752</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="65">
+      <data key="d2">-64.29007973563512</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="69">
+      <data key="d2">-59.85037313663733</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="73">
+      <data key="d2">-28.967221921331486</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="77">
+      <data key="d2">-32.1986159335263</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="81">
+      <data key="d2">-37.56909308432668</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="5" target="85">
+      <data key="d2">-129.05631116787868</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="5" target="89">
+      <data key="d2">-67.86405637882135</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="5" target="93">
+      <data key="d2">-167.6402163229752</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="5" target="97">
+      <data key="d2">-63.09275190585258</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="5" target="Sink">
+      <data key="d2">134.75495315418667</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="9" target="1">
+      <data key="d2">-81.71956376066971</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="5">
+      <data key="d2">-83.59687576256715</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="13">
+      <data key="d2">-73.23694538575978</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="9" target="17">
+      <data key="d2">-69.3844718719117</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="9" target="21">
+      <data key="d2">-70.3022843964078</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="9" target="25">
+      <data key="d2">-177.75375827733836</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="9" target="29">
+      <data key="d2">-63.09275190585258</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="33">
+      <data key="d2">-47.57359312880715</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="9" target="37">
+      <data key="d2">-43.138501944560076</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="9" target="41">
+      <data key="d2">-51.881762894908164</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="45">
+      <data key="d2">-49.207843891257724</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="49">
+      <data key="d2">-53.59945055359741</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="53">
+      <data key="d2">-24.723664318529643</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="9" target="57">
+      <data key="d2">-34.963648376731946</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="9" target="61">
+      <data key="d2">-48.2387739643578</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="65">
+      <data key="d2">-58.377223398316204</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="69">
+      <data key="d2">-54.3068634048505</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="73">
+      <data key="d2">-22.798809534354234</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="77">
+      <data key="d2">-25.968757625671515</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="81">
+      <data key="d2">-31.39965870406555</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="9" target="85">
+      <data key="d2">-123.90993915594234</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="9" target="89">
+      <data key="d2">-62.269150752275905</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="9" target="93">
+      <data key="d2">-167.23282720827052</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="9" target="97">
+      <data key="d2">-63.3729460886113</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="9" target="Sink">
+      <data key="d2">132.91697191231876</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="13" target="1">
+      <data key="d2">-64.95804308960763</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="5">
+      <data key="d2">-67.63932022500211</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="9">
+      <data key="d2">-73.23694538575978</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="17">
+      <data key="d2">-86.0</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="13" target="21">
+      <data key="d2">-65.64840867622816</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="13" target="25">
+      <data key="d2">-176.76253457902646</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="13" target="29">
+      <data key="d2">-64.9201275920311</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="33">
+      <data key="d2">-52.30384635005847</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="13" target="37">
+      <data key="d2">-49.688711258507254</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="13" target="41">
+      <data key="d2">-45.0778451095676</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="45">
+      <data key="d2">-44.29442047189424</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="49">
+      <data key="d2">-49.55250316768663</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="53">
+      <data key="d2">-16.624254688623438</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="13" target="57">
+      <data key="d2">-27.3581609465367</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="13" target="61">
+      <data key="d2">-37.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="65">
+      <data key="d2">-46.39954128681672</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="69">
+      <data key="d2">-43.85891201976269</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="73">
+      <data key="d2">-6.78341511453381</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="77">
+      <data key="d2">-10.118838265833915</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="81">
+      <data key="d2">-15.374267172777465</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="13" target="85">
+      <data key="d2">-107.14883414401436</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="13" target="89">
+      <data key="d2">-45.615317957655705</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="13" target="93">
+      <data key="d2">-153.9722375219433</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="13" target="97">
+      <data key="d2">-50.706234591223</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="13" target="Sink">
+      <data key="d2">147.31464664527613</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="17" target="1">
+      <data key="d2">-61.10702228798753</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="5">
+      <data key="d2">-64.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="9">
+      <data key="d2">-69.3844718719117</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="13">
+      <data key="d2">-86.0</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="17" target="21">
+      <data key="d2">-64.05775645785431</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="17" target="25">
+      <data key="d2">-175.91573102817026</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="17" target="29">
+      <data key="d2">-64.9201275920311</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="33">
+      <data key="d2">-53.59945055359741</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="17" target="37">
+      <data key="d2">-51.51623719021229</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="17" target="41">
+      <data key="d2">-43.761487913212434</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="45">
+      <data key="d2">-43.42747591121993</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="49">
+      <data key="d2">-48.76894374382339</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="53">
+      <data key="d2">-15.327381189622116</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="17" target="57">
+      <data key="d2">-26.09381876531817</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="17" target="61">
+      <data key="d2">-34.78224923088591</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="65">
+      <data key="d2">-43.90227771353556</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="69">
+      <data key="d2">-41.74028595194538</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="73">
+      <data key="d2">-3.3916863113014983</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="77">
+      <data key="d2">-6.78341511453381</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="81">
+      <data key="d2">-11.96795530040238</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="17" target="85">
+      <data key="d2">-103.33118321307339</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="17" target="89">
+      <data key="d2">-41.895946116777225</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="17" target="93">
+      <data key="d2">-150.06766252429568</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="17" target="97">
+      <data key="d2">-46.825933710154196</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="17" target="Sink">
+      <data key="d2">151.31464664527613</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="21" target="1">
+      <data key="d2">-67.06796145048892</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="5">
+      <data key="d2">-72.30819398704587</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="9">
+      <data key="d2">-70.3022843964078</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="13">
+      <data key="d2">-65.64840867622816</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="21" target="17">
+      <data key="d2">-64.05775645785431</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="21" target="25">
+      <data key="d2">-194.95736158851287</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="21" target="29">
+      <data key="d2">-79.80196097281443</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="33">
+      <data key="d2">-64.94007182771666</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="21" target="37">
+      <data key="d2">-59.536907576544365</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="21" target="41">
+      <data key="d2">-69.3844718719117</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="45">
+      <data key="d2">-68.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="49">
+      <data key="d2">-72.8827572313763</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="53">
+      <data key="d2">-40.959200659043084</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="21" target="57">
+      <data key="d2">-51.6724642065264</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="21" target="61">
+      <data key="d2">-60.267862505362984</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="65">
+      <data key="d2">-68.36669234721606</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="69">
+      <data key="d2">-67.32843190249073</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="73">
+      <data key="d2">-24.2124631864059</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="77">
+      <data key="d2">-27.967750322916707</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="81">
+      <data key="d2">-32.43264814150298</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="21" target="85">
+      <data key="d2">-116.69974007973093</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="21" target="89">
+      <data key="d2">-56.45898033750316</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="21" target="93">
+      <data key="d2">-150.01566374800876</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="21" target="97">
+      <data key="d2">-45.401793758044484</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="21" target="Sink">
+      <data key="d2">151.77370708076808</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="25" target="1">
+      <data key="d2">-173.3445382887317</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="5">
+      <data key="d2">-178.55642702948018</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="9">
+      <data key="d2">-177.75375827733836</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="13">
+      <data key="d2">-176.76253457902646</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="25" target="17">
+      <data key="d2">-175.91573102817026</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="25" target="21">
+      <data key="d2">-194.95736158851287</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="25" target="29">
+      <data key="d2">-194.57219678137835</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="33">
+      <data key="d2">-179.14870954182805</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="25" target="37">
+      <data key="d2">-174.01511804636718</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="25" target="41">
+      <data key="d2">-177.59668181351498</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="45">
+      <data key="d2">-177.3963332431559</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="49">
+      <data key="d2">-182.6946850868808</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="53">
+      <data key="d2">-149.26219416596655</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="25" target="57">
+      <data key="d2">-160.0324320331586</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="25" target="61">
+      <data key="d2">-166.65570997781944</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="65">
+      <data key="d2">-174.01511804636718</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="69">
+      <data key="d2">-173.7085520916995</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="73">
+      <data key="d2">-129.43786073945694</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="77">
+      <data key="d2">-133.22655910570137</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="81">
+      <data key="d2">-137.59551427783742</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="25" target="85">
+      <data key="d2">-221.7522218888809</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="25" target="89">
+      <data key="d2">-161.4865934651702</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="25" target="93">
+      <data key="d2">-256.2215105698877</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="25" target="97">
+      <data key="d2">-151.8533077052901</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="25" target="Sink">
+      <data key="d2">44.836786365019606</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="29" target="1">
+      <data key="d2">-58.19383004845152</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="5">
+      <data key="d2">-63.3729460886113</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="9">
+      <data key="d2">-63.09275190585258</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="13">
+      <data key="d2">-64.9201275920311</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="29" target="17">
+      <data key="d2">-64.9201275920311</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="29" target="21">
+      <data key="d2">-79.80196097281443</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="29" target="25">
+      <data key="d2">-194.57219678137835</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="29" target="33">
+      <data key="d2">-74.37950064818669</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="29" target="37">
+      <data key="d2">-69.408739718026</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="29" target="41">
+      <data key="d2">-66.56925097228003</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="45">
+      <data key="d2">-67.63932022500211</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="49">
+      <data key="d2">-73.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="53">
+      <data key="d2">-39.0</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="29" target="57">
+      <data key="d2">-49.688711258507254</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="29" target="61">
+      <data key="d2">-53.94448724536011</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="65">
+      <data key="d2">-60.267862505362984</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="69">
+      <data key="d2">-60.8452405257735</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="73">
+      <data key="d2">-15.273833230922378</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="77">
+      <data key="d2">-19.119819413322602</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="81">
+      <data key="d2">-23.29167967936833</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="29" target="85">
+      <data key="d2">-106.50211306910109</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="29" target="89">
+      <data key="d2">-46.26214454274192</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="29" target="93">
+      <data key="d2">-141.11094637627463</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="29" target="97">
+      <data key="d2">-36.849270936326754</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="29" target="Sink">
+      <data key="d2">159.6699857046035</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="33" target="1">
+      <data key="d2">-42.599242617469514</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="5">
+      <data key="d2">-47.79810430798162</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="9">
+      <data key="d2">-47.57359312880715</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="13">
+      <data key="d2">-52.30384635005847</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="33" target="17">
+      <data key="d2">-53.59945055359741</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="33" target="21">
+      <data key="d2">-64.94007182771666</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="33" target="25">
+      <data key="d2">-179.14870954182805</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="33" target="29">
+      <data key="d2">-74.37950064818669</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="37">
+      <data key="d2">-84.0</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="33" target="41">
+      <data key="d2">-61.83974431934256</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="45">
+      <data key="d2">-65.83390805281086</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="49">
+      <data key="d2">-69.3844718719117</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="53">
+      <data key="d2">-39.79043915746723</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="33" target="57">
+      <data key="d2">-49.392118991506095</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="33" target="61">
+      <data key="d2">-46.825933710154196</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="65">
+      <data key="d2">-50.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="69">
+      <data key="d2">-52.66369059481106</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="73">
+      <data key="d2">-5.406856069773596</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="77">
+      <data key="d2">-9.377422517014509</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="81">
+      <data key="d2">-12.8378331045583</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="33" target="85">
+      <data key="d2">-92.28716255425854</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="33" target="89">
+      <data key="d2">-32.12945481507885</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="33" target="93">
+      <data key="d2">-125.49740700977031</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="33" target="97">
+      <data key="d2">-21.232275012183223</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="33" target="Sink">
+      <data key="d2">175.2459729582635</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="37" target="1">
+      <data key="d2">-37.68690720728024</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="5">
+      <data key="d2">-42.83009433971698</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="9">
+      <data key="d2">-43.138501944560076</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="13">
+      <data key="d2">-49.688711258507254</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="37" target="17">
+      <data key="d2">-51.51623719021229</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="37" target="21">
+      <data key="d2">-59.536907576544365</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="37" target="25">
+      <data key="d2">-174.01511804636718</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="37" target="29">
+      <data key="d2">-69.408739718026</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="33">
+      <data key="d2">-84.0</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="37" target="41">
+      <data key="d2">-56.04414630730071</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="45">
+      <data key="d2">-60.267862505362984</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="49">
+      <data key="d2">-63.52359541025255</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="53">
+      <data key="d2">-35.32825226865342</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="37" target="57">
+      <data key="d2">-44.51373833782336</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="37" target="61">
+      <data key="d2">-40.96939731147495</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="65">
+      <data key="d2">-44.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="69">
+      <data key="d2">-46.710278356173276</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="73">
+      <data key="d2">0.553851381374173</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="77">
+      <data key="d2">-3.420556712346553</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="81">
+      <data key="d2">-6.849533976050381</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="37" target="85">
+      <data key="d2">-86.56895715935947</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="37" target="89">
+      <data key="d2">-26.39968553536862</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="37" target="93">
+      <data key="d2">-120.6987902278479</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="37" target="97">
+      <data key="d2">-16.59019139106819</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="37" target="Sink">
+      <data key="d2">179.75831174779256</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="41" target="1">
+      <data key="d2">-51.63659026541143</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="5">
+      <data key="d2">-56.265744413134</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="9">
+      <data key="d2">-51.881762894908164</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="13">
+      <data key="d2">-45.0778451095676</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="41" target="17">
+      <data key="d2">-43.761487913212434</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="41" target="21">
+      <data key="d2">-69.3844718719117</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="41" target="25">
+      <data key="d2">-177.59668181351498</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="41" target="29">
+      <data key="d2">-66.56925097228003</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="33">
+      <data key="d2">-61.83974431934256</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="41" target="37">
+      <data key="d2">-56.04414630730071</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="41" target="45">
+      <data key="d2">-84.6148351928655</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="49">
+      <data key="d2">-82.38422689413609</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="53">
+      <data key="d2">-61.53950105848459</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="41" target="57">
+      <data key="d2">-72.27995485333065</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="41" target="61">
+      <data key="d2">-74.86725404957845</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="65">
+      <data key="d2">-74.73566247752625</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="69">
+      <data key="d2">-79.55969349108945</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="73">
+      <data key="d2">-32.9649230736032</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="77">
+      <data key="d2">-36.96227757529552</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="81">
+      <data key="d2">-39.91008085452722</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="41" target="85">
+      <data key="d2">-112.25454574086133</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="41" target="89">
+      <data key="d2">-51.7900536509144</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="41" target="93">
+      <data key="d2">-137.21815771689108</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="41" target="97">
+      <data key="d2">-31.39965870406555</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="41" target="Sink">
+      <data key="d2">168.46212675456437</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="45" target="1">
+      <data key="d2">-48.14627721831925</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="5">
+      <data key="d2">-53.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="9">
+      <data key="d2">-49.207843891257724</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="13">
+      <data key="d2">-44.29442047189424</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="45" target="17">
+      <data key="d2">-43.42747591121993</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="45" target="21">
+      <data key="d2">-68.0</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="45" target="25">
+      <data key="d2">-177.3963332431559</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="45" target="29">
+      <data key="d2">-67.63932022500211</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="33">
+      <data key="d2">-65.83390805281086</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="45" target="37">
+      <data key="d2">-60.267862505362984</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="45" target="41">
+      <data key="d2">-84.6148351928655</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="49">
+      <data key="d2">-84.6148351928655</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="53">
+      <data key="d2">-61.346902436211195</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="45" target="57">
+      <data key="d2">-71.97224362268005</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="45" target="61">
+      <data key="d2">-70.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="65">
+      <data key="d2">-69.408739718026</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="69">
+      <data key="d2">-74.1886116991581</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="73">
+      <data key="d2">-28.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="77">
+      <data key="d2">-32.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="81">
+      <data key="d2">-34.773194914063694</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="45" target="85">
+      <data key="d2">-107.11784200998531</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="45" target="89">
+      <data key="d2">-46.721829983235196</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="45" target="93">
+      <data key="d2">-132.92769357920506</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="45" target="97">
+      <data key="d2">-27.35017956929166</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="45" target="Sink">
+      <data key="d2">171.7488115478018</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="49" target="1">
+      <data key="d2">-51.87825126340907</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="5">
+      <data key="d2">-56.8941092855063</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="9">
+      <data key="d2">-53.59945055359741</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="13">
+      <data key="d2">-49.55250316768663</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="49" target="17">
+      <data key="d2">-48.76894374382339</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="49" target="21">
+      <data key="d2">-72.8827572313763</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="49" target="25">
+      <data key="d2">-182.6946850868808</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="49" target="29">
+      <data key="d2">-73.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="33">
+      <data key="d2">-69.3844718719117</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="49" target="37">
+      <data key="d2">-63.52359541025255</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="49" target="41">
+      <data key="d2">-82.38422689413609</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="45">
+      <data key="d2">-84.6148351928655</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="53">
+      <data key="d2">-56.0</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="49" target="57">
+      <data key="d2">-66.6761924206188</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="49" target="61">
+      <data key="d2">-67.43897165464304</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="65">
+      <data key="d2">-69.3844718719117</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="69">
+      <data key="d2">-73.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="73">
+      <data key="d2">-25.804984617183862</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="77">
+      <data key="d2">-29.792027106038525</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="81">
+      <data key="d2">-33.0</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="49" target="85">
+      <data key="d2">-108.36281009734311</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="49" target="89">
+      <data key="d2">-48.11921681725615</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="49" target="93">
+      <data key="d2">-135.9184314708956</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="49" target="97">
+      <data key="d2">-30.63671168137668</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="49" target="Sink">
+      <data key="d2">167.77727426947757</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="53" target="1">
+      <data key="d2">-25.99173764185678</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="5">
+      <data key="d2">-29.966675920785462</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="9">
+      <data key="d2">-24.723664318529643</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="13">
+      <data key="d2">-16.624254688623438</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="53" target="17">
+      <data key="d2">-15.327381189622116</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="53" target="21">
+      <data key="d2">-40.959200659043084</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="53" target="25">
+      <data key="d2">-149.26219416596655</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="53" target="29">
+      <data key="d2">-39.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="33">
+      <data key="d2">-39.79043915746723</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="53" target="37">
+      <data key="d2">-35.32825226865342</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="53" target="41">
+      <data key="d2">-61.53950105848459</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="45">
+      <data key="d2">-61.346902436211195</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="49">
+      <data key="d2">-56.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="57">
+      <data key="d2">-79.229670385731</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="53" target="61">
+      <data key="d2">-64.29007973563512</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="65">
+      <data key="d2">-54.77217009238293</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="69">
+      <data key="d2">-59.98333796039273</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="73">
+      <data key="d2">-35.879763489060764</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="77">
+      <data key="d2">-39.39367628448002</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="81">
+      <data key="d2">-39.19645681647785</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="53" target="85">
+      <data key="d2">-95.52438300291615</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="53" target="89">
+      <data key="d2">-33.697246958963014</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="53" target="93">
+      <data key="d2">-115.40937970422267</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="53" target="97">
+      <data key="d2">-8.415687782515448</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="53" target="Sink">
+      <data key="d2">194.32178913802468</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="57" target="1">
+      <data key="d2">-35.76434703312558</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="5">
+      <data key="d2">-39.960015987212785</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="9">
+      <data key="d2">-34.963648376731946</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="13">
+      <data key="d2">-27.3581609465367</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="57" target="17">
+      <data key="d2">-26.09381876531817</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="57" target="21">
+      <data key="d2">-51.6724642065264</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="57" target="25">
+      <data key="d2">-160.0324320331586</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="57" target="29">
+      <data key="d2">-49.688711258507254</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="33">
+      <data key="d2">-49.392118991506095</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="57" target="37">
+      <data key="d2">-44.51373833782336</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="57" target="41">
+      <data key="d2">-72.27995485333065</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="45">
+      <data key="d2">-71.97224362268005</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="49">
+      <data key="d2">-66.6761924206188</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="53">
+      <data key="d2">-79.229670385731</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="57" target="61">
+      <data key="d2">-71.97224362268005</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="65">
+      <data key="d2">-63.75119050318662</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="69">
+      <data key="d2">-69.3844718719117</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="73">
+      <data key="d2">-35.879763489060764</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="77">
+      <data key="d2">-39.71083615727937</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="81">
+      <data key="d2">-40.75571099101948</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="57" target="85">
+      <data key="d2">-102.53271575762614</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="57" target="89">
+      <data key="d2">-41.23525863905356</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="57" target="93">
+      <data key="d2">-123.78937389463901</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="57" target="97">
+      <data key="d2">-17.198901107194814</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="57" target="Sink">
+      <data key="d2">184.5226195392376</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="61" target="1">
+      <data key="d2">-50.67213785647664</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="5">
+      <data key="d2">-54.09735385796752</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="9">
+      <data key="d2">-48.2387739643578</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="13">
+      <data key="d2">-37.0</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="61" target="17">
+      <data key="d2">-34.78224923088591</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="61" target="21">
+      <data key="d2">-60.267862505362984</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="61" target="25">
+      <data key="d2">-166.65570997781944</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="61" target="29">
+      <data key="d2">-53.94448724536011</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="33">
+      <data key="d2">-46.825933710154196</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="61" target="37">
+      <data key="d2">-40.96939731147495</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="61" target="41">
+      <data key="d2">-74.86725404957845</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="45">
+      <data key="d2">-70.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="49">
+      <data key="d2">-67.43897165464304</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="53">
+      <data key="d2">-64.29007973563512</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="61" target="57">
+      <data key="d2">-71.97224362268005</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="61" target="65">
+      <data key="d2">-79.80196097281443</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="69">
+      <data key="d2">-82.92893218813452</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="73">
+      <data key="d2">-48.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="77">
+      <data key="d2">-52.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="81">
+      <data key="d2">-54.64466094067262</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="61" target="85">
+      <data key="d2">-120.54700438855434</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="61" target="89">
+      <data key="d2">-59.12930191913374</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="61" target="93">
+      <data key="d2">-141.11094637627463</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="61" target="97">
+      <data key="d2">-34.09830056250526</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="61" target="Sink">
+      <data key="d2">169.5915723359632</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="65" target="1">
+      <data key="d2">-60.839417969292796</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="5">
+      <data key="d2">-64.29007973563512</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="9">
+      <data key="d2">-58.377223398316204</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="13">
+      <data key="d2">-46.39954128681672</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="65" target="17">
+      <data key="d2">-43.90227771353556</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="65" target="21">
+      <data key="d2">-68.36669234721606</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="65" target="25">
+      <data key="d2">-174.01511804636718</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="65" target="29">
+      <data key="d2">-60.267862505362984</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="33">
+      <data key="d2">-50.0</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="65" target="37">
+      <data key="d2">-44.0</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="65" target="41">
+      <data key="d2">-74.73566247752625</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="45">
+      <data key="d2">-69.408739718026</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="49">
+      <data key="d2">-69.3844718719117</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="53">
+      <data key="d2">-54.77217009238293</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="65" target="57">
+      <data key="d2">-63.75119050318662</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="65" target="61">
+      <data key="d2">-79.80196097281443</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="69">
+      <data key="d2">-84.1690481051547</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="73">
+      <data key="d2">-44.87794330928609</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="77">
+      <data key="d2">-48.76894374382339</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="81">
+      <data key="d2">-52.66369059481106</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="65" target="85">
+      <data key="d2">-127.24844451016423</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="65" target="89">
+      <data key="d2">-66.56925097228003</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="65" target="93">
+      <data key="d2">-150.01566374800876</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="65" target="97">
+      <data key="d2">-43.42747591121993</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="65" target="Sink">
+      <data key="d2">159.44298278577673</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="69" target="1">
+      <data key="d2">-55.99967364995024</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="5">
+      <data key="d2">-59.85037313663733</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="9">
+      <data key="d2">-54.3068634048505</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="13">
+      <data key="d2">-43.85891201976269</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="69" target="17">
+      <data key="d2">-41.74028595194538</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="69" target="21">
+      <data key="d2">-67.32843190249073</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="69" target="25">
+      <data key="d2">-173.7085520916995</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="69" target="29">
+      <data key="d2">-60.8452405257735</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="33">
+      <data key="d2">-52.66369059481106</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="69" target="37">
+      <data key="d2">-46.710278356173276</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="69" target="41">
+      <data key="d2">-79.55969349108945</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="45">
+      <data key="d2">-74.1886116991581</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="49">
+      <data key="d2">-73.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="53">
+      <data key="d2">-59.98333796039273</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="69" target="57">
+      <data key="d2">-69.3844718719117</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="69" target="61">
+      <data key="d2">-82.92893218813452</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="65">
+      <data key="d2">-84.1690481051547</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="73">
+      <data key="d2">-42.734790807614104</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="77">
+      <data key="d2">-46.710278356173276</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="81">
+      <data key="d2">-50.0</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="69" target="85">
+      <data key="d2">-121.56214025470007</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="69" target="89">
+      <data key="d2">-60.79383626697953</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="69" target="93">
+      <data key="d2">-144.2680695598865</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="69" target="97">
+      <data key="d2">-37.79846745544725</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="69" target="Sink">
+      <data key="d2">164.31464664527613</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="73" target="1">
+      <data key="d2">-28.559620704903935</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="5">
+      <data key="d2">-28.967221921331486</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="9">
+      <data key="d2">-22.798809534354234</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="13">
+      <data key="d2">-6.78341511453381</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="73" target="17">
+      <data key="d2">-3.3916863113014983</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="73" target="21">
+      <data key="d2">-24.2124631864059</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="73" target="25">
+      <data key="d2">-129.43786073945694</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="73" target="29">
+      <data key="d2">-15.273833230922378</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="33">
+      <data key="d2">-5.406856069773596</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="73" target="37">
+      <data key="d2">0.553851381374173</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="73" target="41">
+      <data key="d2">-32.9649230736032</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="45">
+      <data key="d2">-28.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="49">
+      <data key="d2">-25.804984617183862</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="53">
+      <data key="d2">-35.879763489060764</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="73" target="57">
+      <data key="d2">-35.879763489060764</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="73" target="61">
+      <data key="d2">-48.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="65">
+      <data key="d2">-44.87794330928609</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="69">
+      <data key="d2">-42.734790807614104</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="77">
+      <data key="d2">-86.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="81">
+      <data key="d2">-81.39767473295737</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="73" target="85">
+      <data key="d2">-117.11397033302904</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="73" target="89">
+      <data key="d2">-49.688711258507254</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="73" target="93">
+      <data key="d2">-136.84982290307</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="73" target="97">
+      <data key="d2">-26.368246920267865</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="73" target="Sink">
+      <data key="d2">189.3838405852659</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="77" target="1">
+      <data key="d2">-31.615006708335947</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="5">
+      <data key="d2">-32.1986159335263</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="9">
+      <data key="d2">-25.968757625671515</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="13">
+      <data key="d2">-10.118838265833915</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="77" target="17">
+      <data key="d2">-6.78341511453381</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="77" target="21">
+      <data key="d2">-27.967750322916707</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="77" target="25">
+      <data key="d2">-133.22655910570137</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="77" target="29">
+      <data key="d2">-19.119819413322602</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="33">
+      <data key="d2">-9.377422517014509</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="77" target="37">
+      <data key="d2">-3.420556712346553</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="77" target="41">
+      <data key="d2">-36.96227757529552</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="45">
+      <data key="d2">-32.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="49">
+      <data key="d2">-29.792027106038525</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="53">
+      <data key="d2">-39.39367628448002</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="77" target="57">
+      <data key="d2">-39.71083615727937</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="77" target="61">
+      <data key="d2">-52.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="65">
+      <data key="d2">-48.76894374382339</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="69">
+      <data key="d2">-46.710278356173276</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="73">
+      <data key="d2">-86.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="81">
+      <data key="d2">-84.1690481051547</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="77" target="85">
+      <data key="d2">-119.47720300298624</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="77" target="89">
+      <data key="d2">-52.46335124175308</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="77" target="93">
+      <data key="d2">-138.50428338807603</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="77" target="97">
+      <data key="d2">-28.28290350316211</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="77" target="Sink">
+      <data key="d2">186.55611192365785</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="81" target="1">
+      <data key="d2">-37.144105000093546</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="5">
+      <data key="d2">-37.56909308432668</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="9">
+      <data key="d2">-31.39965870406555</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="13">
+      <data key="d2">-15.374267172777465</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="81" target="17">
+      <data key="d2">-11.96795530040238</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="81" target="21">
+      <data key="d2">-32.43264814150298</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="81" target="25">
+      <data key="d2">-137.59551427783742</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="81" target="29">
+      <data key="d2">-23.29167967936833</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="33">
+      <data key="d2">-12.8378331045583</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="81" target="37">
+      <data key="d2">-6.849533976050381</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="81" target="41">
+      <data key="d2">-39.91008085452722</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="45">
+      <data key="d2">-34.773194914063694</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="49">
+      <data key="d2">-33.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="53">
+      <data key="d2">-39.19645681647785</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="81" target="57">
+      <data key="d2">-40.75571099101948</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="81" target="61">
+      <data key="d2">-54.64466094067262</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="65">
+      <data key="d2">-52.66369059481106</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="69">
+      <data key="d2">-50.0</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="73">
+      <data key="d2">-81.39767473295737</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="77">
+      <data key="d2">-84.1690481051547</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="85">
+      <data key="d2">-125.30028284196419</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="81" target="89">
+      <data key="d2">-58.1723390743209</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="81" target="93">
+      <data key="d2">-144.2680695598865</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="81" target="97">
+      <data key="d2">-34.09830056250526</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="81" target="Sink">
+      <data key="d2">180.88318914019993</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="85" target="1">
+      <data key="d2">-130.18079808380452</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="5">
+      <data key="d2">-129.05631116787868</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="9">
+      <data key="d2">-123.90993915594234</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="13">
+      <data key="d2">-107.14883414401436</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="85" target="17">
+      <data key="d2">-103.33118321307339</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="85" target="21">
+      <data key="d2">-116.69974007973093</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="85" target="25">
+      <data key="d2">-221.7522218888809</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="85" target="29">
+      <data key="d2">-106.50211306910109</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="33">
+      <data key="d2">-92.28716255425854</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="85" target="37">
+      <data key="d2">-86.56895715935947</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="85" target="41">
+      <data key="d2">-112.25454574086133</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="45">
+      <data key="d2">-107.11784200998531</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="49">
+      <data key="d2">-108.36281009734311</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="53">
+      <data key="d2">-95.52438300291615</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="85" target="57">
+      <data key="d2">-102.53271575762614</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="85" target="61">
+      <data key="d2">-120.54700438855434</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="65">
+      <data key="d2">-127.24844451016423</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="69">
+      <data key="d2">-121.56214025470007</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="73">
+      <data key="d2">-117.11397033302904</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="77">
+      <data key="d2">-119.47720300298624</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="81">
+      <data key="d2">-125.30028284196419</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="85" target="89">
+      <data key="d2">-150.14755095049162</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="85" target="93">
+      <data key="d2">-238.47307676482933</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="85" target="97">
+      <data key="d2">-129.28390626081276</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="85" target="Sink">
+      <data key="d2">86.24099132314196</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="89" target="1">
+      <data key="d2">-68.40841336797624</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="5">
+      <data key="d2">-67.86405637882135</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="9">
+      <data key="d2">-62.269150752275905</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="13">
+      <data key="d2">-45.615317957655705</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="89" target="17">
+      <data key="d2">-41.895946116777225</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="89" target="21">
+      <data key="d2">-56.45898033750316</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="89" target="25">
+      <data key="d2">-161.4865934651702</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="89" target="29">
+      <data key="d2">-46.26214454274192</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="33">
+      <data key="d2">-32.12945481507885</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="89" target="37">
+      <data key="d2">-26.39968553536862</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="89" target="41">
+      <data key="d2">-51.7900536509144</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="45">
+      <data key="d2">-46.721829983235196</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="49">
+      <data key="d2">-48.11921681725615</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="53">
+      <data key="d2">-33.697246958963014</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="89" target="57">
+      <data key="d2">-41.23525863905356</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="89" target="61">
+      <data key="d2">-59.12930191913374</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="65">
+      <data key="d2">-66.56925097228003</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="69">
+      <data key="d2">-60.79383626697953</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="73">
+      <data key="d2">-49.688711258507254</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="77">
+      <data key="d2">-52.46335124175308</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="89" target="81">
+      <data key="d2">-58.1723390743209</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="89" target="85">
+      <data key="d2">-150.14755095049162</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="89" target="93">
+      <data key="d2">-171.04574488986978</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="89" target="97">
+      <data key="d2">-62.833844585587755</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="89" target="Sink">
+      <data key="d2">149.07348345155603</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="93" target="1">
+      <data key="d2">-170.8705817843684</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="5">
+      <data key="d2">-167.6402163229752</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="9">
+      <data key="d2">-167.23282720827052</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="13">
+      <data key="d2">-153.9722375219433</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="93" target="17">
+      <data key="d2">-150.06766252429568</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="93" target="21">
+      <data key="d2">-150.01566374800876</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="93" target="25">
+      <data key="d2">-256.2215105698877</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="93" target="29">
+      <data key="d2">-141.11094637627463</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="33">
+      <data key="d2">-125.49740700977031</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="93" target="37">
+      <data key="d2">-120.6987902278479</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="93" target="41">
+      <data key="d2">-137.21815771689108</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="45">
+      <data key="d2">-132.92769357920506</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="49">
+      <data key="d2">-135.9184314708956</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="53">
+      <data key="d2">-115.40937970422267</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="93" target="57">
+      <data key="d2">-123.78937389463901</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="93" target="61">
+      <data key="d2">-141.11094637627463</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="65">
+      <data key="d2">-150.01566374800876</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="69">
+      <data key="d2">-144.2680695598865</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="73">
+      <data key="d2">-136.84982290307</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="77">
+      <data key="d2">-138.50428338807603</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="81">
+      <data key="d2">-144.2680695598865</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="93" target="85">
+      <data key="d2">-238.47307676482933</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="93" target="89">
+      <data key="d2">-171.04574488986978</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="93" target="97">
+      <data key="d2">-193.11971763123154</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="93" target="Sink">
+      <data key="d2">38.55560878904249</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="97" target="1">
+      <data key="d2">-66.32810555244097</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="5">
+      <data key="d2">-63.09275190585258</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="9">
+      <data key="d2">-63.3729460886113</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="13">
+      <data key="d2">-50.706234591223</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="97" target="17">
+      <data key="d2">-46.825933710154196</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="97" target="21">
+      <data key="d2">-45.401793758044484</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="97" target="25">
+      <data key="d2">-151.8533077052901</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="97" target="29">
+      <data key="d2">-36.849270936326754</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="33">
+      <data key="d2">-21.232275012183223</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="97" target="37">
+      <data key="d2">-16.59019139106819</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="97" target="41">
+      <data key="d2">-31.39965870406555</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="45">
+      <data key="d2">-27.35017956929166</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="49">
+      <data key="d2">-30.63671168137668</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="53">
+      <data key="d2">-8.415687782515448</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="97" target="57">
+      <data key="d2">-17.198901107194814</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="97" target="61">
+      <data key="d2">-34.09830056250526</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="65">
+      <data key="d2">-43.42747591121993</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="69">
+      <data key="d2">-37.79846745544725</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="73">
+      <data key="d2">-26.368246920267865</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="77">
+      <data key="d2">-28.28290350316211</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="81">
+      <data key="d2">-34.09830056250526</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="97" target="85">
+      <data key="d2">-129.28390626081276</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="97" target="89">
+      <data key="d2">-62.833844585587755</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="97" target="93">
+      <data key="d2">-193.11971763123154</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="97" target="Sink">
+      <data key="d2">142.34240302259607</data>
+      <data key="d3">0.0</data>
+    </edge>
+    <edge source="Source" target="1">
+      <data key="d2">132.3149729953259</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="5">
+      <data key="d2">134.75495315418667</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="9">
+      <data key="d2">132.91697191231876</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="13">
+      <data key="d2">147.31464664527613</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="Source" target="17">
+      <data key="d2">151.31464664527613</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="Source" target="21">
+      <data key="d2">151.77370708076808</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="Source" target="25">
+      <data key="d2">44.836786365019606</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="Source" target="29">
+      <data key="d2">159.6699857046035</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="33">
+      <data key="d2">175.2459729582635</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="Source" target="37">
+      <data key="d2">179.75831174779256</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="Source" target="41">
+      <data key="d2">168.46212675456437</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="45">
+      <data key="d2">171.7488115478018</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="49">
+      <data key="d2">167.77727426947757</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="53">
+      <data key="d2">194.32178913802468</data>
+      <data key="d3">20.0</data>
+    </edge>
+    <edge source="Source" target="57">
+      <data key="d2">184.5226195392376</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="Source" target="61">
+      <data key="d2">169.5915723359632</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="65">
+      <data key="d2">159.44298278577673</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="69">
+      <data key="d2">164.31464664527613</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="73">
+      <data key="d2">189.3838405852659</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="77">
+      <data key="d2">186.55611192365785</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="81">
+      <data key="d2">180.88318914019993</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="Source" target="85">
+      <data key="d2">86.24099132314196</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <edge source="Source" target="89">
+      <data key="d2">149.07348345155603</data>
+      <data key="d3">10.0</data>
+    </edge>
+    <edge source="Source" target="93">
+      <data key="d2">38.55560878904249</data>
+      <data key="d3">40.0</data>
+    </edge>
+    <edge source="Source" target="97">
+      <data key="d2">142.34240302259607</data>
+      <data key="d3">30.0</data>
+    </edge>
+    <data key="d0">True</data>
+    <data key="d1">1</data>
+  </graph>
+</graphml>
diff --git a/test/python/tests_bidirectional.py b/test/python/tests_bidirectional.py
index c431286..3a45b08 100644
--- a/test/python/tests_bidirectional.py
+++ b/test/python/tests_bidirectional.py
@@ -18,104 +18,112 @@ def setUp(self):
         self.max_res, self.min_res = [4, 20], [0, 0]
         # Create simple digraph with appropriate attributes
         self.G = DiGraph(directed=True, n_res=2)
-        self.G.add_edge('Source', 'A', res_cost=[1, 2], weight=-1)
-        self.G.add_edge('A', 'B', res_cost=[1, 0.3], weight=-1)
-        self.G.add_edge('B', 'C', res_cost=[1, 3], weight=-10)
-        self.G.add_edge('B', 'Sink', res_cost=[1, 2], weight=10)
-        self.G.add_edge('C', 'Sink', res_cost=[1, 10], weight=-1)
+        self.G.add_edge("Source", "A", res_cost=[1, 2], weight=-1)
+        self.G.add_edge("A", "B", res_cost=[1, 0.3], weight=-1)
+        self.G.add_edge("B", "C", res_cost=[1, 3], weight=-10)
+        self.G.add_edge("B", "Sink", res_cost=[1, 2], weight=10)
+        self.G.add_edge("C", "Sink", res_cost=[1, 10], weight=-1)
 
-        self.result_path = ['Source', 'A', 'B', 'C', 'Sink']
+        self.result_path = ["Source", "A", "B", "C", "Sink"]
         self.total_cost = -13
         self.consumed_resources = [4, 15.3]
 
     def test_generated(self):
         'Test method = "generated"'
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            method="generated")
+        alg = BiDirectional(self.G, self.max_res, self.min_res, method="generated")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_processed(self):
         'Test method = "processed"'
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            method="processed")
+        alg = BiDirectional(self.G, self.max_res, self.min_res, method="processed")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_unprocessed(self):
         'Test method = "unprocessed"'
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            method="unprocessed")
+        alg = BiDirectional(self.G, self.max_res, self.min_res, method="unprocessed")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_unprocessed_time_limit(self):
-        'Test time_limit parameter'
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            method="unprocessed",
-                            time_limit=0.001)
+        "Test time_limit parameter"
+        alg = BiDirectional(
+            self.G, self.max_res, self.min_res, method="unprocessed", time_limit=0.001
+        )
         start = time()
         alg.run()
         self.assertTrue(time() - start <= 0.001 + 1e-3)
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_unprocessed_threshold(self):
-        'Test threshold parameter'
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            method="unprocessed",
-                            threshold=0)
+        "Test threshold parameter"
+        alg = BiDirectional(
+            self.G, self.max_res, self.min_res, method="unprocessed", threshold=0
+        )
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
+
+    def test_elementary(self):
+        "Test elementary parameter"
+        alg = BiDirectional(self.G, self.max_res, self.min_res, elementary=True)
+        alg.run()
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
+
+    def test_two_cycle_elimination(self):
+        "Test two_cycle_elimination parameter"
+        alg = BiDirectional(
+            self.G, self.max_res, self.min_res, two_cycle_elimination=True
+        )
+        alg.run()
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_unprocessed_time_limit_threshold(self):
-        'Test time_limit and threshold parameters'
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            method="unprocessed",
-                            time_limit=0.001,
-                            threshold=0)
+        "Test time_limit and threshold parameters"
+        alg = BiDirectional(
+            self.G,
+            self.max_res,
+            self.min_res,
+            method="unprocessed",
+            time_limit=0.001,
+            threshold=0,
+        )
         start = time()
         alg.run()
         self.assertTrue(time() - start <= 0.001 + 1e-3)
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_forward(self):
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='forward')
+        alg = BiDirectional(self.G, self.max_res, self.min_res, direction="forward")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_backward(self):
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='backward')
+        alg = BiDirectional(self.G, self.max_res, self.min_res, direction="backward")
         # Check path
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_input_exceptions(self):
         # Check whether wrong input raises exceptions
-        self.assertRaises(Exception, BiDirectional, self.G, 'x', [1, 'foo'],
-                          'up')
+        self.assertRaises(Exception, BiDirectional, self.G, "x", [1, "foo"], "up")
diff --git a/test/python/tests_grasp.py b/test/python/tests_grasp.py
index 7a043b5..86d7092 100644
--- a/test/python/tests_grasp.py
+++ b/test/python/tests_grasp.py
@@ -17,48 +17,44 @@ def setUp(self):
 
         # Create digraph with a resource infeasible minimum cost path
         self.G = DiGraph(directed=True, n_res=2)
-        self.G.add_edge('Source', 'A', res_cost=array([1, 1]), weight=1)
-        self.G.add_edge('Source', 'B', res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("Source", "A", res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("Source", "B", res_cost=array([1, 1]), weight=1)
         # Resource infeasible edge
-        self.G.add_edge('Source', 'C', res_cost=array([10, 1]), weight=10)
-        self.G.add_edge('A', 'C', res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("Source", "C", res_cost=array([10, 1]), weight=10)
+        self.G.add_edge("A", "C", res_cost=array([1, 1]), weight=1)
         # Resource infeasible edge
-        self.G.add_edge('A', 'E', res_cost=array([10, 1]), weight=10)
+        self.G.add_edge("A", "E", res_cost=array([10, 1]), weight=10)
         # Resource infeasible edge
-        self.G.add_edge('A', 'F', res_cost=array([10, 1]), weight=10)
-        self.G.add_edge('B', 'C', res_cost=array([2, 1]), weight=-1)
+        self.G.add_edge("A", "F", res_cost=array([10, 1]), weight=10)
+        self.G.add_edge("B", "C", res_cost=array([2, 1]), weight=-1)
         # Resource infeasible edge
-        self.G.add_edge('B', 'F', res_cost=array([10, 1]), weight=10)
+        self.G.add_edge("B", "F", res_cost=array([10, 1]), weight=10)
         # Resource infeasible edge
-        self.G.add_edge('B', 'E', res_cost=array([10, 1]), weight=10)
-        self.G.add_edge('C', 'D', res_cost=array([1, 1]), weight=-1)
-        self.G.add_edge('D', 'E', res_cost=array([1, 1]), weight=1)
-        self.G.add_edge('D', 'F', res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("B", "E", res_cost=array([10, 1]), weight=10)
+        self.G.add_edge("C", "D", res_cost=array([1, 1]), weight=-1)
+        self.G.add_edge("D", "E", res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("D", "F", res_cost=array([1, 1]), weight=1)
         # Resource infeasible edge
-        self.G.add_edge('D', 'Sink', res_cost=array([10, 10]), weight=10)
+        self.G.add_edge("D", "Sink", res_cost=array([10, 10]), weight=10)
         # Resource infeasible edge
-        self.G.add_edge('F', 'Sink', res_cost=array([10, 1]), weight=1)
-        self.G.add_edge('E', 'Sink', res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("F", "Sink", res_cost=array([10, 1]), weight=1)
+        self.G.add_edge("E", "Sink", res_cost=array([1, 1]), weight=1)
 
-        self.result_path = ['Source', 'A', 'C', 'D', 'E', 'Sink']
+        self.result_path = ["Source", "A", "C", "D", "E", "Sink"]
         self.total_cost = 3
         self.consumed_resources = [5, 5]
 
     def test(self):
-        alg = GRASP(self.G,
-                    self.max_res,
-                    self.min_res,
-                    max_iter=50,
-                    max_localiter=10)
+        alg = GRASP(self.G, self.max_res, self.min_res, max_iter=50, max_localiter=10)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_input_exceptions(self):
         # Check whether wrong input raises exceptions
-        self.assertRaises(Exception, GRASP, self.G, 'x', [1, 'foo'],
-                          'maxnumber')
+        self.assertRaises(Exception, GRASP, self.G, "x", [1, "foo"], "maxnumber")
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/test/python/tests_greedy_elimination.py b/test/python/tests_greedy_elimination.py
index 65966bd..2f02145 100644
--- a/test/python/tests_greedy_elimination.py
+++ b/test/python/tests_greedy_elimination.py
@@ -20,13 +20,13 @@ def setUp(self):
         self.max_res, self.min_res = [4, 20], [0, 0]
         # Create simple digraph with appropriate attributes
         self.G = DiGraph(directed=True, n_res=2)
-        self.G.add_edge('Source', 'A', res_cost=array([1, 2]), weight=-1)
-        self.G.add_edge('A', 'B', res_cost=array([1, 0.3]), weight=-1)
-        self.G.add_edge('B', 'C', res_cost=array([1, 3]), weight=-10)
-        self.G.add_edge('B', 'Sink', res_cost=array([1, 2]), weight=10)
-        self.G.add_edge('C', 'Sink', res_cost=array([1, 10]), weight=-1)
+        self.G.add_edge("Source", "A", res_cost=array([1, 2]), weight=-1)
+        self.G.add_edge("A", "B", res_cost=array([1, 0.3]), weight=-1)
+        self.G.add_edge("B", "C", res_cost=array([1, 3]), weight=-10)
+        self.G.add_edge("B", "Sink", res_cost=array([1, 2]), weight=10)
+        self.G.add_edge("C", "Sink", res_cost=array([1, 10]), weight=-1)
 
-        self.result_path = ['Source', 'A', 'B', 'C', 'Sink']
+        self.result_path = ["Source", "A", "B", "C", "Sink"]
         self.total_cost = -13
         self.consumed_resources = [4, 15.3]
 
@@ -34,82 +34,84 @@ def test_simple(self):
         'algorithm = "simple" (default)'
         alg = GreedyElim(self.G, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_astar(self):
         'algorithm = "astar"'
         alg = GreedyElim(self.G, self.max_res, self.min_res, algorithm="astar")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_time_limit(self):
-        'time limit parameter'
+        "time limit parameter"
         alg = GreedyElim(self.G, self.max_res, self.min_res, time_limit=0.001)
         start = time()
         alg.run()
         self.assertTrue(time() - start <= 0.002)
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_threshold(self):
-        'test threshold parameter'
+        "test threshold parameter"
         alg = GreedyElim(self.G, self.max_res, self.min_res, threshold=100)
         alg.run()
         self.check_result(alg, ["Source", "A", "B", "Sink"], 8, [3, 4.3])
 
     def test_time_limit_threshold(self):
-        'time limit and threshold parameters'
-        alg = GreedyElim(self.G,
-                         self.max_res,
-                         self.min_res,
-                         time_limit=0.001,
-                         threshold=100)
+        "time limit and threshold parameters"
+        alg = GreedyElim(
+            self.G, self.max_res, self.min_res, time_limit=0.001, threshold=100
+        )
         start = time()
         alg.run()
         self.assertTrue(time() - start <= 0.002)
         self.check_result(alg, ["Source", "A", "B", "Sink"], 8, [3, 4.3])
 
     def test_astar_time_limit(self):
-        'time limit parameter'
-        alg = GreedyElim(self.G,
-                         self.max_res,
-                         self.min_res,
-                         algorithm="astar",
-                         time_limit=0.001)
+        "time limit parameter"
+        alg = GreedyElim(
+            self.G, self.max_res, self.min_res, algorithm="astar", time_limit=0.001
+        )
         start = time()
         alg.run()
         self.assertTrue(time() - start <= 0.002)
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_astar_threshold(self):
-        'test threshold parameter'
-        alg = GreedyElim(self.G,
-                         self.max_res,
-                         self.min_res,
-                         algorithm="astar",
-                         threshold=100)
+        "test threshold parameter"
+        alg = GreedyElim(
+            self.G, self.max_res, self.min_res, algorithm="astar", threshold=100
+        )
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_astar_time_limit_threshold(self):
-        'time limit and threshold parameters'
-        alg = GreedyElim(self.G,
-                         self.max_res,
-                         self.min_res,
-                         algorithm="astar",
-                         time_limit=0.001,
-                         threshold=100)
+        "time limit and threshold parameters"
+        alg = GreedyElim(
+            self.G,
+            self.max_res,
+            self.min_res,
+            algorithm="astar",
+            time_limit=0.001,
+            threshold=100,
+        )
         start = time()
         alg.run()
         self.assertTrue(time() - start <= 0.002)
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_time_limit_raises(self):
-        'Time limit of 0 raises an exception'
+        "Time limit of 0 raises an exception"
         alg = GreedyElim(self.G, self.max_res, self.min_res, time_limit=0)
         self.assertRaises(Exception, alg.run)
diff --git a/test/python/tests_issue104.py b/test/python/tests_issue104.py
new file mode 100644
index 0000000..d50293c
--- /dev/null
+++ b/test/python/tests_issue104.py
@@ -0,0 +1,60 @@
+from networkx import DiGraph, read_graphml
+
+from cspy import BiDirectional
+
+from utils import TestingBase
+
+
+def load_graph():
+    G = read_graphml("../test/python/graph104.graphml")
+    for e in G.edges(data=True):
+        e[2]["res_cost"] = [e[2]["res_cost"]]
+        e[2]["weight"] = round(e[2]["weight"], 5)
+    return G
+
+
+class TestsIssue104(TestingBase):
+    """
+    Tests for issue #104
+    https://github.com/torressa/cspy/issues/104
+    """
+
+    def setUp(self):
+        self.max_res = [50]
+        self.min_res = [0]
+
+        # This works
+        self.G = load_graph()
+
+        self.result_path = ["Source", "1", "65", "69", "5", "9", "Sink"]
+        self.total_cost = -23.22
+        self.consumed_resources = [50.0]
+
+    def test_forward_elementary(self):
+        alg = BiDirectional(
+            self.G,
+            self.max_res,
+            self.min_res,
+            elementary=True,
+            direction="forward",
+        )
+
+        alg.run()
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources, almost=True
+        )
+
+    # TODO: this is very broken
+    # def test_both_elementary(self):
+    #     alg = BiDirectional(
+    #         self.G,
+    #         self.max_res,
+    #         self.min_res,
+    #         # elementary=True,
+    #         two_cycle_elimination=True,
+    #     )
+    #     alg.run()
+    #     self.check_result(
+    #         alg, self.result_path, self.total_cost, self.consumed_resources
+    #     )
+    #
diff --git a/test/python/tests_issue17.py b/test/python/tests_issue17.py
index 158cce0..b4ef64e 100644
--- a/test/python/tests_issue17.py
+++ b/test/python/tests_issue17.py
@@ -30,7 +30,7 @@ def setUp(self):
         # Maximum and minimum resource arrays
         self.max_res, self.min_res = [len(self.G.nodes()), 6], [0, 0]
         # Expected results
-        self.result_path = ['Source', 2, 5, 'Sink']
+        self.result_path = ["Source", 2, 5, "Sink"]
         self.total_cost = 1
         self.consumed_resources = [3, 3]
 
@@ -40,36 +40,30 @@ def test_bidirectional(self):
         self.assertEqual(alg.path, self.result_path)
         self.assertEqual(alg.total_cost, self.total_cost)
         self.assertTrue(alg.consumed_resources == self.consumed_resources)
-        self.assertTrue(
-            all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
+        self.assertTrue(all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
 
     def test_bidirectional_forward(self):
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='forward',
-                            elementary=True)
+        alg = BiDirectional(
+            self.G, self.max_res, self.min_res, direction="forward", elementary=True
+        )
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
-        self.assertTrue(
-            all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
+        self.assertTrue(all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
 
     def test_bidirectional_backward(self):
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='backward',
-                            elementary=True)
+        alg = BiDirectional(
+            self.G, self.max_res, self.min_res, direction="backward", elementary=True
+        )
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
-        self.assertTrue(
-            all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
+        self.assertTrue(all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
 
     def test_tabu(self):
         alg = Tabu(self.G, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, ['Source', 2, 5, 4, 'Sink'], 1, [4, 4])
-        self.assertTrue(
-            all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
+        self.check_result(alg, ["Source", 2, 5, 4, "Sink"], 1, [4, 4])
+        self.assertTrue(all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
diff --git a/test/python/tests_issue20.py b/test/python/tests_issue20.py
index 688771b..5c5092f 100644
--- a/test/python/tests_issue20.py
+++ b/test/python/tests_issue20.py
@@ -26,7 +26,7 @@ def setUp(self):
         # Maximum and minimum resource arrays
         self.max_res, self.min_res = [len(self.G.edges()), 2], [0, 0]
         # Expected results
-        self.result_path = ['Source', 2, 1, 'Sink']
+        self.result_path = ["Source", 2, 1, "Sink"]
         self.total_cost = -10
         self.consumed_resources = [3, 2]
 
@@ -37,10 +37,10 @@ def test_bidirectional(self):
         """
         alg = BiDirectional(self.G, self.max_res, self.min_res, elementary=True)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
-        self.assertTrue(
-            all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
+        self.assertTrue(all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
 
     def test_tabu(self):
         """
@@ -48,7 +48,7 @@ def test_tabu(self):
         """
         alg = Tabu(self.G, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
-        self.assertTrue(
-            all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
+        self.assertTrue(all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
diff --git a/test/python/tests_issue22.py b/test/python/tests_issue22.py
index 63ae953..08f9e91 100644
--- a/test/python/tests_issue22.py
+++ b/test/python/tests_issue22.py
@@ -27,60 +27,55 @@ def setUp(self):
         # Maximum and minimum resource arrays
         self.max_res, self.min_res = [len(self.G.edges()), 2], [0, 0]
         # Expected results
-        self.result_path = ['Source', 2, 1, 'Sink']
+        self.result_path = ["Source", 2, 1, "Sink"]
         self.total_cost = -10
         self.consumed_resources = [3, 2]
 
     def test_tabu(self):
-        """Find shortest path of simple test digraph using Tabu.
-        """
+        """Find shortest path of simple test digraph using Tabu."""
         alg = Tabu(self.G, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
-        self.assertTrue(
-            all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
+        self.assertTrue(all(e in self.G.edges() for e in zip(alg.path, alg.path[1:])))
         # Check networkx's astar_path
         path_star = astar_path(self.G, "Source", "Sink")
-        self.assertEqual(path_star, ['Source', 1, 'Sink'])
+        self.assertEqual(path_star, ["Source", 1, "Sink"])
 
     def test_bidirectional(self):
         """
         Test BiDirectional with randomly chosen sequence of directions
         for a range of seeds.
         """
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            elementary=False)
+        alg = BiDirectional(self.G, self.max_res, self.min_res, elementary=False)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_bidirectional_forward(self):
         """
         Find shortest path using BiDirectional algorithm with only forward
         direction.
         """
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='forward',
-                            elementary=True)
+        alg = BiDirectional(
+            self.G, self.max_res, self.min_res, direction="forward", elementary=True
+        )
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_bidirectional_backward(self):
         """
         Find shortest path of simple test digraph using BiDirectional
         algorithm with only backward direction.
         """
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='backward',
-                            elementary=True)
+        alg = BiDirectional(
+            self.G, self.max_res, self.min_res, direction="backward", elementary=True
+        )
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
diff --git a/test/python/tests_issue25.py b/test/python/tests_issue25.py
index 23eb0da..bc93299 100644
--- a/test/python/tests_issue25.py
+++ b/test/python/tests_issue25.py
@@ -17,18 +17,19 @@ def setUp(self):
         self.max_res, self.min_res = [4, 20], [0, 0]
         # Create simple digraph with appropriate attributes
         self.G = DiGraph(directed=True, n_res=2)
-        self.G.add_edge('Source', 'A', res_cost=array([1, 2]), weight=-1)
-        self.G.add_edge('A', 'B', res_cost=array([1, 0.3]), weight=-1)
-        self.G.add_edge('B', 'C', res_cost=array([1, 3]), weight=-10)
-        self.G.add_edge('B', 'Sink', res_cost=array([1, 2]), weight=10)
-        self.G.add_edge('C', 'Sink', res_cost=array([1, 10]), weight=-1)
+        self.G.add_edge("Source", "A", res_cost=array([1, 2]), weight=-1)
+        self.G.add_edge("A", "B", res_cost=array([1, 0.3]), weight=-1)
+        self.G.add_edge("B", "C", res_cost=array([1, 3]), weight=-10)
+        self.G.add_edge("B", "Sink", res_cost=array([1, 2]), weight=10)
+        self.G.add_edge("C", "Sink", res_cost=array([1, 10]), weight=-1)
         # Expected results
-        self.result_path = ['Source', 'A', 'B', 'C', 'Sink']
+        self.result_path = ["Source", "A", "B", "C", "Sink"]
         self.total_cost = -13
         self.consumed_resources = [4, 15.3]
 
     def test_bidirectional(self):
         alg = BiDirectional(self.G, self.max_res, self.min_res, elementary=True)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
diff --git a/test/python/tests_issue32.py b/test/python/tests_issue32.py
index 5528f18..090b694 100644
--- a/test/python/tests_issue32.py
+++ b/test/python/tests_issue32.py
@@ -7,41 +7,37 @@
 
 
 class MyCallback(REFCallback):
-
     def __init__(self, max_res):
         REFCallback.__init__(self)
         # Graph to be set once it has been preprocessed
         self.G: DiGraph = None
         self._max_res = max_res
 
-    def REF_fwd(self, cumul_res, tail, head, edge_res, partial_path,
-                cumul_cost):
+    def REF_fwd(self, cumul_res, tail, head, edge_res, partial_path, cumul_cost):
         res_new = list(cumul_res)
         # Monotone resource
         res_new[0] += 1.0
         _, head_original = self._get_original_node_label(tail, head)
         # Increasing resource
         if head_original != "Sink":
-            res_new[1] += float(int(head_original)**2)
+            res_new[1] += float(int(head_original) ** 2)
         # Resource reset
         res_new[2] += edge_res[1]
         return res_new
 
-    def REF_bwd(self, cumul_res, tail, head, edge_res, partial_path,
-                cumul_cost):
+    def REF_bwd(self, cumul_res, tail, head, edge_res, partial_path, cumul_cost):
         res_new = list(cumul_res)
         _, head_original = self._get_original_node_label(tail, head)
         # Monotone resource
         res_new[0] -= 1
         # Increasing resource
         if head_original != "Sink":
-            res_new[1] += float(int(head_original)**2)
+            res_new[1] += float(int(head_original) ** 2)
         # Resource reset
         res_new[2] += edge_res[1]
         return res_new
 
-    def REF_join(self, fwd_resources, bwd_resources, tail, head,
-                 edge_resources):
+    def REF_join(self, fwd_resources, bwd_resources, tail, head, edge_resources):
         # local copies
         fwd_res = list(fwd_resources)
         bwd_res = list(bwd_resources)
@@ -50,8 +46,7 @@ def REF_join(self, fwd_resources, bwd_resources, tail, head,
         merged_res = [0] * len(fwd_res)
         merged_res[0] = fwd_res[0] + bwd_res[0]
         if head_original != "Sink":
-            merged_res[1] = fwd_res[1] + bwd_res[1] + float(
-                int(head_original)**2)
+            merged_res[1] = fwd_res[1] + bwd_res[1] + float(int(head_original) ** 2)
         else:
             merged_res[1] = fwd_res[1] + bwd_res[1]
         merged_res[2] = fwd_res[2] + bwd_res[2]
@@ -77,47 +72,53 @@ def setUp(self):
         # Create simple digraph with appropriate attributes
         # No resource costs required for custom REFs
         self.G = DiGraph(directed=True, n_res=3)
-        self.G.add_edge('Source', 1, res_cost=array([0, 0, 0]), weight=-1)
+        self.G.add_edge("Source", 1, res_cost=array([0, 0, 0]), weight=-1)
         self.G.add_edge(1, 2, res_cost=array([0, 0, 0]), weight=-1)
         self.G.add_edge(2, 3, res_cost=array([0, 0, 0]), weight=-10)
         self.G.add_edge(2, 4, res_cost=array([0, 1, 0]), weight=-10)
         self.G.add_edge(3, 4, res_cost=array([0, 1, 0]), weight=-10)
-        self.G.add_edge(4, 'Sink', res_cost=array([0, 0, 0]), weight=-1)
+        self.G.add_edge(4, "Sink", res_cost=array([0, 0, 0]), weight=-1)
         # Expected results
-        self.result_path = ['Source', 1, 2, 3, 4, 'Sink']
+        self.result_path = ["Source", 1, 2, 3, 4, "Sink"]
         self.total_cost = -23
         self.consumed_resources = [5, 30, 1]
 
-        self.result_path_heur = ['Source', 1, 2, 4, 'Sink']
+        self.result_path_heur = ["Source", 1, 2, 4, "Sink"]
         self.total_cost_heur = -13
         self.consumed_resources_heur = [4, 21, 1]
 
     def test_bidirectional(self):
         """Test BiDirectional with custom callback."""
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            method="unprocessed",
-                            REF_callback=self.my_callback)
+        alg = BiDirectional(
+            self.G,
+            self.max_res,
+            self.min_res,
+            method="unprocessed",
+            REF_callback=self.my_callback,
+        )
         # Overwrite graph as original labelling won't match
         self.my_callback.G = alg.G
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_forward(self):
         """
         Test BiDirectional in the forward direction with custom callback.
         """
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction="forward",
-                            REF_callback=self.my_callback)
+        alg = BiDirectional(
+            self.G,
+            self.max_res,
+            self.min_res,
+            direction="forward",
+            REF_callback=self.my_callback,
+        )
         self.my_callback.G = alg.G
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     # def test_tabu(self):
     #     alg = Tabu(self.G,
diff --git a/test/python/tests_issue38.py b/test/python/tests_issue38.py
index 6da1dd5..a909ea7 100644
--- a/test/python/tests_issue38.py
+++ b/test/python/tests_issue38.py
@@ -20,12 +20,13 @@ def setUp(self):
         self.G = DiGraph(directed=True, n_res=2)
         self.G.add_edge("Source", "A", res_cost=array([1, 2]), weight=0)
         self.G.add_edge("A", "Sink", res_cost=array([1, 10]), weight=0)
-        self.result_path = ['Source', "A", 'Sink']
+        self.result_path = ["Source", "A", "Sink"]
         self.total_cost = 0
         self.consumed_resources = [2, 12]
 
     def test_bidirectional(self):
         alg = BiDirectional(self.G, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
diff --git a/test/python/tests_issue41.py b/test/python/tests_issue41.py
index 7c0ead5..c3a05ca 100644
--- a/test/python/tests_issue41.py
+++ b/test/python/tests_issue41.py
@@ -24,7 +24,7 @@ def setUp(self):
         self.G.add_edge("B", "Sink", res_cost=array([1, 1]), weight=5)
         self.G.add_edge("C", "Sink", res_cost=array([1, 1]), weight=0)
         # Expected results
-        self.result_path = ['Source', 'A', 'C', 'Sink']
+        self.result_path = ["Source", "A", "C", "Sink"]
         self.total_cost = 20
         self.consumed_resources = [3, 3]
 
@@ -35,23 +35,20 @@ def test_bidirectional(self):
         """
         alg = BiDirectional(self.G, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_bidirectional_forward(self):
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='forward')
+        alg = BiDirectional(self.G, self.max_res, self.min_res, direction="forward")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_bidirectional_backward(self):
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='backward')
+        alg = BiDirectional(self.G, self.max_res, self.min_res, direction="backward")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
diff --git a/test/python/tests_issue52.py b/test/python/tests_issue52.py
index 1d130ee..2139689 100644
--- a/test/python/tests_issue52.py
+++ b/test/python/tests_issue52.py
@@ -23,7 +23,7 @@ def setUp(self):
         self.G.add_edge("C", "A", res_cost=array([1, 1]), weight=-10)
         self.G.add_edge("A", "Sink", res_cost=array([1, 1]), weight=0)
         # Expected results
-        self.result_path = ['Source', 'A', 'B', 'C', 'A', 'Sink']
+        self.result_path = ["Source", "A", "B", "C", "A", "Sink"]
         self.total_cost = -30
         self.consumed_resources = [5, 5]
 
@@ -34,23 +34,20 @@ def test_bidirectional(self):
         """
         alg = BiDirectional(self.G, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_bidirectional_forward(self):
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='forward')
+        alg = BiDirectional(self.G, self.max_res, self.min_res, direction="forward")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_bidirectional_backward(self):
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='backward')
+        alg = BiDirectional(self.G, self.max_res, self.min_res, direction="backward")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
diff --git a/test/python/tests_issue69.py b/test/python/tests_issue69.py
index 3d6de18..4249dea 100644
--- a/test/python/tests_issue69.py
+++ b/test/python/tests_issue69.py
@@ -24,7 +24,7 @@ def setUp(self):
         self.G.add_edge(0, "Sink", res_cost=array([6, 3]), weight=7)
         self.G.add_edge(6, "Sink", res_cost=array([3, 8]), weight=8)
         # Expected results
-        self.result_path = ['Source', 3, 6, 'Sink']
+        self.result_path = ["Source", 3, 6, "Sink"]
         self.total_cost = 18.0
         self.consumed_resources = [18.0, 24.0]
 
@@ -35,23 +35,20 @@ def test_bidirectional(self):
         """
         alg = BiDirectional(self.G, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_forward(self):
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='forward')
+        alg = BiDirectional(self.G, self.max_res, self.min_res, direction="forward")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_backward(self):
-        alg = BiDirectional(self.G,
-                            self.max_res,
-                            self.min_res,
-                            direction='backward')
+        alg = BiDirectional(self.G, self.max_res, self.min_res, direction="backward")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
diff --git a/test/python/tests_issue79.py b/test/python/tests_issue79.py
index 473100c..34b5fba 100644
--- a/test/python/tests_issue79.py
+++ b/test/python/tests_issue79.py
@@ -18,20 +18,20 @@ def setUp(self):
         self.max_res, self.min_res = [7, 5], [1, 0]
         # Create simple digraph with appropriate attributes
         self.G = DiGraph(directed=True, n_res=2)
-        self.G.add_edge("Source", '1', res_cost=array([5, 3]), weight=1)
-        self.G.add_edge("Source", '2', res_cost=array([1, 1]), weight=2)
-        self.G.add_edge('1', "Sink", res_cost=array([5, 3]), weight=1)
-        self.G.add_edge('2', "Sink", res_cost=array([1, 1]), weight=2)
+        self.G.add_edge("Source", "1", res_cost=array([5, 3]), weight=1)
+        self.G.add_edge("Source", "2", res_cost=array([1, 1]), weight=2)
+        self.G.add_edge("1", "Sink", res_cost=array([5, 3]), weight=1)
+        self.G.add_edge("2", "Sink", res_cost=array([1, 1]), weight=2)
         self.G.add_edge("Source", "Sink", res_cost=array([2, 2]), weight=3)
         # Expected CSP results
-        self.result_path = ['Source', 'Sink']
+        self.result_path = ["Source", "Sink"]
         self.total_cost = 3.0
         self.consumed_resources = [2.0, 2.0]
         # Maximum value of position for source/sink
         self.pos_limit = -20
         # Hypothetical nodes that could be missorted
-        self.nodes = ['ZZ', 'AA', 'Sink', 'Source']
-        self.expected_nodes = ['Source', 'AA', 'ZZ', 'Sink']
+        self.nodes = ["ZZ", "AA", "Sink", "Source"]
+        self.expected_nodes = ["Source", "AA", "ZZ", "Sink"]
 
     def test_direct_connect(self):
         """
@@ -39,19 +39,17 @@ def test_direct_connect(self):
         """
         alg = PSOLGENT(self.G, self.max_res, self.min_res, seed=42)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_include_source_sink(self):
         """
         Test PSOLGENT can find path of Source directly connected to Sink
         """
-        alg = PSOLGENT(self.G,
-                       self.max_res,
-                       self.min_res,
-                       seed=42,
-                       lower_bound=-2,
-                       upper_bound=2)
+        alg = PSOLGENT(
+            self.G, self.max_res, self.min_res, seed=42, lower_bound=-2, upper_bound=2
+        )
         alg.run()
         # Check that all considered paths include source/sink
         self.assertLessEqual(np.max(alg.pos[:, [0, -1]]), self.pos_limit)
diff --git a/test/python/tests_issue82.py b/test/python/tests_issue82.py
index 409c611..d865c65 100644
--- a/test/python/tests_issue82.py
+++ b/test/python/tests_issue82.py
@@ -17,19 +17,20 @@ def setUp(self):
         self.max_res, self.min_res = [10], [0]
         # Create simple digraph with appropriate attributes
         self.G = DiGraph(directed=True, n_res=1)
-        self.G.add_edge("Source", 'A', res_cost=array([1]), weight=8)
-        self.G.add_edge('A', 'B', res_cost=array([9]), weight=8)
-        self.G.add_edge('A', 'Sink', res_cost=array([1]), weight=1)
-        self.G.add_edge('Source', 'B', res_cost=array([1]), weight=1)
-        self.G.add_edge('B', 'Sink', res_cost=array([10]), weight=1)
-        self.G.add_edge('B', 'A', res_cost=array([1]), weight=1)
+        self.G.add_edge("Source", "A", res_cost=array([1]), weight=8)
+        self.G.add_edge("A", "B", res_cost=array([9]), weight=8)
+        self.G.add_edge("A", "Sink", res_cost=array([1]), weight=1)
+        self.G.add_edge("Source", "B", res_cost=array([1]), weight=1)
+        self.G.add_edge("B", "Sink", res_cost=array([10]), weight=1)
+        self.G.add_edge("B", "A", res_cost=array([1]), weight=1)
         # Expected CSP results
-        self.result_path = ['Source', "B", "A", 'Sink']
+        self.result_path = ["Source", "B", "A", "Sink"]
         self.total_cost = 3.0
         self.consumed_resources = [3.0]
 
     def test_PSOLGENT(self):
         alg = PSOLGENT(self.G, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
diff --git a/test/python/tests_issue89.py b/test/python/tests_issue89.py
index f215c30..d3dc10f 100644
--- a/test/python/tests_issue89.py
+++ b/test/python/tests_issue89.py
@@ -23,72 +23,69 @@ def setUp(self):
 
         self.H.add_edge(0, 3, res_cost=rc, weight=1)
         self.H.add_edge(0, 5, res_cost=rc, weight=1)
-        self.H.add_edge(0, 'Sink', res_cost=rc, weight=1)
+        self.H.add_edge(0, "Sink", res_cost=rc, weight=1)
         self.H.add_edge(1, 3, res_cost=rc, weight=1)
         self.H.add_edge(1, 5, res_cost=rc, weight=1)
-        self.H.add_edge(1, 'Sink', res_cost=rc, weight=1)
+        self.H.add_edge(1, "Sink", res_cost=rc, weight=1)
         self.H.add_edge(2, 1, res_cost=rc, weight=1)
         self.H.add_edge(2, 4, res_cost=rc, weight=1)
-        self.H.add_edge(2, 'Sink', res_cost=rc, weight=1)
+        self.H.add_edge(2, "Sink", res_cost=rc, weight=1)
         self.H.add_edge(3, 1, res_cost=rc, weight=1)
         self.H.add_edge(3, 4, res_cost=rc, weight=1)
-        self.H.add_edge(3, 'Sink', res_cost=rc, weight=1)
+        self.H.add_edge(3, "Sink", res_cost=rc, weight=1)
         self.H.add_edge(4, 0, res_cost=rc, weight=1)
         self.H.add_edge(4, 2, res_cost=rc, weight=1)
-        self.H.add_edge(4, 'Sink', res_cost=rc, weight=1)
+        self.H.add_edge(4, "Sink", res_cost=rc, weight=1)
         self.H.add_edge(5, 0, res_cost=rc, weight=1)
         self.H.add_edge(5, 2, res_cost=rc, weight=1)
-        self.H.add_edge(5, 'Sink', res_cost=rc, weight=1)
-        self.H.add_edge('Source', 0, res_cost=[1, 1], weight=1)
-        self.H.add_edge('Source', 1, res_cost=rc, weight=1)
-        self.H.add_edge('Source', 2, res_cost=rc, weight=1)
-        self.H.add_edge('Source', 3, res_cost=rc, weight=1)
-        self.H.add_edge('Source', 4, res_cost=rc, weight=1)
-        self.H.add_edge('Source', 5, res_cost=rc, weight=1)
+        self.H.add_edge(5, "Sink", res_cost=rc, weight=1)
+        self.H.add_edge("Source", 0, res_cost=[1, 1], weight=1)
+        self.H.add_edge("Source", 1, res_cost=rc, weight=1)
+        self.H.add_edge("Source", 2, res_cost=rc, weight=1)
+        self.H.add_edge("Source", 3, res_cost=rc, weight=1)
+        self.H.add_edge("Source", 4, res_cost=rc, weight=1)
+        self.H.add_edge("Source", 5, res_cost=rc, weight=1)
 
         # Example 2 (subgraph of example 1)
         self.H2 = DiGraph(directed=True, n_res=2)
         self.H2.add_nodes_from([0, 1, 2, 3, 4, 5, 6, 7])
 
-        self.H2.add_edge(0, 'Sink', res_cost=rc, weight=1)
-        self.H2.add_edge(1, 'Sink', res_cost=rc, weight=1)
-        self.H2.add_edge('Source', 0, res_cost=[1, 1], weight=1)
-        self.H2.add_edge('Source', 1, res_cost=rc, weight=1)
-        self.result_path = ['Source', 0, 'Sink']
+        self.H2.add_edge(0, "Sink", res_cost=rc, weight=1)
+        self.H2.add_edge(1, "Sink", res_cost=rc, weight=1)
+        self.H2.add_edge("Source", 0, res_cost=[1, 1], weight=1)
+        self.H2.add_edge("Source", 1, res_cost=rc, weight=1)
+        self.result_path = ["Source", 0, "Sink"]
         self.total_cost = 2.0
         self.consumed_resources = [2.0, 1.0]
 
     def test_forward_elementary(self):
-        alg = BiDirectional(self.H,
-                            self.max_res,
-                            self.min_res,
-                            elementary=True,
-                            direction="forward")
+        alg = BiDirectional(
+            self.H, self.max_res, self.min_res, elementary=True, direction="forward"
+        )
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_both_elementary(self):
         alg = BiDirectional(self.H, self.max_res, self.min_res, elementary=True)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_H2_forward_elementary(self):
-        alg = BiDirectional(self.H2,
-                            self.max_res,
-                            self.min_res,
-                            elementary=True,
-                            direction="forward")
+        alg = BiDirectional(
+            self.H2, self.max_res, self.min_res, elementary=True, direction="forward"
+        )
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_H2_both_elementary(self):
-        alg = BiDirectional(self.H2,
-                            self.max_res,
-                            self.min_res,
-                            elementary=True)
+        alg = BiDirectional(self.H2, self.max_res, self.min_res, elementary=True)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
diff --git a/test/python/tests_issue90.py b/test/python/tests_issue90.py
index 7871d09..c247107 100644
--- a/test/python/tests_issue90.py
+++ b/test/python/tests_issue90.py
@@ -7,10 +7,10 @@
 
 def createG():
     H = DiGraph(directed=True, n_res=2)
-    H.add_nodes_from(['Source', 1, 2, 3, 4, 'Sink'])
+    H.add_nodes_from(["Source", 1, 2, 3, 4, "Sink"])
     H.add_edge(2, 3, res_cost=[1, 1], weight=1)
     H.add_edge(3, 4, res_cost=[1, 1], weight=1)
-    H.add_edge(4, 'Sink', res_cost=[1, 1], weight=1)
+    H.add_edge(4, "Sink", res_cost=[1, 1], weight=1)
     return H
 
 
@@ -26,80 +26,75 @@ def setUp(self):
 
         # This works
         self.H = createG()
-        self.H.add_edge('Source', 1, res_cost=[1, 1], weight=1)
+        self.H.add_edge("Source", 1, res_cost=[1, 1], weight=1)
         self.H.add_edge(1, 2, res_cost=[1, -1], weight=1)
 
         self.H2 = createG()
-        self.H2.add_edge('Source', 1, res_cost=[1, -1], weight=1)
+        self.H2.add_edge("Source", 1, res_cost=[1, -1], weight=1)
         self.H2.add_edge(1, 2, res_cost=[1, 1], weight=1)
 
-        self.result_path = ['Source', 1, 2, 3, 4, 'Sink']
+        self.result_path = ["Source", 1, 2, 3, 4, "Sink"]
         self.total_cost = 5.0
         self.consumed_resources = [5.0, 3.0]
 
     def test_forward_elementary(self):
-        alg = BiDirectional(self.H,
-                            self.max_res,
-                            self.min_res,
-                            elementary=True,
-                            direction="forward")
+        alg = BiDirectional(
+            self.H, self.max_res, self.min_res, elementary=True, direction="forward"
+        )
 
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_forward(self):
-        alg = BiDirectional(self.H,
-                            self.max_res,
-                            self.min_res,
-                            direction="forward")
+        alg = BiDirectional(self.H, self.max_res, self.min_res, direction="forward")
 
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_both_elementary(self):
         alg = BiDirectional(self.H, self.max_res, self.min_res, elementary=True)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_both(self):
         alg = BiDirectional(self.H, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_H2_forward_elementary(self):
-        alg = BiDirectional(self.H2,
-                            self.max_res,
-                            self.min_res,
-                            elementary=True,
-                            direction="forward")
+        alg = BiDirectional(
+            self.H2, self.max_res, self.min_res, elementary=True, direction="forward"
+        )
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_H2_forward(self):
-        alg = BiDirectional(self.H2,
-                            self.max_res,
-                            self.min_res,
-                            direction="forward")
+        alg = BiDirectional(self.H2, self.max_res, self.min_res, direction="forward")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_H2_both_elementary(self):
-        alg = BiDirectional(self.H2,
-                            self.max_res,
-                            self.min_res,
-                            elementary=True)
+        alg = BiDirectional(self.H2, self.max_res, self.min_res, elementary=True)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_H2_both(self):
         alg = BiDirectional(self.H2, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
diff --git a/test/python/tests_preprocessing.py b/test/python/tests_preprocessing.py
index 4d6f38f..04e42b1 100644
--- a/test/python/tests_preprocessing.py
+++ b/test/python/tests_preprocessing.py
@@ -16,42 +16,42 @@ class TestsPreprocessing(unittest.TestCase):
     def setUp(self):
         # Create digraph with negative resource costs with unreachable node 'E'
         self.G = DiGraph(directed=True, n_res=2)
-        self.G.add_edge('Source', 'A', res_cost=array([1, 2]), weight=0)
-        self.G.add_edge('A', 'C', res_cost=array([-1, 0.3]), weight=0)
-        self.G.add_edge('A', 'B', res_cost=array([-1, 3]), weight=0)
-        self.G.add_edge('B', 'D', res_cost=array([-1, 2]), weight=0)
+        self.G.add_edge("Source", "A", res_cost=array([1, 2]), weight=0)
+        self.G.add_edge("A", "C", res_cost=array([-1, 0.3]), weight=0)
+        self.G.add_edge("A", "B", res_cost=array([-1, 3]), weight=0)
+        self.G.add_edge("B", "D", res_cost=array([-1, 2]), weight=0)
         # Unreachable node E
-        self.G.add_edge('B', 'E', res_cost=array([10, 1]), weight=0)
-        self.G.add_edge('C', 'D', res_cost=array([1, 0.1]), weight=0)
-        self.G.add_edge('D', 'Sink', res_cost=array([1, 0.1]), weight=0)
+        self.G.add_edge("B", "E", res_cost=array([10, 1]), weight=0)
+        self.G.add_edge("C", "D", res_cost=array([1, 0.1]), weight=0)
+        self.G.add_edge("D", "Sink", res_cost=array([1, 0.1]), weight=0)
 
         # Create digraph with a resource infeasible minimum cost path
         self.H = MultiGraph(directed=True)
-        self.H.add_edge('Source', 'A', res_cost=array([1, 1]), weight=-1)
-        self.H.add_edge('A', 'B', res_cost=array([1, 1]), weight=-1)
-        self.H.add_edge('B', 'Sink', res_cost=array([1, 1]), weight=-1)
-        self.H.add_edge('Sink', 'Source', weight=-1)
+        self.H.add_edge("Source", "A", res_cost=array([1, 1]), weight=-1)
+        self.H.add_edge("A", "B", res_cost=array([1, 1]), weight=-1)
+        self.H.add_edge("B", "Sink", res_cost=array([1, 1]), weight=-1)
+        self.H.add_edge("Sink", "Source", weight=-1)
         self.max_res, self.min_res = ["foo"], ["bar"]
 
         # Create digraph to test issue 72
         self.F = DiGraph(n_res=1)
-        self.F.add_edge('Source', 'A', res_cost=array([1]), weight=1)
-        self.F.add_edge('A', 'B', res_cost=array([10]), weight=1)
-        self.F.add_edge('A', 'Sink', res_cost=array([1]), weight=1)
+        self.F.add_edge("Source", "A", res_cost=array([1]), weight=1)
+        self.F.add_edge("A", "B", res_cost=array([10]), weight=1)
+        self.F.add_edge("A", "Sink", res_cost=array([1]), weight=1)
 
     def testUnreachable(self):
         """
         Tests if the unreachable node 'E' is removed.
         """
         self.G = preprocess_graph(self.G, [5, 20], [0, 0], True)
-        self.assertTrue('E' not in self.G.nodes())
+        self.assertTrue("E" not in self.G.nodes())
 
     def testFindBadNode(self):
         """
         Tests if expensive node 'B' is removed
         """
         self.F = preprocess_graph(self.F, [5], [1], True)
-        self.assertTrue('B' not in self.F.nodes())
+        self.assertTrue("B" not in self.F.nodes())
 
     def testFormatting(self):
         """
@@ -62,24 +62,26 @@ def testFormatting(self):
             check(self.H, self.max_res, self.min_res, 1, "foo", "alg")
 
         self.assertTrue("REF" in str(context.exception))
-        self.assertTrue(
-            "Input must be a nx.Digraph()" in str(context.exception))
+        self.assertTrue("Input must be a nx.Digraph()" in str(context.exception))
 
         # Turn MultiGraph into DiGraph
         self.H = DiGraph(self.H)
         with self.assertRaises(Exception) as context:
             check(self.H, self.max_res, [1, 2])
         self.assertTrue(
-            "Input graph must have 'n_res' attribute" in str(context.exception))
-        self.assertTrue("Input graph must have edges with 'res_cost' attribute"
-                        in str(context.exception))
+            "Input graph must have 'n_res' attribute" in str(context.exception)
+        )
         self.assertTrue(
-            "Input lists have to be equal length" in str(context.exception))
+            "Input graph must have edges with 'res_cost' attribute"
+            in str(context.exception)
+        )
+        self.assertTrue("Input lists have to be equal length" in str(context.exception))
         with self.assertRaises(Exception) as context:
             check(self.H, self.max_res, [2], 1)
         self.assertTrue(
-            "Elements of input lists must be numbers" in str(context.exception))
+            "Elements of input lists must be numbers" in str(context.exception)
+        )
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/test/python/tests_psolgent.py b/test/python/tests_psolgent.py
index 662ff98..3b0fc8e 100644
--- a/test/python/tests_psolgent.py
+++ b/test/python/tests_psolgent.py
@@ -17,48 +17,51 @@ def setUp(self):
         self.max_res, self.min_res = [5, 5], [0, 0]
         # Create digraph with a resource infeasible minimum cost path
         self.G = DiGraph(directed=True, n_res=2)
-        self.G.add_edge('Source', 'A', res_cost=array([1, 1]), weight=1)
-        self.G.add_edge('Source', 'B', res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("Source", "A", res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("Source", "B", res_cost=array([1, 1]), weight=1)
         # Resource infeasible edge
-        self.G.add_edge('Source', 'C', res_cost=array([10, 1]), weight=10)
-        self.G.add_edge('A', 'C', res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("Source", "C", res_cost=array([10, 1]), weight=10)
+        self.G.add_edge("A", "C", res_cost=array([1, 1]), weight=1)
         # Resource infeasible edge
-        self.G.add_edge('A', 'E', res_cost=array([10, 1]), weight=10)
+        self.G.add_edge("A", "E", res_cost=array([10, 1]), weight=10)
         # Resource infeasible edge
-        self.G.add_edge('A', 'F', res_cost=array([10, 1]), weight=10)
-        self.G.add_edge('B', 'C', res_cost=array([2, 1]), weight=-1)
+        self.G.add_edge("A", "F", res_cost=array([10, 1]), weight=10)
+        self.G.add_edge("B", "C", res_cost=array([2, 1]), weight=-1)
         # Resource infeasible edge
-        self.G.add_edge('B', 'F', res_cost=array([10, 1]), weight=10)
+        self.G.add_edge("B", "F", res_cost=array([10, 1]), weight=10)
         # Resource infeasible edge
-        self.G.add_edge('B', 'E', res_cost=array([10, 1]), weight=10)
-        self.G.add_edge('C', 'D', res_cost=array([1, 1]), weight=-1)
-        self.G.add_edge('D', 'E', res_cost=array([1, 1]), weight=1)
-        self.G.add_edge('D', 'F', res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("B", "E", res_cost=array([10, 1]), weight=10)
+        self.G.add_edge("C", "D", res_cost=array([1, 1]), weight=-1)
+        self.G.add_edge("D", "E", res_cost=array([1, 1]), weight=1)
+        self.G.add_edge("D", "F", res_cost=array([1, 1]), weight=1)
         # Resource infeasible edge
-        self.G.add_edge('D', 'Sink', res_cost=array([10, 10]), weight=10)
+        self.G.add_edge("D", "Sink", res_cost=array([10, 10]), weight=10)
         # Resource infeasible edge
-        self.G.add_edge('F', 'Sink', res_cost=array([10, 1]), weight=1)
-        self.G.add_edge('E', 'Sink', res_cost=array([1, 1]), weight=1)
-        self.result_path = ['Source', 'A', 'C', 'D', 'E', 'Sink']
+        self.G.add_edge("F", "Sink", res_cost=array([10, 1]), weight=1)
+        self.G.add_edge("E", "Sink", res_cost=array([1, 1]), weight=1)
+        self.result_path = ["Source", "A", "C", "D", "E", "Sink"]
         self.total_cost = 3
         self.consumed_resources = [5, 5]
 
     def testPSOLGENT(self):
-        psolgent = PSOLGENT(self.G,
-                            self.max_res,
-                            self.min_res,
-                            neighbourhood_size=3,
-                            max_iter=50,
-                            seed=RandomState(123))
+        psolgent = PSOLGENT(
+            self.G,
+            self.max_res,
+            self.min_res,
+            neighbourhood_size=3,
+            max_iter=50,
+            seed=RandomState(123),
+        )
         # Check exception for not running first
         with self.assertRaises(Exception) as context:
             psolgent.path
         self.assertTrue("run()" in str(context.exception))
         # Run and test results
         psolgent.run()
-        self.check_result(psolgent, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            psolgent, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def testInputExceptions(self):
         # Check whether wrong input raises exceptions
-        self.assertRaises(Exception, PSOLGENT, self.G, 'x', [1, 'foo'], 'up')
+        self.assertRaises(Exception, PSOLGENT, self.G, "x", [1, "foo"], "up")
diff --git a/test/python/tests_tabu.py b/test/python/tests_tabu.py
index e609475..1f2aa84 100644
--- a/test/python/tests_tabu.py
+++ b/test/python/tests_tabu.py
@@ -20,27 +20,29 @@ def setUp(self):
         self.max_res, self.min_res = [4, 20], [0, 0]
         # Create simple digraph with appropriate attributes
         self.G = DiGraph(directed=True, n_res=2)
-        self.G.add_edge('Source', 'A', res_cost=array([1, 2]), weight=-1)
-        self.G.add_edge('A', 'B', res_cost=array([1, 0.3]), weight=-1)
-        self.G.add_edge('B', 'C', res_cost=array([1, 3]), weight=-10)
-        self.G.add_edge('B', 'Sink', res_cost=array([1, 2]), weight=10)
-        self.G.add_edge('C', 'Sink', res_cost=array([1, 10]), weight=-1)
+        self.G.add_edge("Source", "A", res_cost=array([1, 2]), weight=-1)
+        self.G.add_edge("A", "B", res_cost=array([1, 0.3]), weight=-1)
+        self.G.add_edge("B", "C", res_cost=array([1, 3]), weight=-10)
+        self.G.add_edge("B", "Sink", res_cost=array([1, 2]), weight=10)
+        self.G.add_edge("C", "Sink", res_cost=array([1, 10]), weight=-1)
 
-        self.result_path = ['Source', 'A', 'B', 'C', 'Sink']
+        self.result_path = ["Source", "A", "B", "C", "Sink"]
         self.total_cost = -13
         self.consumed_resources = [4, 15.3]
 
     def test_simple(self):
         alg = Tabu(self.G, self.max_res, self.min_res)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_astar(self):
         alg = Tabu(self.G, self.max_res, self.min_res, algorithm="astar")
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_time_limit(self):
         alg = Tabu(self.G, self.max_res, self.min_res, time_limit=0.001)
@@ -48,8 +50,9 @@ def test_time_limit(self):
         alg.run()
         # Fudge for windows workflow
         self.assertTrue(time() - start <= 0.002)
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_threshold(self):
         alg = Tabu(self.G, self.max_res, self.min_res, threshold=100)
@@ -57,54 +60,50 @@ def test_threshold(self):
         self.check_result(alg, ["Source", "A", "B", "Sink"], 8, [3, 4.3])
 
     def test_time_limit_threshold(self):
-        alg = Tabu(self.G,
-                   self.max_res,
-                   self.min_res,
-                   time_limit=0.001,
-                   threshold=0)
+        alg = Tabu(self.G, self.max_res, self.min_res, time_limit=0.001, threshold=0)
         start = time()
         alg.run()
         self.assertTrue(time() - start <= 0.002)
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_astar_time_limit(self):
-        'time limit parameter'
-        alg = Tabu(self.G,
-                   self.max_res,
-                   self.min_res,
-                   algorithm="astar",
-                   time_limit=0.001)
+        "time limit parameter"
+        alg = Tabu(
+            self.G, self.max_res, self.min_res, algorithm="astar", time_limit=0.001
+        )
         start = time()
         alg.run()
         self.assertTrue(time() - start <= 0.002)
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_astar_threshold(self):
-        'test threshold parameter'
-        alg = Tabu(self.G,
-                   self.max_res,
-                   self.min_res,
-                   algorithm="astar",
-                   threshold=100)
+        "test threshold parameter"
+        alg = Tabu(self.G, self.max_res, self.min_res, algorithm="astar", threshold=100)
         alg.run()
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_astar_time_limit_threshold(self):
-        'time limit and threshold parameters'
-        alg = Tabu(self.G,
-                   self.max_res,
-                   self.min_res,
-                   algorithm="astar",
-                   time_limit=0.001,
-                   threshold=100)
+        "time limit and threshold parameters"
+        alg = Tabu(
+            self.G,
+            self.max_res,
+            self.min_res,
+            algorithm="astar",
+            time_limit=0.001,
+            threshold=100,
+        )
         start = time()
         alg.run()
         self.assertTrue(time() - start <= 0.002)
-        self.check_result(alg, self.result_path, self.total_cost,
-                          self.consumed_resources)
+        self.check_result(
+            alg, self.result_path, self.total_cost, self.consumed_resources
+        )
 
     def test_time_limit_raises(self):
         alg = Tabu(self.G, self.max_res, self.min_res, time_limit=0)
@@ -112,4 +111,4 @@ def test_time_limit_raises(self):
 
     def testInputExceptions(self):
         # Check whether wrong input raises exceptions
-        self.assertRaises(Exception, Tabu, self.G, 'x', [1, 'foo'], 'up')
+        self.assertRaises(Exception, Tabu, self.G, "x", [1, "foo"], "up")
diff --git a/test/python/utils.py b/test/python/utils.py
index 6c985cd..79cf8f2 100644
--- a/test/python/utils.py
+++ b/test/python/utils.py
@@ -4,9 +4,10 @@
 
 
 class TestingBase(unittest.TestCase):
-
-    def check_result(self, alg, path, cost, resources) -> bool:
+    def check_result(self, alg, path, cost, resources, almost=False) -> bool:
         self.assertEqual(alg.path, path)
-        self.assertEqual(alg.total_cost, cost)
-        self.assertIsNone(
-            testing.assert_allclose(alg.consumed_resources, resources))
+        if almost:
+            self.assertAlmostEqual(alg.total_cost, cost, places=2)
+        else:
+            self.assertEqual(alg.total_cost, cost)
+        self.assertIsNone(testing.assert_allclose(alg.consumed_resources, resources))