Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions nestkernel/conn_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,14 +619,14 @@ nest::OneToOneBuilder::connect_()
Node* target = n->get_node();

const size_t tnode_id = n->get_node_id();
const int idx = targets_->find( tnode_id );
if ( idx < 0 ) // Is local node in target list?
const long lid = targets_->get_lid( tnode_id );
if ( lid < 0 ) // Is local node in target list?
{
continue;
}

// one-to-one, thus we can use target idx for source as well
const size_t snode_id = ( *sources_ )[ idx ];
const size_t snode_id = ( *sources_ )[ lid ];
if ( not allow_autapses_ and snode_id == tnode_id )
{
// no skipping required / possible,
Expand Down Expand Up @@ -819,7 +819,7 @@ nest::AllToAllBuilder::connect_()
const size_t tnode_id = n->get_node_id();

// Is the local node in the targets list?
if ( targets_->find( tnode_id ) < 0 )
if ( targets_->get_lid( tnode_id ) < 0 )
{
continue;
}
Expand Down Expand Up @@ -1102,7 +1102,7 @@ nest::FixedInDegreeBuilder::connect_()
const size_t tnode_id = n->get_node_id();

// Is the local node in the targets list?
if ( targets_->find( tnode_id ) < 0 )
if ( targets_->get_lid( tnode_id ) < 0 )
{
continue;
}
Expand Down Expand Up @@ -1534,7 +1534,7 @@ nest::BernoulliBuilder::connect_()
const size_t tnode_id = n->get_node_id();

// Is the local node in the targets list?
if ( targets_->find( tnode_id ) < 0 )
if ( targets_->get_lid( tnode_id ) < 0 )
{
continue;
}
Expand Down
14 changes: 8 additions & 6 deletions nestkernel/layer_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,9 @@ Layer< D >::dump_connections( std::ostream& out,
std::vector< std::pair< Position< D >, size_t > >* src_vec = get_global_positions_vector( node_collection );

// Dictionary with parameters for get_connections()
DictionaryDatum ncdict( new Dictionary );
def( ncdict, names::synapse_model, syn_model );
DictionaryDatum conn_filter( new Dictionary );
def( conn_filter, names::synapse_model, syn_model );
def( conn_filter, names::target, NodeCollectionDatum( target_layer->get_node_collection() ) );

// Avoid setting up new array for each iteration of the loop
std::vector< size_t > source_array( 1 );
Expand All @@ -321,8 +322,8 @@ Layer< D >::dump_connections( std::ostream& out,
const Position< D > source_pos = src_iter->first;

source_array[ 0 ] = source_node_id;
def( ncdict, names::source, NodeCollectionDatum( NodeCollection::create( source_array ) ) );
ArrayDatum connectome = kernel().connection_manager.get_connections( ncdict );
def( conn_filter, names::source, NodeCollectionDatum( NodeCollection::create( source_array ) ) );
ArrayDatum connectome = kernel().connection_manager.get_connections( conn_filter );

// Print information about all local connections for current source
for ( size_t i = 0; i < connectome.size(); ++i )
Expand All @@ -344,8 +345,9 @@ Layer< D >::dump_connections( std::ostream& out,
Layer< D >* tgt_layer = dynamic_cast< Layer< D >* >( target_layer.get() );

out << ' ';
const size_t tnode_id = tgt_layer->node_collection_->find( target_node_id );
tgt_layer->compute_displacement( source_pos, tnode_id ).print( out );
const long tnode_lid = tgt_layer->node_collection_->get_lid( target_node_id );
assert( tnode_lid >= 0 );
tgt_layer->compute_displacement( source_pos, tnode_lid ).print( out );
out << '\n';
}
}
Expand Down
2 changes: 1 addition & 1 deletion nestkernel/nestmodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1072,7 +1072,7 @@ NestModule::Find_g_iFunction::execute( SLIInterpreter* i ) const
NodeCollectionDatum nodecollection = getValue< NodeCollectionDatum >( i->OStack.pick( 1 ) );
const long node_id = getValue< long >( i->OStack.pick( 0 ) );

const auto res = nodecollection->find( node_id );
const auto res = nodecollection->get_lid( node_id );
i->OStack.pop( 2 );
i->OStack.push( res );
i->EStack.pop();
Expand Down
8 changes: 4 additions & 4 deletions nestkernel/node_collection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -981,11 +981,11 @@ NodeCollectionComposite::merge_parts_( std::vector< NodeCollectionPrimitive >& p
bool
NodeCollectionComposite::contains( const size_t node_id ) const
{
return find( node_id ) != -1;
return get_lid( node_id ) != -1;
}

long
NodeCollectionComposite::find( const size_t node_id ) const
NodeCollectionComposite::get_lid( const size_t node_id ) const
{
const auto add_size_op = []( const long a, const NodeCollectionPrimitive& b ) { return a + b.size(); };

Expand Down Expand Up @@ -1018,7 +1018,7 @@ NodeCollectionComposite::find( const size_t node_id ) const
// Need to find number of nodes in previous parts to know if the the step hits the node_id.
const auto num_prev_nodes =
std::accumulate( parts_.begin(), parts_.begin() + middle, static_cast< size_t >( 0 ), add_size_op );
const auto absolute_pos = num_prev_nodes + parts_[ middle ].find( node_id );
const auto absolute_pos = num_prev_nodes + parts_[ middle ].get_lid( node_id );

// The first or the last node can be somewhere in the middle part.
const auto absolute_part_start = start_part_ == middle ? start_offset_ : 0;
Expand All @@ -1041,7 +1041,7 @@ NodeCollectionComposite::find( const size_t node_id ) const
// Since NC is not sliced, we can just calculate and return the local ID.
const auto sum_pre =
std::accumulate( parts_.begin(), parts_.begin() + middle, static_cast< size_t >( 0 ), add_size_op );
return sum_pre + parts_[ middle ].find( node_id );
return sum_pre + parts_[ middle ].get_lid( node_id );
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions nestkernel/node_collection.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ class NodeCollection
*
* @return Index of node with given node ID; -1 if node not in NodeCollection.
*/
virtual long find( const size_t ) const = 0;
virtual long get_lid( const size_t ) const = 0;

/**
* Returns whether the NodeCollection contains any nodes with proxies or not.
Expand Down Expand Up @@ -512,7 +512,7 @@ class NodeCollectionPrimitive : public NodeCollection
bool is_range() const override;
bool empty() const override;

long find( const size_t ) const override;
long get_lid( const size_t ) const override;

bool has_proxies() const override;

Expand Down Expand Up @@ -650,7 +650,7 @@ class NodeCollectionComposite : public NodeCollection
bool is_range() const override;
bool empty() const override;

long find( const size_t ) const override;
long get_lid( const size_t ) const override;

bool has_proxies() const override;
};
Expand Down Expand Up @@ -807,7 +807,7 @@ NodeCollectionPrimitive::empty() const
}

inline long
NodeCollectionPrimitive::find( const size_t neuron_id ) const
NodeCollectionPrimitive::get_lid( const size_t neuron_id ) const
{
if ( neuron_id > last_ )
{
Expand Down
35 changes: 30 additions & 5 deletions pynest/nest/lib/hl_api_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,16 @@
API of the PyNEST wrapper.
"""

import warnings
import json
import functools
import textwrap
import json
import os
import pydoc

import textwrap
import warnings
from string import Template

from ..ll_api import sli_func, sps, sr, spp
from .. import pynestkernel as kernel
from ..ll_api import sli_func, spp, sps, sr

__all__ = [
"broadcast",
Expand All @@ -52,6 +51,7 @@
"restructure_data",
"show_deprecation_warning",
"show_help_with_pager",
"stringify_path",
"SuppressedDeprecationWarning",
]

Expand Down Expand Up @@ -148,6 +148,31 @@ def new_func(*args, **kwargs):
return deprecated_decorator


def stringify_path(filepath):
"""
Convert path-like object to string form.

Attempt to convert path-like object to a string by coercing objects
supporting the fspath protocol to its ``__fspath__`` method. Anything that
is not path-like, which includes bytes and strings, is passed through
unchanged.

Parameters
----------
filepath : object
Object representing file system path.

Returns
-------
filepath : str
Stringified filepath.
"""

if isinstance(filepath, os.PathLike):
filepath = filepath.__fspath__() # should return str or bytes object
return filepath


def is_literal(obj):
"""Check whether obj is a "literal": a unicode string or SLI literal

Expand Down
14 changes: 11 additions & 3 deletions pynest/nest/lib/hl_api_spatial.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,20 @@
Functions relating to spatial properties of nodes
"""

import os

import numpy as np

from ..ll_api import sli_func
from .hl_api_helper import is_iterable
from .hl_api_connections import GetConnections
from .hl_api_helper import is_iterable, stringify_path
from .hl_api_parallel_computing import NumProcesses, Rank
from .hl_api_types import NodeCollection

try:
import matplotlib as mpl
import matplotlib.path as mpath
import matplotlib.patches as mpatches
import matplotlib.path as mpath

HAVE_MPL = True
except ImportError:
Expand Down Expand Up @@ -530,9 +531,12 @@ def DumpLayerNodes(layer, outname):
nest.DumpLayerNodes(s_nodes, 'positions.txt')

"""

if not isinstance(layer, NodeCollection):
raise TypeError("layer must be a NodeCollection")

outname = stringify_path(outname)

sli_func(
"""
(w) file exch DumpLayerNodes close
Expand Down Expand Up @@ -599,11 +603,15 @@ def DumpLayerConnections(source_layer, target_layer, synapse_model, outname):
# write connectivity information to file
nest.DumpLayerConnections(s_nodes, s_nodes, 'static_synapse', 'conns.txt')
"""

if not isinstance(source_layer, NodeCollection):
raise TypeError("source_layer must be a NodeCollection")

if not isinstance(target_layer, NodeCollection):
raise TypeError("target_layer must be a NodeCollection")

outname = stringify_path(outname)

sli_func(
"""
/oname Set
Expand Down Expand Up @@ -1467,8 +1475,8 @@ def _create_mask_patches(mask, periodic, extent, source_pos, face_color="yellow"

# import pyplot here and not at toplevel to avoid preventing users
# from changing matplotlib backend after importing nest
import matplotlib.pyplot as plt
import matplotlib as mtpl
import matplotlib.pyplot as plt

edge_color = "black"
alpha = 0.2
Expand Down
91 changes: 91 additions & 0 deletions testsuite/pytests/sli2py_regressions/test_issue_2629.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
#
# test_issue_2629.py
#
# This file is part of NEST.
#
# Copyright (C) 2004 The NEST Initiative
#
# NEST is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# NEST is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see <http://www.gnu.org/licenses/>.

"""
Regression test for Issue #2629 (GitHub).

The issue was that ``DumpLayerConnections`` failed when a source layer was
connected to more than one target layer. The test ensures that this is no
longer the case.

For each connection between the specified source and target layer,
``DumpLayerConnections`` writes the following to file:

source_node_id target_node_id weight delay dx dy [dz]

where (dx, dy [, dz]) is the displacement from source to target node.

This test uses the ``tmp_path`` Pytest fixture, which will provide a
temporary directory unique to the test invocation. ``tmp_path`` is a
``pathlib.Path`` object. Hence, the test also implicitly verifies that it
is possible to pass a ``pathlib.Path`` object as filename.
"""

import pytest

import nest


@pytest.fixture(scope="module")
def network():
"""Fixture for building network."""

grid = nest.spatial.grid(shape=[2, 1])
src_layer = nest.Create("iaf_psc_alpha", positions=grid)
tgt_layer_1 = nest.Create("iaf_psc_alpha", positions=grid)
tgt_layer_2 = nest.Create("iaf_psc_alpha", positions=grid)

nest.Connect(src_layer, tgt_layer_1, "all_to_all")
nest.Connect(src_layer, tgt_layer_2, "one_to_one")

return src_layer, tgt_layer_1, tgt_layer_2


def test_dump_layer_connections_target_1(tmp_path, network):
"""Test that dumping connections with target layer 1 works."""

src_layer, tgt_layer_1, _ = network

fname_1 = tmp_path / "conns_1.txt"
nest.DumpLayerConnections(src_layer, tgt_layer_1, "static_synapse", fname_1)
expected_dump_1 = [
"1 3 1 1 0 0",
"1 4 1 1 0.5 0",
"2 3 1 1 -0.5 0",
"2 4 1 1 0 0",
]
actual_dump_1 = fname_1.read_text().splitlines()
assert actual_dump_1 == expected_dump_1


def test_dump_layer_connections_target_2(tmp_path, network):
"""Test that dumping connections with target layer 2 works."""

src_layer, _, tgt_layer_2 = network

fname_2 = tmp_path / "conns_2.txt"
nest.DumpLayerConnections(src_layer, tgt_layer_2, "static_synapse", fname_2)
expected_dump_2 = [
"1 5 1 1 0 0",
"2 6 1 1 0 0",
]
actual_dump_2 = fname_2.read_text().splitlines()
assert actual_dump_2 == expected_dump_2
Loading