diff --git a/CMakeLists.txt b/CMakeLists.txt index 106b5d6bdb94450fb6f267c55be465e475779533..521c5a80fbc16eefaa04ef234c9b54fb2b64dc5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,13 +135,13 @@ set(USE_OPTIMIZED_KERNELS OFF CACHE BOOL "generate optimized code that vectorize # Only build modcc if it has not already been installed. # This is useful if cross compiling for KNL, when it is not desirable to compile # modcc with the same flags that are used for the KNL target. -set(use_external_modcc OFF BOOL) +set(use_external_modcc OFF) find_program(MODCC_BIN modcc) if(MODCC_BIN STREQUAL "MODCC_BIN-NOTFOUND") set(modcc "${CMAKE_BINARY_DIR}/modcc/modcc") else() + set(use_external_modcc ON) set(modcc "${MODCC_BIN}") - set(use_external_modcc ON BOOL) endif() # Validation data generation @@ -187,7 +187,7 @@ if(BUILD_VALIDATION_DATA) endif() # only compile modcc if it is not provided externally -if(use_external_modcc) +if(NOT use_external_modcc) add_subdirectory(modcc) endif() diff --git a/mechanisms/BuildModules.cmake b/mechanisms/BuildModules.cmake new file mode 100644 index 0000000000000000000000000000000000000000..88674f9a6c2b8a941b0c158753aa83ec14e48a71 --- /dev/null +++ b/mechanisms/BuildModules.cmake @@ -0,0 +1,40 @@ +include(CMakeParseArguments) + +# Uses CMake variables modcc and use_external_modcc as set in top level CMakeLists.txt + +function(build_modules) + cmake_parse_arguments(build_modules "" "TARGET;SOURCE_DIR;DEST_DIR;MECH_SUFFIX" "MODCC_FLAGS" ${ARGN}) + + foreach(mech ${build_modules_UNPARSED_ARGUMENTS}) + set(mod "${build_modules_SOURCE_DIR}/${mech}.mod") + set(hpp "${build_modules_DEST_DIR}/${mech}.hpp") + + set(depends "${mod}") + if(NOT use_external_modcc) + list(APPEND depends modcc) + endif() + + set(flags ${build_modules_MODCC_FLAGS} -o "${hpp}") + if(build_modules_MECH_SUFFIX) + list(APPEND flags -m "${mech}${build_modules_MECH_SUFFIX}") + endif() + + add_custom_command( + OUTPUT "${hpp}" + DEPENDS ${depends} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND ${modcc} ${flags} ${mod} + ) + set_source_files_properties("${hpp}" PROPERTIES GENERATED TRUE) + list(APPEND all_mod_hpps "${hpp}") + endforeach() + + # Fake target to always trigger .mod -> .hpp dependencies because wtf CMake + if (build_modules_TARGET) + set(depends ${all_mod_hpps}) + if(NOT use_external_modcc) + list(APPEND depends modcc) + endif() + add_custom_target(${build_modules_TARGET} DEPENDS ${depends}) + endif() +endfunction() diff --git a/mechanisms/CMakeLists.txt b/mechanisms/CMakeLists.txt index d7ad64000dfbc0e7946e1f87529a58b4d4106368..71c4643f049f1f94e4d53305010c72f826345109 100644 --- a/mechanisms/CMakeLists.txt +++ b/mechanisms/CMakeLists.txt @@ -1,77 +1,34 @@ +include(BuildModules.cmake) + # the list of built-in mechanisms to be provided by default set(mechanisms pas hh expsyn exp2syn) -# set the flags for the modcc compiler that converts NMODL -# files to C++/CUDA source. -set(modcc_flags "-t cpu") - +set(modcc_opt) if(USE_OPTIMIZED_KERNELS) # generate optimized kernels - set(modcc_flags ${modcc_flags} -O) -endif() - -# make path for the kernels that will be generated by modcc -file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/multicore) -if(WITH_CUDA) - file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/gpu) + set(modcc_opt "-O") endif() -# generate source for each mechanism -foreach(mech ${mechanisms}) - set(mod "${CMAKE_CURRENT_SOURCE_DIR}/mod/${mech}.mod") - set(hpp "${CMAKE_CURRENT_SOURCE_DIR}/multicore/${mech}.hpp") - if(use_external_modcc) - add_custom_command( - OUTPUT "${hpp}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND ${modcc} ${modcc_flags} ${mod} -o ${hpp} - ) - else() - add_custom_command( - OUTPUT "${hpp}" - DEPENDS modcc "${mod}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND ${modcc} ${modcc_flags} ${mod} -o ${hpp} - ) - endif() - set_source_files_properties("${hpp}" PROPERTIES GENERATED TRUE) - list(APPEND all_mod_hpps "${hpp}") -endforeach() +set(mod_srcdir "${CMAKE_CURRENT_SOURCE_DIR}/mod") -# Fake target to always trigger .mod -> .hpp dependencies because wtf CMake -add_custom_target(build_all_mods DEPENDS ${all_mod_hpps} modcc) +set(mech_dir "${CMAKE_CURRENT_SOURCE_DIR}/multicore") +file(MAKE_DIRECTORY "${mech_dir}") +build_modules( + ${mechanisms} + SOURCE_DIR "${mod_srcdir}" + DEST_DIR "${mech_dir}" + MODCC_FLAGS -t cpu ${modcc_opt} + TARGET build_all_mods +) -# oh sweet jesus, CMake is a dog's breakfast. -# that said, let'g go through the same dance to generate CUDA kernels if -# we are targetting the GPU. if(WITH_CUDA) - set(modcc_flags "-t gpu") - - if(USE_OPTIMIZED_KERNELS) - set(modcc_flags ${modcc_flags} -O) - endif() - - # generate source for each mechanism - foreach(mech ${mechanisms}) - set(mod "${CMAKE_CURRENT_SOURCE_DIR}/mod/${mech}.mod") - set(hpp "${CMAKE_CURRENT_SOURCE_DIR}/gpu/${mech}.hpp") - if(use_external_modcc) - add_custom_command( - OUTPUT "${hpp}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND ${modcc} ${modcc_flags} ${mod} -o ${hpp} - ) - else() - add_custom_command( - OUTPUT "${hpp}" - DEPENDS modparser "${mod}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND ${modcc} ${modcc_flags} ${mod} -o ${hpp} - ) - endif() - set_source_files_properties("${hpp}" PROPERTIES GENERATED TRUE) - list(APPEND all_gpu_mod_hpps "${hpp}") - endforeach() - - # Fake target to always trigger .mod -> .hpp dependencies because wtf CMake - add_custom_target(build_all_gpu_mods DEPENDS ${all_gpu_mod_hpps} modcc) + set(mech_dir "${CMAKE_CURRENT_SOURCE_DIR}/gpu") + file(MAKE_DIRECTORY "${mech_dir}") + build_modules( + ${mechanisms} + SOURCE_DIR "${mod_srcdir}" + DEST_DIR "${mech_dir}" + MODCC_FLAGS -t gpu ${modcc_opt} + TARGET build_all_gpu_mods + ) endif() + diff --git a/modcc/cprinter.cpp b/modcc/cprinter.cpp index beb97a673d28f2b6c24bbb250dd579f945dfd9cb..c588c5ec2af70a59a34c79384f2b9cf23895a980 100644 --- a/modcc/cprinter.cpp +++ b/modcc/cprinter.cpp @@ -2,6 +2,7 @@ #include "cprinter.hpp" #include "lexer.hpp" +#include "options.hpp" /****************************************************************************** CPrinter driver @@ -26,6 +27,11 @@ CPrinter::CPrinter(Module &m, bool o) } } + std::string module_name = Options::instance().modulename; + if (module_name == "") { + module_name = m.name(); + } + ////////////////////////////////////////////// ////////////////////////////////////////////// text_.add_line("#pragma once"); @@ -40,9 +46,9 @@ CPrinter::CPrinter(Module &m, bool o) ////////////////////////////////////////////// ////////////////////////////////////////////// - std::string class_name = "mechanism_" + m.name(); + std::string class_name = "mechanism_" + module_name; - text_.add_line("namespace nest{ namespace mc{ namespace mechanisms{ namespace " + m.name() + "{"); + text_.add_line("namespace nest{ namespace mc{ namespace mechanisms{ namespace " + module_name + "{"); text_.add_line(); text_.add_line("template<class Backend>"); text_.add_line("class " + class_name + " : public mechanism<Backend> {"); @@ -182,7 +188,7 @@ CPrinter::CPrinter(Module &m, bool o) text_.add_line("std::string name() const override {"); text_.increase_indentation(); - text_.add_line("return \"" + m.name() + "\";"); + text_.add_line("return \"" + module_name + "\";"); text_.decrease_indentation(); text_.add_line("}"); text_.add_line(); @@ -861,4 +867,3 @@ void CPrinter::visit(BinaryExpression *e) { // reset parent precedence parent_op_ = pop; } - diff --git a/modcc/cudaprinter.cpp b/modcc/cudaprinter.cpp index c6c33ead52ef2131d3efc6582a603026ee5f3868..23246ea69295163bc883494f9e11d49787471a09 100644 --- a/modcc/cudaprinter.cpp +++ b/modcc/cudaprinter.cpp @@ -3,6 +3,7 @@ #include "cprinter.hpp" // needed for printing net_receive method #include "cudaprinter.hpp" #include "lexer.hpp" +#include "options.hpp" /****************************************************************************** ******************************************************************************/ @@ -26,6 +27,11 @@ CUDAPrinter::CUDAPrinter(Module &m, bool o) } } + std::string module_name = Options::instance().modulename; + if (module_name == "") { + module_name = m.name(); + } + ////////////////////////////////////////////// // header files ////////////////////////////////////////////// @@ -39,7 +45,7 @@ CUDAPrinter::CUDAPrinter(Module &m, bool o) text_.add_line("#include <util/pprintf.hpp>"); text_.add_line(); - text_.add_line("namespace nest{ namespace mc{ namespace mechanisms{ namespace gpu{ namespace " + m.name() + "{"); + text_.add_line("namespace nest{ namespace mc{ namespace mechanisms{ namespace gpu{ namespace " + module_name + "{"); text_.add_line(); increase_indentation(); @@ -48,7 +54,7 @@ CUDAPrinter::CUDAPrinter(Module &m, bool o) //////////////////////////////////////////////////////////// std::vector<std::string> param_pack; text_.add_line("template <typename T, typename I>"); - text_.add_gutter() << "struct " << m.name() << "_ParamPack {"; + text_.add_gutter() << "struct " << module_name << "_ParamPack {"; text_.end_line(); text_.increase_indentation(); text_.add_line("// array parameters"); @@ -141,7 +147,7 @@ CUDAPrinter::CUDAPrinter(Module &m, bool o) ////////////////////////////////////////////// ////////////////////////////////////////////// - std::string class_name = "mechanism_" + m.name(); + std::string class_name = "mechanism_" + module_name; text_.add_line("template<typename Backend>"); text_.add_line("class " + class_name + " : public mechanism<Backend> {"); @@ -159,7 +165,7 @@ CUDAPrinter::CUDAPrinter(Module &m, bool o) 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 = " + m.name() + "_ParamPack<value_type, size_type>;"); + text_.add_line("using param_pack_type = " + module_name + "_ParamPack<value_type, size_type>;"); ////////////////////////////////////////////// ////////////////////////////////////////////// @@ -286,7 +292,7 @@ CUDAPrinter::CUDAPrinter(Module &m, bool o) // name member function text_.add_line("std::string name() const override {"); text_.increase_indentation(); - text_.add_line("return \"" + m.name() + "\";"); + text_.add_line("return \"" + module_name + "\";"); text_.decrease_indentation(); text_.add_line("}"); text_.add_line(); @@ -420,7 +426,7 @@ CUDAPrinter::CUDAPrinter(Module &m, bool o) ////////////////////////////////////////////// ////////////////////////////////////////////// for(auto const &var : m.symbols()) { - if( var.second->kind()==symbolKind::procedure && + if( var.second->kind()==symbolKind::procedure && var.second->is_procedure()->kind()==procedureKind::api) { auto proc = var.second->is_api_method(); @@ -884,4 +890,3 @@ void CUDAPrinter::visit(BinaryExpression *e) { // reset parent precedence parent_op_ = pop; } - diff --git a/modcc/modcc.cpp b/modcc/modcc.cpp index eb2b29e9351e927b7c32d0a24ffd0ba464d1a547..5a642cec5e3565f30fd52a75ba3cd44f8bcbc8cb 100644 --- a/modcc/modcc.cpp +++ b/modcc/modcc.cpp @@ -11,37 +11,12 @@ #include "parser.hpp" #include "perfvisitor.hpp" #include "modccutil.hpp" +#include "options.hpp" //#define VERBOSE -enum class targetKind {cpu, gpu}; - -struct Options { - std::string filename; - std::string outputname; - bool has_output = false; - bool verbose = true; - bool optimize = false; - bool analysis = false; - targetKind target = targetKind::cpu; - - void print() { - std::cout << cyan("." + std::string(60, '-') + ".") << std::endl; - std::cout << cyan("| file ") << filename << std::string(61-11-filename.size(),' ') << cyan("|") << std::endl; - std::string outname = (outputname.size() ? outputname : "stdout"); - std::cout << cyan("| output ") << outname << std::string(61-11-outname.size(),' ') << cyan("|") << std::endl; - std::cout << cyan("| verbose ") << (verbose ? "yes" : "no ") << std::string(61-11-3,' ') << cyan("|") << std::endl; - std::cout << cyan("| optimize ") << (optimize ? "yes" : "no ") << std::string(61-11-3,' ') << cyan("|") << std::endl; - std::cout << cyan("| target ") << (target==targetKind::cpu? "cpu" : "gpu") << std::string(61-11-3,' ') << cyan("|") << std::endl; - std::cout << cyan("| analysis ") << (analysis ? "yes" : "no ") << std::string(61-11-3,' ') << cyan("|") << std::endl; - std::cout << cyan("." + std::string(60, '-') + ".") << std::endl; - } -}; - int main(int argc, char **argv) { - Options options; - // parse command line arguments try { TCLAP::CmdLine cmd("welcome to mod2c", ' ', "0.1"); @@ -51,7 +26,7 @@ int main(int argc, char **argv) { fin_arg("input_file", "the name of the .mod file to compile", true, "", "filename"); // output filename TCLAP::ValueArg<std::string> - fout_arg("o","output","name of output file", false,"","filname"); + fout_arg("o","output","name of output file", false,"","filename"); // output filename TCLAP::ValueArg<std::string> target_arg("t","target","backend target={cpu,gpu}", true,"cpu","cpu/gpu"); @@ -61,41 +36,45 @@ int main(int argc, char **argv) { TCLAP::SwitchArg analysis_arg("A","analyse","toggle analysis mode", cmd, false); // optimization mode TCLAP::SwitchArg opt_arg("O","optimize","turn optimizations on", cmd, false); + // Set module name explicitly + TCLAP::ValueArg<std::string> + module_arg("m", "module", "module name to use", false, "", "module"); cmd.add(fin_arg); cmd.add(fout_arg); cmd.add(target_arg); + cmd.add(module_arg); cmd.parse(argc, argv); - options.outputname = fout_arg.getValue(); - options.has_output = options.outputname.size()>0; - options.filename = fin_arg.getValue(); - options.verbose = verbose_arg.getValue(); - options.optimize = opt_arg.getValue(); - options.analysis = analysis_arg.getValue(); + Options::instance().outputname = fout_arg.getValue(); + Options::instance().has_output = Options::instance().outputname.size()>0; + Options::instance().filename = fin_arg.getValue(); + Options::instance().modulename = module_arg.getValue(); + Options::instance().verbose = verbose_arg.getValue(); + Options::instance().optimize = opt_arg.getValue(); + Options::instance().analysis = analysis_arg.getValue(); auto targstr = target_arg.getValue(); if(targstr == "cpu") { - options.target = targetKind::cpu; + Options::instance().target = targetKind::cpu; } else if(targstr == "gpu") { - options.target = targetKind::gpu; + Options::instance().target = targetKind::gpu; } else { - std::cerr << red("error") << " target must be one in {cpu, gpu}" << std::endl; + std::cerr << red("error") << " target must be one in {cpu, gpu}\n"; return 1; } } // catch any exceptions in command line handling catch(TCLAP::ArgException &e) { - std::cerr << "error: " << e.error() - << " for arg " << e.argId() - << std::endl; + std::cerr << "error: " << e.error() + << " for arg " << e.argId() << "\n"; } try { // load the module from file passed as first argument - Module m(options.filename.c_str()); + Module m(Options::instance().filename.c_str()); // check that the module is not empty if(m.buffer().size()==0) { @@ -104,14 +83,14 @@ int main(int argc, char **argv) { return 1; } - if(options.verbose) { - options.print(); + if(Options::instance().verbose) { + Options::instance().print(); } //////////////////////////////////////////////////////////// // parsing //////////////////////////////////////////////////////////// - if(options.verbose) std::cout << green("[") + "parsing" + green("]") << std::endl; + if(Options::instance().verbose) std::cout << green("[") + "parsing" + green("]") << std::endl; // initialize the parser Parser p(m, false); @@ -123,7 +102,7 @@ int main(int argc, char **argv) { //////////////////////////////////////////////////////////// // semantic analysis //////////////////////////////////////////////////////////// - if(options.verbose) + if(Options::instance().verbose) std::cout << green("[") + "semantic analysis" + green("]") << "\n"; m.semantic(); @@ -139,8 +118,8 @@ int main(int argc, char **argv) { //////////////////////////////////////////////////////////// // optimize //////////////////////////////////////////////////////////// - if(options.optimize) { - if(options.verbose) std::cout << green("[") + "optimize" + green("]") << std::endl; + if(Options::instance().optimize) { + if(Options::instance().verbose) std::cout << green("[") + "optimize" + green("]") << std::endl; m.optimize(); if(m.status() == lexerStatus::error) { return 1; @@ -150,47 +129,49 @@ int main(int argc, char **argv) { //////////////////////////////////////////////////////////// // generate output //////////////////////////////////////////////////////////// - if(options.verbose) { + if(Options::instance().verbose) { std::cout << green("[") + "code generation" << green("]") << std::endl; } std::string text; - switch(options.target) { + switch(Options::instance().target) { case targetKind::cpu : - text = CPrinter(m, options.optimize).text(); + text = CPrinter(m, Options::instance().optimize).text(); break; case targetKind::gpu : - text = CUDAPrinter(m, options.optimize).text(); + text = CUDAPrinter(m, Options::instance().optimize).text(); break; default : std::cerr << red("error") << ": unknown printer" << std::endl; exit(1); } - if(options.has_output) { - std::ofstream fout(options.outputname); + if(Options::instance().has_output) { + std::ofstream fout(Options::instance().outputname); fout << text; fout.close(); } else { - std::cout << cyan("--------------------------------------") << std::endl; + std::cout << cyan("--------------------------------------\n"); std::cout << text; - std::cout << cyan("--------------------------------------") << std::endl; + std::cout << cyan("--------------------------------------\n"); } - std::cout << yellow("successfully compiled ") << white(options.filename) << " -> " << white(options.outputname) << std::endl; + std::cout << yellow("successfully compiled ") + << white(Options::instance().filename) << " -> " + << white(Options::instance().outputname) << "\n"; //////////////////////////////////////////////////////////// // print module information //////////////////////////////////////////////////////////// - if(options.analysis) { + if(Options::instance().analysis) { std::cout << green("performance analysis") << std::endl; for(auto &symbol : m.symbols()) { if(auto method = symbol.second->is_api_method()) { - std::cout << white("-------------------------") << std::endl; - std::cout << yellow("method " + method->name()) << std::endl; - std::cout << white("-------------------------") << std::endl; + std::cout << white("-------------------------\n"); + std::cout << yellow("method " + method->name()) << "\n"; + std::cout << white("-------------------------\n"); auto flops = make_unique<FlopVisitor>(); method->accept(flops.get()); @@ -209,24 +190,21 @@ int main(int argc, char **argv) { catch(compiler_exception e) { std::cerr << red("internal compiler error: ") << white("this means a bug in the compiler," - " please report to modcc developers") - << std::endl - << e.what() << " @ " << e.location() << std::endl; + " please report to modcc developers\n") + << e.what() << " @ " << e.location() << "\n"; exit(1); } catch(std::exception e) { std::cerr << red("internal compiler error: ") << white("this means a bug in the compiler," - " please report to modcc developers") - << std::endl - << e.what() << std::endl; + " please report to modcc developers\n") + << e.what() << "\n"; exit(1); } catch(...) { std::cerr << red("internal compiler error: ") << white("this means a bug in the compiler," - " please report to modcc developers") - << std::endl; + " please report to modcc developers\n"); exit(1); } diff --git a/modcc/options.hpp b/modcc/options.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d994740e79e73c06030b774b710b56dc805da7a4 --- /dev/null +++ b/modcc/options.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include <iostream> + +enum class targetKind { cpu, gpu }; + +struct Options { + std::string filename; + std::string outputname; + std::string modulename; + bool has_output = false; + bool verbose = true; + bool optimize = false; + bool analysis = false; + targetKind target = targetKind::cpu; + + void print() { + std::cout << cyan("." + std::string(60, '-') + ".") << "\n"; + std::cout << cyan("| file ") << filename + << std::string(61-11-filename.size(),' ') + << cyan("|") << "\n"; + + std::string outname = (outputname.size() ? outputname : "stdout"); + std::cout << cyan("| output ") << outname + << std::string(61-11-outname.size(),' ') + << cyan("|") << "\n"; + std::cout << cyan("| verbose ") << (verbose ? "yes" : "no ") + << std::string(61-11-3,' ') << cyan("|") << "\n"; + std::cout << cyan("| optimize ") << (optimize ? "yes" : "no ") + << std::string(61-11-3,' ') << cyan("|") << "\n"; + std::cout << cyan("| target ") + << (target==targetKind::cpu? "cpu" : "gpu") + << std::string(61-11-3,' ') << cyan("|") << "\n"; + std::cout << cyan("| analysis ") << (analysis ? "yes" : "no ") + << std::string(61-11-3,' ') << cyan("|") << "\n"; + std::cout << cyan("." + std::string(60, '-') + ".") << std::endl; + } + + Options(const Options& other) = delete; + void operator=(const Options& other) = delete; + + static Options& instance() { + static Options instance; + return instance; + } + +private: + Options() {} +}; diff --git a/src/util/cycle.hpp b/src/util/cycle.hpp index a4405bdbce6b0d4c391b3e1275c86641faaf3cd5..66611f1c3601aea36f921c21c4f57ce85b1d3807 100644 --- a/src/util/cycle.hpp +++ b/src/util/cycle.hpp @@ -1,5 +1,6 @@ #pragma once +#include <initializer_list> #include <utility> #include <util/iterutil.hpp> #include <util/range.hpp> @@ -205,6 +206,15 @@ cyclic_view(const Seq& s) { return { make_cyclic_iterator(cbegin(s), cend(s)), cend(s) }; } +// Handle initializer lists +template <typename T> +range<cyclic_iterator<typename std::initializer_list<T>::const_iterator, + typename std::initializer_list<T>::const_iterator> > +cyclic_view(const std::initializer_list<T> &list) { + return { make_cyclic_iterator(cbegin(list), cend(list)), + make_cyclic_iterator(cend(list), cend(list)) }; +} + } // namespace util } // namespace mc } // namespace nest diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 45de971662d68f0972cda67419655ea5c574eb4b..b211b296f2de918be2c6b40cd06af53e86e5a57e 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -1,3 +1,21 @@ +include(${CMAKE_SOURCE_DIR}/mechanisms/BuildModules.cmake) + +# Build prototype mechanisms for testing in test_mechanisms. +set(proto_mechanisms pas hh expsyn exp2syn) +set(mech_proto_dir "${CMAKE_CURRENT_BINARY_DIR}/mech_proto") +file(MAKE_DIRECTORY "${mech_proto_dir}") + +build_modules( + ${proto_mechanisms} + SOURCE_DIR "${CMAKE_SOURCE_DIR}/mechanisms/mod" + DEST_DIR "${mech_proto_dir}" + MECH_SUFFIX _proto + MODCC_FLAGS -t cpu + TARGET build_test_mods +) + +# Unit test sources + set(TEST_CUDA_SOURCES test_cell_group.cu test_matrix.cu @@ -54,6 +72,8 @@ add_definitions("-DDATADIR=\"${CMAKE_SOURCE_DIR}/data\"") set(TARGETS test.exe) add_executable(test.exe ${TEST_SOURCES} ${HEADERS}) +add_dependencies(test.exe build_test_mods) +target_include_directories(test.exe PRIVATE "${mech_proto_dir}/..") if(WITH_CUDA) set(TARGETS ${TARGETS} test_cuda.exe) @@ -75,4 +95,3 @@ foreach(target ${TARGETS}) RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests" ) endforeach() - diff --git a/tests/unit/test_cycle.cpp b/tests/unit/test_cycle.cpp index d022a666b2f03308e6d951dd06aa153eef68a929..621f4515bb160b552470b646cdd2d646a8024916 100644 --- a/tests/unit/test_cycle.cpp +++ b/tests/unit/test_cycle.cpp @@ -147,6 +147,20 @@ TEST(cycle, cyclic_view) { } } +TEST(cycle, cyclic_view_initlist) { + std::vector<int> values; + + std::copy_n(util::cyclic_view({2., 3., 4.}).cbegin(), 10, + std::back_inserter(values)); + + EXPECT_EQ(10u, values.size()); + + auto i = 0; + for (auto const& v : values) { + EXPECT_EQ(2 + i++ % 3, v); + } +} + TEST(cycle_iterator, difference) { int values[] = { 4, 2, 3 }; diff --git a/tests/unit/test_mechanisms.cpp b/tests/unit/test_mechanisms.cpp index fbc0989c66a25098ad816807cf3991e06707cf95..58ffd34efb2152a734124515cb2408d727fd5703 100644 --- a/tests/unit/test_mechanisms.cpp +++ b/tests/unit/test_mechanisms.cpp @@ -1,7 +1,24 @@ #include "../gtest.h" -#include <matrix.hpp> +// Prototype mechanisms in tests +#include "mech_proto/expsyn.hpp" +#include "mech_proto/exp2syn.hpp" +#include "mech_proto/hh.hpp" +#include "mech_proto/pas.hpp" + +// modcc generated mechanisms +#include "mechanisms/multicore/expsyn.hpp" +#include "mechanisms/multicore/exp2syn.hpp" +#include "mechanisms/multicore/hh.hpp" +#include "mechanisms/multicore/pas.hpp" + +#include <initializer_list> #include <backends/fvm_multicore.hpp> +#include <ion.hpp> +#include <matrix.hpp> +#include <memory/wrappers.hpp> +#include <util/rangeutil.hpp> +#include <util/cycle.hpp> TEST(mechanisms, helpers) { using namespace nest::mc; @@ -32,3 +49,154 @@ TEST(mechanisms, helpers) { std::out_of_range ); } + +// Setup and update mechanism +template<typename T> +void mech_update(T* mech, int num_iters) { + + using namespace nest::mc; + std::map<mechanisms::ionKind, mechanisms::ion<typename T::backend>> ions; + + mech->set_params(2., 0.1); + mech->nrn_init(); + for (auto ion_kind : mechanisms::ion_kinds()) { + auto ion_indexes = util::make_copy<std::vector<typename T::size_type>>( + mech->node_index_ + ); + + // Create and fill in the ion + mechanisms::ion<typename T::backend> ion = ion_indexes; + + memory::fill(ion.current(), 5.); + memory::fill(ion.reversal_potential(), 100.); + memory::fill(ion.internal_concentration(), 10.); + memory::fill(ion.external_concentration(), 140.); + ions[ion_kind] = ion; + + if (mech->uses_ion(ion_kind)) { + mech->set_ion(ion_kind, ions[ion_kind], ion_indexes); + } + } + + for (auto i = 0; i < mech->node_index_.size(); ++i) { + mech->net_receive(i, 1.); + } + + for (auto i = 0; i < num_iters; ++i) { + mech->nrn_current(); + mech->nrn_state(); + } +} + +template<typename T, typename Seq> +void array_init(T& array, const Seq& seq) { + auto seq_iter = seq.cbegin(); + for (auto& e : array) { + e = *seq_iter++; + } +} + +template<typename S, typename T, bool alias = false> +struct mechanism_info { + using mechanism_type = S; + using proto_mechanism_type = T; + static constexpr bool index_aliasing = alias; +}; + +template<typename T> +class mechanisms : public ::testing::Test { }; + +TYPED_TEST_CASE_P(mechanisms); + +TYPED_TEST_P(mechanisms, update) { + using mechanism_type = typename TypeParam::mechanism_type; + using proto_mechanism_type = typename TypeParam::proto_mechanism_type; + + // Type checking + EXPECT_TRUE((std::is_same<typename proto_mechanism_type::iarray, + typename mechanism_type::iarray>::value)); + EXPECT_TRUE((std::is_same<typename proto_mechanism_type::value_type, + typename mechanism_type::value_type>::value)); + EXPECT_TRUE((std::is_same<typename proto_mechanism_type::array, + typename mechanism_type::array>::value)); + + auto num_syn = 32; + + typename mechanism_type::iarray indexes(num_syn); + typename mechanism_type::array voltage(num_syn, -65.0); + typename mechanism_type::array current(num_syn, 1.0); + typename mechanism_type::array weights(num_syn, 1.0); + + array_init(voltage, nest::mc::util::cyclic_view({ -65.0, -61.0, -63.0 })); + array_init(current, nest::mc::util::cyclic_view({ 1.0, 0.9, 1.1 })); + array_init(weights, nest::mc::util::cyclic_view({ 1.0 })); + + // Initialise indexes + std::vector<int> index_freq; + if (TypeParam::index_aliasing) { + index_freq.assign({ 4, 2, 3 }); + } + else { + index_freq.assign({ 1 }); + } + + auto freq_begin = nest::mc::util::cyclic_view(index_freq).cbegin(); + auto freq = freq_begin; + auto index = indexes.begin(); + while (index != indexes.end()) { + for (auto i = 0; i < *freq && index != indexes.end(); ++i) { + *index++ = freq - freq_begin; + } + ++freq; + } + + + // Copy indexes, voltage and current to use for the prototype mechanism + typename mechanism_type::iarray indexes_copy(indexes); + typename mechanism_type::array voltage_copy(voltage); + typename mechanism_type::array current_copy(current); + typename mechanism_type::array weights_copy(weights); + + // Create mechanisms + auto mech = nest::mc::mechanisms::make_mechanism<mechanism_type>( + voltage, current, std::move(weights), std::move(indexes) + ); + + auto mech_proto = nest::mc::mechanisms::make_mechanism<proto_mechanism_type>( + voltage_copy, current_copy, + std::move(weights_copy), std::move(indexes_copy) + ); + + mech_update(dynamic_cast<mechanism_type*>(mech.get()), 10); + mech_update(dynamic_cast<proto_mechanism_type*>(mech_proto.get()), 10); + + auto citer = current_copy.begin(); + for (auto const& c: current) { + EXPECT_NEAR(*citer++, c, 1e-6); + } +} + +REGISTER_TYPED_TEST_CASE_P(mechanisms, update); + +using mechanism_types = ::testing::Types< + mechanism_info< + nest::mc::mechanisms::hh::mechanism_hh<nest::mc::multicore::backend>, + nest::mc::mechanisms::hh_proto::mechanism_hh_proto<nest::mc::multicore::backend> + >, + mechanism_info< + nest::mc::mechanisms::pas::mechanism_pas<nest::mc::multicore::backend>, + nest::mc::mechanisms::pas_proto::mechanism_pas_proto<nest::mc::multicore::backend> + >, + mechanism_info< + nest::mc::mechanisms::expsyn::mechanism_expsyn<nest::mc::multicore::backend>, + nest::mc::mechanisms::expsyn_proto::mechanism_expsyn_proto<nest::mc::multicore::backend>, + true + >, + mechanism_info< + nest::mc::mechanisms::exp2syn::mechanism_exp2syn<nest::mc::multicore::backend>, + nest::mc::mechanisms::exp2syn_proto::mechanism_exp2syn_proto<nest::mc::multicore::backend>, + true + > +>; + +INSTANTIATE_TYPED_TEST_CASE_P(mechanism_types, mechanisms, mechanism_types); diff --git a/tests/unit/test_synapses.cpp b/tests/unit/test_synapses.cpp index 0084ed56b281707ff470a8581c58407840e0588b..cd899a5b0fd4890cce0b8c14860340facada0055 100644 --- a/tests/unit/test_synapses.cpp +++ b/tests/unit/test_synapses.cpp @@ -153,4 +153,3 @@ TEST(synapses, exp2syn_basic_state) EXPECT_NEAR(ptr->A[1], ptr->factor[1]*3.14, 1e-6); EXPECT_NEAR(ptr->B[3], ptr->factor[3]*1.04, 1e-6); } -