Skip to content
Draft
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
1 change: 1 addition & 0 deletions brian2/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def setup_and_teardown(request):

yield # run test

brian2.prefs._restore()
# Reset defaultclock.dt to be sure
defaultclock.dt = 0.1 * ms

Expand Down
2 changes: 2 additions & 0 deletions brian2/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def __init__(
#: Used to remember the `Network` in which this object has been included
#: before, to raise an error if it is included in a new `Network`
self._network = None
self._used_in_network = False

#: The ID string determining when the object should be updated in `Network.run`.
self.when = when
Expand Down Expand Up @@ -187,6 +188,7 @@ def __del__(self):
if (
prefs.logging.warn_for_unused_objects
and getattr(self, "_network", "uninitialized") is None
and not getattr(self, "_used_in_network", False)
and getattr(self, "group", None) is None
and not BrianLogger.exception_occured # No need to add a warning if something went wrong
):
Expand Down
3 changes: 3 additions & 0 deletions brian2/core/clocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ def __init__(self, name):
# We need a name right away because some devices (e.g. cpp_standalone)
# need a name for the object when creating the variables
Nameable.__init__(self, name=name)
from brian2.core.base import BrianObject

self._scope_key = BrianObject._scope_current_key
self.variables = Variables(self)
self.variables.add_array(
"timestep", size=1, dtype=np.int64, read_only=True, scalar=True
Expand Down
4 changes: 4 additions & 0 deletions brian2/core/magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,3 +489,7 @@ def start_scope():
included by the magic functions such as `run`.
"""
BrianObject._scope_current_key += 1

# Run garbage collection here to destroy stale objects from previous runs
# preventing them from squatting on auto-generated names in the InstanceFollower
gc.collect()
14 changes: 13 additions & 1 deletion brian2/core/names.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,20 @@ def find_name(name, names=None):
name = name[:-1]

if names is None:
from brian2.core.base import BrianObject

instances = set(Nameable.__instances__())
allnames = {obj().name for obj in instances if hasattr(obj(), "name")}
allnames = set()
for obj_ref in instances:
obj = obj_ref()
if obj is not None and hasattr(obj, "name"):
obj_scope_key = getattr(obj, "_scope_key", None)
if (
obj_scope_key is not None
and obj_scope_key != BrianObject._scope_current_key
):
continue
allnames.add(obj.name)
else:
allnames = names

Expand Down
3 changes: 2 additions & 1 deletion brian2/core/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,14 +543,15 @@ def add(self, *objs):
"""
for obj in objs:
if isinstance(obj, BrianObject):
if obj._network is not None:
if obj._network is not None and obj._network != self.id:
raise RuntimeError(
f"{obj.name} has already been simulated, cannot "
"add it to the network. If you were "
"trying to remove and add an object to "
"temporarily stop it from being run, "
"set its active flag to False instead."
)
obj._used_in_network = True
self.objects.add(obj)
else:
# allow adding values from dictionaries
Expand Down
4 changes: 4 additions & 0 deletions brian2/synapses/synapses.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,11 +916,15 @@ def __init__(
self._connect_called = False
self.codeobj_class = codeobj_class

if isinstance(source, SynapticSubgroup):
raise TypeError("SynapticSubgroup cannot be used as a source")
self.source = source
self.add_dependency(source)
if target is None:
self.target = self.source
else:
if isinstance(target, SynapticSubgroup):
raise TypeError("SynapticSubgroup cannot be used as a target")
self.target = target
self.add_dependency(target)

Expand Down
27 changes: 27 additions & 0 deletions brian2/tests/test_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -1922,6 +1922,33 @@ def test_negative_duration_in_net_run():
net.run(-1 * second)


@pytest.mark.codegen_independent
def test_network_restore_loop():
# Issue 1814: Verify that running and restoring networks in a loop
# does not raise KeyError due to auto-generated clocks leaking iteratively
filename = tempfile.mktemp(suffix="state", prefix="brian_test")
try:
start_scope()
G = NeuronGroup(10, "v:1", name="G")
G.run_regularly("v += 0.1", dt=1 * ms)
net = Network(collect())
net.run(1 * ms)
net.store(filename=filename)

for i in range(2):
start_scope()
G = NeuronGroup(10, "v:1", name="G")
G.run_regularly("v += 0.1", dt=1 * ms)
net2 = Network(collect())
net2.restore(filename=filename)
net2.run(1 * ms)
finally:
try:
os.remove(filename)
except OSError:
pass


if __name__ == "__main__":
BrianLogger.log_level_warn()
for t in [
Expand Down
18 changes: 18 additions & 0 deletions brian2/tests/test_synapses.py
Original file line number Diff line number Diff line change
Expand Up @@ -3697,6 +3697,24 @@ def test_synaptic_subgroups():
assert connections == {(1, 0), (1, 1), (2, 0), (2, 1)}


@pytest.mark.codegen_independent
def test_synaptic_subgroup_type_error():
# Issue 1287: Ensure error is raised if passing SynapticSubgroup directly
source = NeuronGroup(5, "")
target = NeuronGroup(3, "")
syn1 = Synapses(source, target)
syn1.connect()
subgroup = syn1[0:2, :]

with pytest.raises(TypeError) as exc:
Synapses(subgroup, target)
assert "SynapticSubgroup cannot be used as a source" in str(exc.value)

with pytest.raises(TypeError) as exc:
Synapses(source, subgroup)
assert "SynapticSubgroup cannot be used as a target" in str(exc.value)


@pytest.mark.codegen_independent
def test_incorrect_connect_N_incoming_outgoing():
# See github issue #1227
Expand Down
Loading