diff --git a/mechanisms/CMakeLists.txt b/mechanisms/CMakeLists.txt
index 01e08e46ef7379de76a78a43e372fb701bc1ab6d..8521261ebd872734f566d5ab6f77861974e33bc7 100644
--- a/mechanisms/CMakeLists.txt
+++ b/mechanisms/CMakeLists.txt
@@ -13,8 +13,10 @@ if(NMC_VECTORIZE_TARGET STREQUAL "KNL")
     set(modcc_target "avx512")
 elseif(NMC_VECTORIZE_TARGET STREQUAL "AVX")
     set(modcc_opt "-O")
+    set(modcc_target "cpu")
 elseif(NMC_VECTORIZE_TARGET STREQUAL "AVX2")
     set(modcc_opt "-O")
+    set(modcc_target "cpu")
 else()
     set(modcc_target "cpu")
 endif()
diff --git a/modcc/cprinter.cpp b/modcc/cprinter.cpp
index a30e8253718bc3921c983e6f112b69b4e6c5f0fe..d48555b4a1e50bbeefcb4c415691bb6318913c89 100644
--- a/modcc/cprinter.cpp
+++ b/modcc/cprinter.cpp
@@ -57,7 +57,6 @@ std::string CPrinter::emit_source() {
     text_.add_line("using view   = typename base::view;");
     text_.add_line("using iview  = typename base::iview;");
     text_.add_line("using const_iview = typename base::const_iview;");
-    text_.add_line("using indexed_view_type= typename base::indexed_view_type;");
     text_.add_line("using ion_type = typename base::ion_type;");
     text_.add_line();
 
@@ -589,8 +588,7 @@ void CPrinter::visit(APIMethod *e) {
                 auto const& name = var->name();
                 auto const& index_name = var->external_variable()->index_name();
                 text_.add_gutter();
-                if(var->is_read()) text_ << "const ";
-                text_ << "indexed_view_type " + index_name;
+                text_ << "auto " + index_name + " = util::indirect_view";
                 auto channel = var->external_variable()->ion_channel();
                 if(channel==ionKind::none) {
                     text_ << "(" + index_name + "_, node_index_);\n";
diff --git a/modcc/cudaprinter.cpp b/modcc/cudaprinter.cpp
index 904bf7b32a162eae55f92ce210c2afe22dce7fc0..2885a1c5be2df231df570f0f731e985c66c371ae 100644
--- a/modcc/cudaprinter.cpp
+++ b/modcc/cudaprinter.cpp
@@ -136,7 +136,7 @@ CUDAPrinter::CUDAPrinter(Module &m, bool o)
 
     text_.add_line("template<typename Backend>");
     text_.add_line("class " + class_name + " : public mechanism<Backend> {");
-    text_.add_line("public: ");
+    text_.add_line("public:");
     text_.increase_indentation();
     text_.add_line("using base = mechanism<Backend>;");
     text_.add_line("using typename base::value_type;");
@@ -148,7 +148,6 @@ CUDAPrinter::CUDAPrinter(Module &m, bool o)
     text_.add_line("using typename base::iview;");
     text_.add_line("using typename base::const_iview;");
     text_.add_line("using typename base::const_view;");
-    text_.add_line("using typename base::indexed_view_type;");
     text_.add_line("using typename base::ion_type;");
     text_.add_line("using param_pack_type = " + module_name + "_ParamPack<value_type, size_type>;");
 
diff --git a/modcc/simd_printer.hpp b/modcc/simd_printer.hpp
index 53b072221e388333215ce98e3ec87928764bce22..4c9efe965db9230924815be2afe6c2474dc11aa6 100644
--- a/modcc/simd_printer.hpp
+++ b/modcc/simd_printer.hpp
@@ -155,15 +155,13 @@ void SimdPrinter<Arch>::emit_indexed_view(LocalVariable* var,
     auto const& index_name = var->external_variable()->index_name();
     text_.add_gutter();
 
-    if (var->is_read())
-        text_ << "const ";
-
     if (decls.find(index_name) == decls.cend()) {
-        text_ << "indexed_view_type ";
+        text_ << "auto ";
         decls.insert(index_name);
     }
 
     text_ << index_name;
+    text_ << " = util::indirect_view";
     auto channel = var->external_variable()->ion_channel();
     if (channel == ionKind::none) {
         text_ << "(" + emit_member_name(index_name) + ", node_index_);\n";
diff --git a/src/backends/stimulus_gpu.hpp b/src/backends/stimulus_gpu.hpp
index 3f3cfa70e4b9a45bdfb7693353c7b241d5339451..3d2ac907fc89e9d2bd0ac188c980418c870b2706 100644
--- a/src/backends/stimulus_gpu.hpp
+++ b/src/backends/stimulus_gpu.hpp
@@ -58,7 +58,6 @@ public:
     using view   = typename base::view;
     using iview  = typename base::iview;
     using const_iview = typename base::const_iview;
-    using indexed_view_type= typename base::indexed_view_type;
     using ion_type = typename base::ion_type;
 
     stimulus(view vec_v, view vec_i, iarray&& node_index):
diff --git a/src/backends/stimulus_multicore.hpp b/src/backends/stimulus_multicore.hpp
index 59deb6e108ec46c7ff7d390f508af6e4290003ae..4ca4f0eff0dd603878458e19704e71f2cc7ce5a0 100644
--- a/src/backends/stimulus_multicore.hpp
+++ b/src/backends/stimulus_multicore.hpp
@@ -5,6 +5,7 @@
 
 #include <mechanism.hpp>
 #include <algorithms.hpp>
+#include <util/indirect.hpp>
 #include <util/pprintf.hpp>
 
 namespace nest{
@@ -24,7 +25,6 @@ public:
     using view   = typename base::view;
     using iview  = typename base::iview;
     using const_iview = typename base::const_iview;
-    using indexed_view_type= typename base::indexed_view_type;
     using ion_type = typename base::ion_type;
 
     stimulus(view vec_v, view vec_i, iarray&& node_index):
@@ -80,7 +80,7 @@ public:
         if (amplitude.size() != size()) {
             throw std::domain_error("stimulus called with mismatched parameter size\n");
         }
-        indexed_view_type vec_i(vec_i_, node_index_);
+        auto vec_i = util::indirect_view(vec_i_, node_index_);
         int n = size();
         for(int i=0; i<n; ++i) {
             if (t>=delay[i] && t<(delay[i]+duration[i])) {
diff --git a/src/indexed_view.hpp b/src/indexed_view.hpp
deleted file mode 100644
index 55c0e0a305507d99ffe9e584f0c3954ef7980816..0000000000000000000000000000000000000000
--- a/src/indexed_view.hpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-#include <memory/memory.hpp>
-
-namespace nest {
-namespace mc {
-
-template <typename Backend>
-struct indexed_view {
-    using backend = Backend;
-    using value_type  = typename backend::value_type;
-    using size_type   = typename backend::size_type;
-    using view        = typename backend::view;
-    using const_iview = typename backend::const_iview;
-    using reference   = typename view::reference;
-    using const_reference = typename view::const_reference;
-
-    view data;
-    const_iview index;
-
-    indexed_view(view v, const_iview i):
-        data(v), index(i)
-    {}
-
-    std::size_t size() const {
-        return index.size();
-    }
-
-    reference operator[] (std::size_t i) {
-        return data[index[i]];
-    }
-
-    const_reference operator[] (std::size_t i) const {
-        return data[index[i]];
-    }
-};
-
-} // namespace mc
-} // namespace nest
diff --git a/src/ion.hpp b/src/ion.hpp
index 8fbe57f86f701f726217a1041a3b4a42b2f8e64b..65ebe3c2d025b3af3130f6e50a70dcfd7df858c9 100644
--- a/src/ion.hpp
+++ b/src/ion.hpp
@@ -2,7 +2,7 @@
 
 #include <array>
 #include <memory/memory.hpp>
-#include <indexed_view.hpp>
+#include <util/indirect.hpp>
 
 namespace nest {
 namespace mc {
@@ -57,8 +57,6 @@ public :
     using view = typename backend::view;
     using const_iview = typename backend::const_iview;
 
-    using indexed_view_type = indexed_view<backend>;
-
     ion() = default;
 
     ion(const std::vector<size_type>& idx) :
diff --git a/src/mechanism.hpp b/src/mechanism.hpp
index 922d73c3b254f5560e6d682b565a2b8fb494c5de..8cde22d2f1103006de589904c23ff1bc9f2c0b65 100644
--- a/src/mechanism.hpp
+++ b/src/mechanism.hpp
@@ -5,9 +5,9 @@
 #include <string>
 #include <util/meta.hpp>
 
-#include <indexed_view.hpp>
 #include <ion.hpp>
 #include <parameter_list.hpp>
+#include <util/indirect.hpp>
 #include <util/make_unique.hpp>
 
 namespace nest {
@@ -37,8 +37,6 @@ public:
     using const_view = typename backend::const_view;
     using const_iview = typename backend::const_iview;
 
-    using indexed_view_type = indexed_view<backend>;
-
     using ion_type = ion<backend>;
 
     mechanism(view vec_v, view vec_i, iarray&& node_index):
diff --git a/src/util/indirect.hpp b/src/util/indirect.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5a9bb5b4d693ab6b14eec4e1cfea1a6d827d1484
--- /dev/null
+++ b/src/util/indirect.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+/*
+ * Given an underlying sequence S and an index map I,
+ * present an indirect view V of S such that
+ * V[i] = S[I[i]] for all valid indices i.
+ *
+ * Implemented as a transform view of the index map.
+ */
+
+#include <util/deduce_return.hpp>
+#include <util/transform.hpp>
+#include <util/meta.hpp>
+
+namespace nest {
+namespace mc {
+namespace util {
+
+// Seq: random access sequence
+
+namespace impl {
+    template <typename Data>
+    struct indirect_accessor {
+        using reference = typename util::sequence_traits<Data>::reference;
+
+        Data& data;
+        indirect_accessor(Data& data): data(data) {}
+
+        template <typename I>
+        reference operator()(const I& i) const { return data[i]; }
+    };
+}
+
+template <typename RASeq, typename Seq>
+auto indirect_view(RASeq& data, const Seq& index_map)
+DEDUCED_RETURN_TYPE(transform_view(index_map, impl::indirect_accessor<RASeq>(data)));
+
+template <typename RASeq, typename Seq>
+auto indirect_view(const RASeq& data, const Seq& index_map)
+DEDUCED_RETURN_TYPE(transform_view(index_map, impl::indirect_accessor<const RASeq>(data)));
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/transform.hpp b/src/util/transform.hpp
index dfb654fa01c95e55775d63278a37f6bbec4f177b..da9165cf085cde1766f651ab3a3f8f8a5eedb6c5 100644
--- a/src/util/transform.hpp
+++ b/src/util/transform.hpp
@@ -36,12 +36,16 @@ class transform_iterator: public iterator_adaptor<transform_iterator<I, F>, I> {
     I& inner() { return inner_; }
 
     using inner_value_type = util::decay_t<decltype(*inner_)>;
+    using raw_value_type = typename std::result_of<F (inner_value_type)>::type;
+
+    static constexpr bool present_lvalue = std::is_reference<raw_value_type>::value;
+
 
 public:
     using typename base::difference_type;
-    using value_type = util::decay_t<typename std::result_of<F (inner_value_type)>::type>;
-    using pointer = const value_type*;
-    using reference = const value_type&;
+    using value_type = util::decay_t<raw_value_type>;
+    using pointer = typename std::conditional<present_lvalue, value_type*, const value_type*>::type;
+    using reference = typename std::conditional<present_lvalue, raw_value_type, const value_type&>::type;
 
     transform_iterator() = default;
 
@@ -77,15 +81,18 @@ public:
 
     // forward and input iterator requirements
 
-    value_type operator*() const {
+    typename std::conditional<present_lvalue, reference, value_type>::type
+    operator*() const {
         return f_.cref()(*inner_);
     }
 
-    util::pointer_proxy<value_type> operator->() const {
-        return **this;
+    typename std::conditional<present_lvalue, pointer, util::pointer_proxy<value_type>>::type
+    operator->() const {
+        return pointer_impl(std::integral_constant<bool, present_lvalue>{});
     }
 
-    value_type operator[](difference_type n) const {
+    typename std::conditional<present_lvalue, reference, value_type>::type
+    operator[](difference_type n) const {
         return *(*this+n);
     }
 
@@ -101,6 +108,17 @@ public:
 
     template <typename Sentinel>
     bool operator!=(const Sentinel& s) const { return !(inner_==s); }
+
+private:
+    // helper routines for operator->(): need different implementations for
+    // lvalue and non-lvalue access.
+    util::pointer_proxy<value_type> pointer_impl(std::false_type) const {
+        return **this;
+    }
+
+    pointer pointer_impl(std::true_type) const {
+        return &(**this);
+    }
 };
 
 template <typename I, typename F>
diff --git a/tests/unit/test_transform.cpp b/tests/unit/test_transform.cpp
index e6f49a79045ea3dc655680d0f6c01121c5faf2fa..33d1522e413f6d9e6939b6a78e2dee01dd1118e3 100644
--- a/tests/unit/test_transform.cpp
+++ b/tests/unit/test_transform.cpp
@@ -5,6 +5,8 @@
 #include <vector>
 
 #include <util/range.hpp>
+#include <util/indirect.hpp>
+#include <util/span.hpp>
 #include <util/transform.hpp>
 
 using namespace nest::mc;
@@ -54,3 +56,69 @@ TEST(transform, transform_view_sentinel) {
     EXPECT_EQ("HELLO", out);
 }
 
+TEST(transform, pointer_access) {
+    struct item {
+        int x;
+    };
+    item data[3];
+
+    auto r = util::transform_view(util::make_span(0u, 3u), [&](unsigned i) -> item& { return data[i]; });
+
+    int c=10;
+    for (auto i=r.begin(); i!=r.end(); ++i) {
+        i->x = c++;
+    }
+
+    EXPECT_EQ(10, data[0].x);
+    EXPECT_EQ(11, data[1].x);
+    EXPECT_EQ(12, data[2].x);
+}
+
+TEST(transform, pointer_proxy) {
+    struct item {
+        int x;
+    };
+    auto r = util::transform_view(util::make_span(0, 3), [&](int i) { return item{13+i}; });
+
+    int c=13;
+    for (auto i=r.begin(); i!=r.end(); ++i) {
+        EXPECT_EQ(c++, i->x);
+    }
+}
+
+TEST(indirect, fwd_index) {
+    std::istringstream string_indices("5 2 3 0 1 1 4");
+    const double data[6] = {10., 11., 12., 13., 14., 15.};
+
+    auto indices = util::make_range(std::istream_iterator<int>(string_indices), std::istream_iterator<int>());
+    auto permuted = util::indirect_view(data, indices);
+
+    std::vector<double> result(permuted.begin(), permuted.end());
+    std::vector<double> expected = {15., 12., 13., 10., 11., 11., 14.};
+
+    EXPECT_EQ(expected, result);
+}
+
+TEST(indirect, modifying) {
+    unsigned map1[] = {0, 2, 4, 1, 3, 0};
+    unsigned map2[] = {0, 1, 1, 1, 2};
+
+    std::vector<double> data = {-1, -1, -1};
+
+    auto permuted = util::indirect_view(util::indirect_view(data, map2), map1);
+
+    // expected mapping:
+    // permuted[0] = data[0]
+    // permuted[1] = data[1]
+    // permuted[2] = data[2]
+    // permuted[3] = data[1]
+    // permuted[4] = data[1]
+    // permuted[5] = data[0]
+
+    for (unsigned i = 0; i<util::size(permuted); ++i) {
+        permuted[i] = 10.+i;
+    }
+    std::vector<double> expected = {15., 14., 12.};
+
+    EXPECT_EQ(expected, data);
+}