diff --git a/Associators/Associators/Associators/Detail/Mixin.h b/Associators/Associators/Associators/Detail/Mixin.h
new file mode 100644
index 0000000000000000000000000000000000000000..607e322370ca4b25bf226297b10c0c283f4d6b86
--- /dev/null
+++ b/Associators/Associators/Associators/Detail/Mixin.h
@@ -0,0 +1,73 @@
+#ifndef LINKERMIXIN_H
+#define LINKERMIXIN_H
+
+#include <GaudiKernel/LinkManager.h>
+#include <GaudiKernel/ContainedObject.h>
+#include <Event/LinksByKey.h>
+
+#include "OutputLinks.h"
+#include <Associators/InputLinks.h>
+
+namespace {
+   struct DefaultType {};
+}
+
+namespace Detail {
+
+template <typename Source>
+class LinkerMixin {
+public:
+
+   static std::string location(const std::string& location) 
+   {
+      return "Link/" + (location.compare(0,7,"/Event/") == 0 ? location.substr(7) 
+                                                             : location);
+   }
+   
+   // Bit of a trick here with enable if: if Target is specified,
+   // so it's not DefaultType, we assume that the user wants
+   // to use the given types for Source and Target
+   template <typename Src, typename Target = DefaultType,
+             typename std::enable_if<!std::is_same<DefaultType, Target>::value, Target>::type* = nullptr>
+   InputLinks<Src, Target> inputLinks(const LHCb::LinksByKey& links) const
+   {
+      return InputLinks<Src, Target>{links};
+   }   
+
+   // Bit of a trick here with enable if: if Target is not specified,
+   // so it's DefaultType, we assume that the user wants Source to be
+   // ContainedObject and Targe to be Target, the remaining parameter
+   // is therefore taken to be the target type.
+   template <typename Src, typename Target = DefaultType,
+             typename std::enable_if<std::is_same<DefaultType, Target>::value, Target>::type* = nullptr>
+   InputLinks<ContainedObject, Src> inputLinks(const LHCb::LinksByKey& links) const
+   {
+      return InputLinks<ContainedObject, Src>{links};
+   }                         
+   
+   // Bit of a trick here with enable if: if Target is not specified,
+   // so it's DefaultType, we assume that the user wants Src to be
+   // ContainedObject and Target to be Target, the remaining template
+   // parameter (Src) is therefore taken to be the target type.
+   template <typename Src, typename Target = DefaultType,
+             typename std::enable_if<std::is_same<DefaultType, Target>::value, Target>::type* = nullptr>
+   Detail::OutputLinks<Source, Src> outputLinks() const
+   {
+      return Detail::OutputLinks<Source, Src>{};
+   }
+
+   // Bit of a trick here with enable if: if Target is specified, so
+   // it's not DefaultType, we assume that the user wants to use the
+   // given types.
+   template <typename Src, typename Target = DefaultType,
+             typename std::enable_if<!std::is_same<DefaultType, Target>::value, Target>::type* = nullptr>
+   Detail::OutputLinks<Src, Target> outputLinks() const
+   {
+      return Detail::OutputLinks<Src, Target>{};
+   }
+   
+};
+ 
+}
+
+#endif
diff --git a/Associators/Associators/Associators/Detail/OutputLinks.h b/Associators/Associators/Associators/Detail/OutputLinks.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5dd6806f67087089fe952d50d44d02b58a7d88d
--- /dev/null
+++ b/Associators/Associators/Associators/Detail/OutputLinks.h
@@ -0,0 +1,64 @@
+#ifndef LINKERDETAILS_OUTPUTLINKS_H
+#define LINKERDETAILS_OUTPUTLINKS_H
+
+#include <Event/LinksByKey.h>
+
+namespace Detail {
+
+template <typename Source, typename Target>
+class OutputLinks final {
+public:
+
+   OutputLinks()
+   {
+      m_links.setTargetClassID(Target::classID());
+      m_links.setSourceClassID(Source::classID());
+   }
+
+   operator LHCb::LinksByKey() {
+      // This is horrible, but needed because the copy constructor of
+      // DataObject creates a new (and empty) LinkManager. The
+      // existing move constructor moves the linkmanager, which would
+      // work, expect the using auto conversion twice would result in
+      // undefined behaviour. So, create a copy, insert all the links
+      // it's and then return it.
+      LHCb::LinksByKey r{m_links};
+      for (long linkID = 0; linkID != m_links.linkMgr()->size(); ++ linkID) {
+         auto link = m_links.linkMgr()->link(linkID);
+         r.linkMgr()->addLink(link->path(), link->object());
+      }
+      return r;
+   }
+
+   const LHCb::LinksByKey& links() const { return m_links; }
+   
+   void reset() { m_links.reset(); }
+
+   void link(const Source* source, const Target* dest, double weight = 1.)
+   {
+      if (!source || !dest) return;
+
+      m_links.addReference(source->index(), m_links.linkID(source->parent()),
+                           dest->index(), m_links.linkID(dest->parent()),
+                           weight);
+   }
+
+   void link(int key, const Target* dest, double weight = 1.)
+   {
+      if (!dest) return;
+
+      m_links.addReference(key, -1, dest->index(), m_links.linkID(dest->parent()), weight);
+   }
+
+   void setIncreasingWeight() { m_links.setIncreasing(); }
+   void setDecreasingWeight() { m_links.setDecreasing(); }
+
+private:
+
+   LHCb::LinksByKey m_links;
+
+};
+
+}
+
+#endif
diff --git a/Associators/Associators/Associators/InputLinks.h b/Associators/Associators/Associators/InputLinks.h
new file mode 100644
index 0000000000000000000000000000000000000000..8adac3fd966fa616de3455f552d0606d6780d8ad
--- /dev/null
+++ b/Associators/Associators/Associators/InputLinks.h
@@ -0,0 +1,133 @@
+#ifndef LINKERDETAILS_H
+#define LINKERDETAILS_H
+
+#include <iostream>
+#include <type_traits>
+
+#include <GaudiKernel/LinkManager.h>
+#include <GaudiKernel/GaudiException.h>
+#include <GaudiKernel/IRegistry.h>
+#include <Event/LinksByKey.h>
+#include <Relations/RelationWeighted2D.h>
+
+// template <class Source, class Targed, class Enabled = void>
+// class InputLinks {};
+
+namespace {
+      
+   template <typename Source>
+   struct GetObject
+   {
+      const Source* operator()(const LHCb::LinksByKey& links,
+                               const int linkID, int index) const
+      {
+         if (linkID != -1) {
+            LinkManager::Link* link = links.linkMgr()->link(linkID);
+            ObjectContainerBase* parent = dynamic_cast<ObjectContainerBase*>(link->object());
+            return parent ? static_cast<const Source*>(parent->containedObject(index))
+               : nullptr;
+         } else {
+            return nullptr;
+         }
+      }
+   };
+}
+
+template <class Source, class Target>
+class InputLinks {
+public:
+
+   using Relations = LHCb::RelationWeighted2D<Source, Target, double>;
+   using KeyRelations = LHCb::RelationWeighted2D<unsigned int, Target, double>;
+
+   /// Standard constructor
+   InputLinks(const LHCb::LinksByKey& links)
+   {
+      //== Check proper template, only if specified.
+      if (links.sourceClassID() != Source::classID() &&
+          CLID_ContainedObject != Source::classID()) {
+         std::ostringstream message;
+         message << "Incompatible Source type; template classID is "
+                 << Source::classID() << " expected " << links.sourceClassID();
+         throw GaudiException(message.str(), "LinkedTo", StatusCode::FAILURE);
+      }
+      if (links.targetClassID() != Target::classID()) {
+         std::ostringstream message;
+         message << "Incompatible Target type; template classID is " << Target::classID()
+                 << " expected " << links.targetClassID();
+         throw GaudiException(message.str(), "LinkedTo", StatusCode::FAILURE);
+      }
+
+      // Very ugly, but there seems to be no way around the loading here.
+      const_cast<LHCb::LinksByKey&>(links).resolveLinks(links.registry()->dataSvc());
+
+      // Fill the relations table by looping over the links. The
+      // keyIndex in the LinksByKey contains pairs of source index and
+      // reference index. The reference at reference index contains
+      // the link ID of the source and target containers, the index of
+      // the target and the weight. The source and target objects can
+      // be obtained by getting container from the link manager and
+      // then the object at the respective indices.
+      const auto& refs = links.linkReference();
+
+      int srcKey = 0;
+      int refIndex = 0;
+
+      for (const auto& entry : links.keyIndex()) {
+         std::tie(srcKey, refIndex) = entry;
+         const auto& ref = refs[refIndex];
+         const auto target = GetObject<Target>{}(links, ref.linkID(), ref.objectKey());
+         if (ref.srcLinkID() != -1) {
+            const Source* source = GetObject<Source>{}(links, ref.srcLinkID(), srcKey);
+            m_relations.i_push(source, target, ref.weight());
+         } else {
+            m_keyRelations.i_push(srcKey, target, ref.weight());
+         }
+      }
+      m_relations.i_sort();
+      m_keyRelations.i_sort();
+   };
+
+   typename LHCb::RelationWeighted2D<unsigned int, Target, double>::Range from(unsigned int key) const
+   {
+      auto range = m_keyRelations.relations(key);
+      return range;
+   }
+
+   typename LHCb::RelationWeighted2D<Source, Target, double>::Range from(const Source* source) const
+   {
+      return m_relations.relations(source);
+   }
+   
+   template <typename Object, typename std::enable_if<(!std::is_same<Source, Object>::value
+                                                       && std::is_same<ContainedObject, Object>::value)
+                                                      || (!std::is_same<ContainedObject, Object>::value
+                                                          && std::is_same<Source, Object>::value)>::type>
+   typename LHCb::RelationWeighted2D<Source, Target, double>::Range from(const Object* source) const
+   {
+      return m_relations.relations(static_cast<const Source*>(source));
+   }
+
+   typename LHCb::RelationWeighted2D<Source, Target, double>::Range to(const Target* target) const
+   {
+      return m_relations.inverse().relations(target);
+   }
+
+   typename LHCb::RelationWeighted2D<Source, Target, double>::Range relations() const
+   {
+      return m_relations;
+   }
+
+   typename LHCb::RelationWeighted2D<unsigned int, Target, double>::Range keyRelations() const
+   {
+      return m_keyRelations;
+   }
+   
+private:
+
+   Relations m_relations;
+   KeyRelations m_keyRelations;
+
+};
+
+#endif
diff --git a/Associators/Associators/Associators/Linker.h b/Associators/Associators/Associators/Linker.h
new file mode 100644
index 0000000000000000000000000000000000000000..28635822ab2d5996f9c8153a22efd318ecf3d54e
--- /dev/null
+++ b/Associators/Associators/Associators/Linker.h
@@ -0,0 +1,64 @@
+#ifndef LINKER_H
+#define LINKER_H
+
+#include <GaudiKernel/KeyedContainer.h>
+#include <GaudiAlg/Transformer.h>
+#include <Event/LinksByKey.h>
+
+#include "Detail/Mixin.h"
+
+namespace Gaudi {
+namespace Functional {
+
+template <typename Signature, typename Traits_ = Traits::useDefaults> class Linker;
+
+// A base class to link reconstructed objects to MC objects to easy
+// the transition from LinkerWithKey to a future event model for the
+// MC.
+// NOTE: Several different object containers are used by recontructed
+// and MC objects, such as KeyedContainer and ObjectVector, these do
+// not contain the same public typedefs, so some specialization tricks
+// are needed to get access to the target and source types.
+template <class Out,
+          template <typename, typename...> class SourceContainer, typename Source, typename... SourceContainerArgs,
+          typename... ExtraIn, typename Traits_>
+class Linker<Out(const SourceContainer<Source, SourceContainerArgs...>&, const ExtraIn&...), Traits_>
+: public Detail::LinkerMixin<Source>,
+  public Transformer<Out(const SourceContainer<Source, SourceContainerArgs...>&, const ExtraIn&...), Traits_>
+{
+public:
+
+   // Forwarding contructor saves a lot of hassle
+   using Transformer<Out(const SourceContainer<Source, SourceContainerArgs...>&, const ExtraIn&...),
+                     Traits_>::Transformer;
+
+};
+
+template <typename Signature, typename Traits_ = Traits::useDefaults> class MultiLinker;
+
+// A base class to link reconstructed objects to MC objects to easy
+// the transition from LinkerWithKey to a future event model for the
+// MC.
+// NOTE: Several different object containers are used by recontructed
+// and MC objects, such as KeyedContainer and ObjectVector, these do
+// not contain the same public typedefs, so some specialization tricks
+// are needed to get access to the target and source types.
+template <typename... Out,
+          template <typename, typename...> class SourceContainer, typename Source, typename... SourceContainerArgs,
+          typename... ExtraIn, typename Traits_>
+class MultiLinker<std::tuple<Out...>(const SourceContainer<Source, SourceContainerArgs...>&, const ExtraIn&...), Traits_>
+: public Detail::LinkerMixin<Source>,
+  public MultiTransformer<std::tuple<Out...>(const SourceContainer<Source, SourceContainerArgs...>&, const ExtraIn&...), Traits_>
+{
+public:
+
+   // Forwarding contructor saves a lot of hassle
+   using MultiTransformer<std::tuple<Out...>(const SourceContainer<Source, SourceContainerArgs...>&, const ExtraIn&...),
+                          Traits_>::MultiTransformer;
+
+};
+
+}
+}
+
+#endif
diff --git a/Associators/Associators/CMakeLists.txt b/Associators/Associators/CMakeLists.txt
index 27ddcc8770ac1105baea287e53e7b38202043982..c992797369c87ea412138968f6f8a135e1adf279 100644
--- a/Associators/Associators/CMakeLists.txt
+++ b/Associators/Associators/CMakeLists.txt
@@ -5,13 +5,17 @@ gaudi_subdir(Associators v3r9p1)
 
 gaudi_depends_on_subdirs(Event/LinkerEvent
                          Event/MCEvent
+                         Kernel/Relations
                          GaudiAlg)
 
 find_package(Boost)
 find_package(ROOT)
 include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS})
 
+gaudi_install_headers(Associators)
+
 gaudi_add_module(Associators
                  src/*.cpp
                  LINK_LIBRARIES LinkerEvent MCEvent GaudiAlgLib)
 
+gaudi_install_python_modules()
diff --git a/Associators/Associators/python/Associators/__init__.py b/Associators/Associators/python/Associators/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c05d3f9218e89309dec12155aeed65874f289cd5
--- /dev/null
+++ b/Associators/Associators/python/Associators/__init__.py
@@ -0,0 +1,5 @@
+import cppyy
+cppyy.gbl.gSystem.Load("libGaudiKernel.so")
+cppyy.gbl.gSystem.Load("libLinkerEvent.so")
+cppyy.gbl.gInterpreter.Declare("#include <Associators/InputLinks.h>")
+InputLinks = cppyy.gbl.InputLinks
diff --git a/VP/VPAssociators/src/VPClusterLinker.cpp b/VP/VPAssociators/src/VPClusterLinker.cpp
old mode 100755
new mode 100644
diff --git a/VP/VPAssociators/src/VPClusterLinker.h b/VP/VPAssociators/src/VPClusterLinker.h
old mode 100755
new mode 100644