diff --git a/src/fvm.hpp b/src/fvm.hpp
index e72bd5e82cf0098c22fc5d92d20c61d1a76e0ba9..bd1339c01c09539e52539c418728fe9952863486 100644
--- a/src/fvm.hpp
+++ b/src/fvm.hpp
@@ -2,14 +2,14 @@
 
 #include <algorithm>
 
-#include "cell.hpp"
-#include "segment.hpp"
+#include <cell.hpp>
+#include <segment.hpp>
+#include <math.hpp>
+#include <matrix.hpp>
+#include <util.hpp>
+#include <algorithms.hpp>
 
-#include "math.hpp"
-#include "util.hpp"
-#include "algorithms.hpp"
-
-#include "../vector/include/Vector.hpp"
+#include <vector/include/Vector.hpp>
 
 namespace nest {
 namespace mc {
@@ -33,7 +33,7 @@ class fvm_cell {
 
     private:
 
-    index_type parent_index_;
+    matrix<value_type, size_type> matrix_;
 };
 
 ////////////////////////////////////////////////////////////
@@ -42,8 +42,8 @@ class fvm_cell {
 
 template <typename T, typename I>
 fvm_cell<T, I>::fvm_cell(nest::mc::cell const& cell)
+:   matrix_(cell.parent_index())
 {
-    parent_index_ = cell.parent_index();
     auto const& segment_index = cell.segment_index();
     auto num_fv = segment_index.back();
 
@@ -69,7 +69,7 @@ fvm_cell<T, I>::fvm_cell(nest::mc::cell const& cell)
 
             for(auto c : cable->compartments()) {
                 auto rhs = segment_index[seg_idx] + c.index;
-                auto lhs = parent_index_[rhs];
+                auto lhs = matrix_.p()[rhs];
 
                 auto rad_C = math::mean(left(c.radius), right(c.radius));
                 auto len = c.length/2;
@@ -89,8 +89,10 @@ fvm_cell<T, I>::fvm_cell(nest::mc::cell const& cell)
         ++seg_idx;
     }
 
-    //std::cout << "areas   " << algorithms::sum(fv_volumes) << ", " << cell.volume() << "\n";
-    //std::cout << "volumes " << algorithms::sum(fv_areas)   << ", " << cell.area() << "\n";
+    for(auto i=0; i<num_fv; ++i) {
+        //auto area = fv_areas[i];
+    }
+
 }
 
 } // namespace fvm
diff --git a/src/matrix.hpp b/src/matrix.hpp
index 1b1daad2fc17af2d8a3ad87ced5fc03435deca96..cd8fc30a9c18ebb90e846c0510cca7ac81855560 100644
--- a/src/matrix.hpp
+++ b/src/matrix.hpp
@@ -1,6 +1,10 @@
 #pragma once
 
-#include "../vector/include/Vector.hpp"
+#include <type_traits>
+
+#include "util.hpp"
+
+#include <vector/include/Vector.hpp>
 
 namespace nest {
 namespace mc {
@@ -20,67 +24,144 @@ class matrix {
     using index_type  = memory::HostVector<size_type>;
     using index_view  = typename index_type::view_type;
 
+    /// construct matrix for one or more cells, with combined parent index
+    /// and a cell index
+    template <
+        typename LHS, typename RHS,
+        typename = typename
+            std::enable_if<
+                util::is_container<LHS>::value &&
+                util::is_container<RHS>::value
+            >
+    >
+    matrix(LHS&& pi, RHS&& ci)
+    :   parent_index_(std::forward<LHS>(pi))
+    ,   cell_index_(std::forward<RHS>(ci))
+    {
+        setup();
+    }
+
+    /// construct matrix for a single cell described by a parent index
+    template <
+        typename IDX,
+        typename = typename
+            std::enable_if< util::is_container<IDX>::value >
+    >
+    matrix(IDX&& pi)
+    :   parent_index_(std::forward<IDX>(pi))
+    ,   cell_index_(2)
+    {
+        cell_index_[0] = 0;
+        cell_index_[1] = size();
+        setup();
+    }
 
     /// the dimension of the matrix (i.e. the number of rows or colums)
     size_type size() const
     {
-        return parent_indices_.size();
+        return parent_index_.size();
     }
 
     /// the total memory used to store the matrix
     std::size_t memory() const
     {
-        auto s = sizeof(value_type) * data_.size();
-        s     += sizeof(size_type)  * parent_indices_.size();
-        return s + sizeof(matrix);
+        auto s = 5 * (sizeof(value_type) * size() + sizeof(vector_type));
+        s     += sizeof(size_type) * (parent_index_.size() + cell_index_.size())
+                + 2*sizeof(index_type);
+        s     += sizeof(matrix);
+        return s;
     }
 
     /// the number of cell matrices that have been packed together
     size_type num_cells() const
     {
-        return cell_index_.size();
+        return cell_index_.size() - 1;
     }
 
-    private:
+    /// the vector holding the lower part of the matrix
+    view_type l()
+    {
+        return l_;
+    }
+
+    /// the vector holding the diagonal of the matrix
+    view_type d()
+    {
+        return d_;
+    }
+
+    /// the vector holding the upper part of the matrix
+    view_type u()
+    {
+        return u_;
+    }
+
+    /// the vector holding the right hand side of the linear equation system
+    view_type rhs()
+    {
+        return rhs_;
+    }
+
+    /// the vector holding the parent index
+    index_view p()
+    {
+        return parent_index_;
+    }
 
-    /// calculate the number of values per sub-array plus padding
-    size_type reservation() const
+    /// solve the linear system
+    /// upon completion the solution is stored in the RHS storage
+    /// and can be accessed via rhs()
+    void solve()
     {
-        constexpr auto alignment = vector_type::coordinator_type::alignment();
-        auto const padding = memory::impl::get_padding<value_type>(alignment, size());
-        return size() + padding;
+        index_view const& p = parent_index_;
+        auto const ncells = num_cells();
+
+        // loop over submatrices
+        for(auto m=0; m<ncells; ++m) {
+            auto first = cell_index_[m];
+            auto last = cell_index_[m+1];
+
+            // backward sweep
+            for(auto i=last-1; i>first; --i) {
+                auto factor = l_[i] / d_[i];
+                d_[p[i]]   -= factor * u_[i];
+                rhs_[p[i]] -= factor * rhs_[i];
+            }
+            rhs_[first] /= d_[first];
+
+            // forward sweep
+            for(auto i=first+1; i<last; ++i) {
+                rhs_[i] -= u_[i] * rhs_[p[i]];
+                rhs_[i] /= d_[i];
+            }
+        }
     }
 
-    /// subdivide the memory in data_ into the sub-arrays that store the matrix
-    /// diagonals and rhs/solution vectors
+    private:
+
+    /// allocate memory for storing matrix and right hand side vector
     void setup()
     {
         const auto n = size();
-        const auto n_alloc = reservation();
-
-        // point sub-vectors into storage
-        a_   = data_(        0,   n);
-        d_   = data_(  n_alloc,   n_alloc + n);
-        b_   = data_(2*n_alloc, 2*n_alloc + n);
-        rhs_ = data_(3*n_alloc, 3*n_alloc + n);
-        v_   = data_(4*n_alloc, 4*n_alloc + n);
-    }
 
-    // the parent indices are stored as an index array
-    index_type parent_indices_;
-    index_type cell_index_;
+        l_   = vector_type(n);
+        d_   = vector_type(n);
+        u_   = vector_type(n);
+        rhs_ = vector_type(n);
+    }
 
-    size_type num_cells_;
+    /// the parent indice that describe matrix structure
+    index_type parent_index_;
 
-    // all the data fields point into a single block of storage
-    vector_type data_;
+    /// indexes that point to the start of each cell in the index
+    index_type cell_index_;
 
-    // the individual data files that point into block storage
-    view_type a_;
-    view_type d_;
-    view_type b_;
-    view_type rhs_;
-    view_type v_;
+    /// storage for lower, diagonal, upper and rhs
+    vector_type l_;
+    vector_type d_;
+    vector_type u_;
+    /// after calling solve, the solution is stored in rhs_
+    vector_type rhs_;
 };
 
 } // namespace nest
diff --git a/src/util.hpp b/src/util.hpp
index cb68c2d1f7aa05c5ad4da7a37a5a1abae11c19dc..d92392799cf98da27ee1ca62788686a308c02c1d 100644
--- a/src/util.hpp
+++ b/src/util.hpp
@@ -65,6 +65,21 @@ namespace util {
         return p.second;
     }
 
+    template <typename T>
+    struct is_std_vector : std::false_type {};
+
+    template <typename T, typename C>
+    struct is_std_vector<std::vector<T,C>> : std::true_type {};
+
+    template <typename T>
+    struct is_container :
+        std::conditional<
+             is_std_vector<typename std::decay<T>::type>::value ||
+             memory::is_array<T>::value,
+             std::true_type,
+             std::false_type
+        >::type
+    {};
 } // namespace util
 } // namespace mc
 } // namespace nest
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 15d3ec160e9f9a701564c802ee188ebd8845b48e..b555fc465f3e3292ae2fa48f11dfad17bbe0e6e5 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -13,10 +13,11 @@ set(TEST_SOURCES
     gtest-all.cpp
 
     # unit tests
+    test_algorithms.cpp
     test_cell.cpp
     test_compartments.cpp
+    test_matrix.cpp
     test_point.cpp
-    test_algorithms.cpp
     test_run.cpp
     test_segment.cpp
     test_stimulus.cpp
diff --git a/tests/test_algorithms.cpp b/tests/test_algorithms.cpp
index decf1316702dc5e04133131e838caa09e2539373..9328b315f992a9b6b12dd9e88b0e13ef6ace5d22 100644
--- a/tests/test_algorithms.cpp
+++ b/tests/test_algorithms.cpp
@@ -2,8 +2,8 @@
 
 #include <vector>
 
-#include "../src/algorithms.hpp"
-#include "../src/util.hpp"
+#include <algorithms.hpp>
+#include <util.hpp>
 
 TEST(algorithms, sum)
 {
diff --git a/tests/test_matrix.cpp b/tests/test_matrix.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ba6794f1bd4ab095cc542bee340bb8a4e5ff15cc
--- /dev/null
+++ b/tests/test_matrix.cpp
@@ -0,0 +1,123 @@
+#include <numeric>
+
+#include "gtest.h"
+
+#include <matrix.hpp>
+#include <math.hpp>
+#include <vector/include/Vector.hpp>
+
+TEST(matrix, construct_from_parent_only)
+{
+    using matrix_type = nest::mc::matrix<double, int>;
+
+    // pass parent index as a std::vector by reference
+    // the input should not be moved from
+    {
+        std::vector<int> p = {0,0,1};
+        matrix_type m{p};
+        EXPECT_EQ(m.num_cells(), 1);
+        EXPECT_EQ(m.size(), 3);
+        EXPECT_EQ(p.size(), 3);
+
+        auto mp = m.p();
+        EXPECT_EQ(mp[0], 0);
+        EXPECT_EQ(mp[1], 0);
+        EXPECT_EQ(mp[2], 1);
+    }
+
+    // pass parent index as a std::vector by rvalue reference
+    // the input should not be moved from
+    {
+        std::vector<int> p = {0,0,1};
+        matrix_type m{std::move(p)};
+        EXPECT_EQ(m.num_cells(), 1);
+        EXPECT_EQ(m.size(), 3);
+        EXPECT_EQ(p.size(), 3);
+        EXPECT_EQ(m.size(), 3);
+
+        auto mp = m.p();
+        EXPECT_EQ(mp[0], 0);
+        EXPECT_EQ(mp[1], 0);
+        EXPECT_EQ(mp[2], 1);
+    }
+
+    // pass parent index as a HostVector by reference
+    // expect that the input is not moved from
+    {
+        memory::HostVector<int> p(3, 0);
+        std::iota(p.begin()+1, p.end(), 0);
+        matrix_type m{p};
+        EXPECT_EQ(m.num_cells(), 1);
+        EXPECT_EQ(m.size(), 3);
+        EXPECT_EQ(p.size(), 3);
+
+        auto mp = m.p();
+        EXPECT_EQ(mp[0], 0);
+        EXPECT_EQ(mp[1], 0);
+        EXPECT_EQ(mp[2], 1);
+    }
+    // pass parent index as a HostVector by rvalue reference
+    // expect that the input is moved from
+    {
+        memory::HostVector<int> p(3, 0);
+        std::iota(p.begin()+1, p.end(), 0);
+        matrix_type m{std::move(p)};
+        EXPECT_EQ(m.num_cells(), 1);
+        EXPECT_EQ(m.size(), 3);
+        EXPECT_EQ(p.size(), 0); // 0 implies moved from
+
+        auto mp = m.p();
+        EXPECT_EQ(mp[0], 0);
+        EXPECT_EQ(mp[1], 0);
+        EXPECT_EQ(mp[2], 1);
+    }
+}
+
+TEST(matrix, solve)
+{
+    using matrix_type = nest::mc::matrix<double, int>;
+
+    // trivial case : 1x1 matrix
+    {
+        matrix_type m{std::vector<int>{0}};
+
+        m.d()(memory::all) =  2;
+        m.l()(memory::all) = -1;
+        m.u()(memory::all) = -1;
+        m.rhs()(memory::all) = 1;
+
+        m.solve();
+
+        EXPECT_EQ(m.rhs()[0], 0.5);
+    }
+    // matrices in the range of 2x2 to 1000x1000
+    {
+        using namespace nest::mc::math;
+        for(auto n : memory::Range(2,1001)) {
+            auto p = std::vector<int>(n);
+            std::iota(p.begin()+1, p.end(), 0);
+            matrix_type m{p};
+
+            EXPECT_EQ(m.size(), n);
+            EXPECT_EQ(m.num_cells(), 1);
+
+            m.d()(memory::all) =  2;
+            m.l()(memory::all) = -1;
+            m.u()(memory::all) = -1;
+            m.rhs()(memory::all) = 1;
+
+            m.solve();
+
+            auto x = m.rhs();
+            auto err = square(std::fabs(2.*x[0] - x[1] - 1.));
+            for(auto i : memory::Range(1,n-1)) {
+                err += square(std::fabs(2.*x[i] - x[i-1] - x[i+1] - 1.));
+            }
+            err += square(std::fabs(2.*x[n-1] - x[n-2] - 1.));
+
+            EXPECT_NEAR(0., std::sqrt(err), 1e-8);
+        }
+
+    }
+}
+
diff --git a/vector b/vector
index c2fc6318f6d65ae9d7aad60309f3b53d590a00ec..e95bcbf1de6f833b5113a82dcc6d47bb7f41c397 160000
--- a/vector
+++ b/vector
@@ -1 +1 @@
-Subproject commit c2fc6318f6d65ae9d7aad60309f3b53d590a00ec
+Subproject commit e95bcbf1de6f833b5113a82dcc6d47bb7f41c397