Skip to content

Commit

Permalink
Merge 8a229e6 into 310dcb8
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcel Stimberg committed Apr 28, 2014
2 parents 310dcb8 + 8a229e6 commit a888fbb
Show file tree
Hide file tree
Showing 44 changed files with 343 additions and 6,151 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ before_install:
# command to install dependencies
install:
- conda update --yes conda
- conda create -n travis_conda --yes pip python=$TRAVIS_PYTHON_VERSION numpy scipy nose sphinx ipython sympy pyparsing cython
- conda create -n travis_conda --yes pip python=${TRAVIS_PYTHON_VERSION:0:3} numpy scipy nose sphinx ipython sympy pyparsing cython
- source activate travis_conda
- pip install -q coveralls --use-mirrors
- pip install -q objgraph --use-mirrors
- python setup.py install --with-cython --fail-on-error
# Make sure that weave uses Anaconda's header files
- mkdir ~/.brian;echo -e "[codegen.runtime.weave]\ninclude_dirs = ['/home/travis/anaconda/envs/travis_conda/include']\n" > ~/.brian/user_preferences
Expand Down
28 changes: 26 additions & 2 deletions brian2/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ def __init__(self, when=None, name='brianobject*'):
# #: The `Clock` determining when the object should be updated.
# self.clock = clock
self._clock = clock


self._dependencies = set()
self._contained_objects = []
self._code_objects = []

Expand All @@ -67,7 +68,30 @@ def __init__(self, when=None, name='brianobject*'):

#: Whether or not `MagicNetwork` is invalidated when a new `BrianObject` of this type is created or removed
invalidates_magic_network = True


#: Whether or not the object should be added to a `MagicNetwork`. Note that
#: all objects in `BrianObject.contained_objects` are automatically added
#: when the parent object is added, therefore e.g. `NeuronGroup` should set
#: `add_to_magic_network` to ``True``, but it should not be set for all the
#: dependent objects such as `StateUpdater`
add_to_magic_network = False

def add_dependency(self, obj):
'''
Add an object to the list of dependencies. Takes care of handling
subgroups correctly (i.e., adds its parent object).
Parameters
----------
obj : `BrianObject`
The object that this object depends on.
'''
from brian2.groups.subgroup import Subgroup
if isinstance(obj, Subgroup):
self._dependencies.add(obj.source.name)
else:
self._dependencies.add(obj.name)

def before_run(self, run_namespace=None, level=0):
'''
Optional method to prepare the object before a run.
Expand Down
102 changes: 90 additions & 12 deletions brian2/core/magic.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import sys
import weakref

from brian2.units.fundamentalunits import check_units
from brian2.units.allunits import second
from brian2.utils.logger import get_logger
from brian2.core.network import Network
from brian2.core.base import BrianObject
from brian2.utils.proxy import get_proxy_count

__all__ = ['MagicNetwork', 'magic_network',
'MagicError',
Expand All @@ -14,6 +16,41 @@
logger = get_logger(__name__)


def drill_down(obj, levels=3):
import gc, types
levels -= 1
if levels == 0:
return
for ref in gc.get_referrers(obj):
if not isinstance(ref, types.FrameType):
print '\t'*levels, type(ref), id(ref), ref
drill_down(ref, levels=levels-1)


def _get_contained_objects(obj):
'''
Helper function to recursively get all contained objects.
Parameters
----------
obj : `BrianObject`
An object that (potentially) contains other objects, e.g. a
`NeuronGroup` contains a `StateUpdater`, etc.
Returns
-------
l : list of `BrianObject`
A list of all the objects contained in `obj`
'''
l = []
contained_objects = getattr(obj, 'contained_objects', [])
l.extend(contained_objects)
for contained_obj in contained_objects:
l.extend(_get_contained_objects(contained_obj))

return l


class MagicError(Exception):
'''
Error that is raised when something goes wrong in `MagicNetwork`
Expand Down Expand Up @@ -101,8 +138,7 @@ def __init__(self):
raise ValueError("There can be only one MagicNetwork.")
MagicNetwork._already_created = True

super(MagicNetwork, self).__init__(name='magicnetwork*',
weak_references=True)
super(MagicNetwork, self).__init__(name='magicnetwork*')

self._previous_refs = set()

Expand All @@ -117,11 +153,35 @@ def remove(self, *objs):
You cannot remove objects directly from `MagicNetwork`
'''
raise MagicError("Cannot directly modify MagicNetwork")



def _update_magic_objects(self):
# Go through all the objects and ignore those that are only referred to
# by Proxy objects (e.g. because a Monitor holds a reference to them)
valid_refs = set()
all_objects = set()
for obj in BrianObject.__instances__():
obj = obj()
proxycount = get_proxy_count(obj)
# subtract 1 from refcount for refcount arg
# subtract 1 from refcount for refcount in this loop
refcount = sys.getrefcount(obj)-2
import objgraph
print 'backrefsfor', obj
objgraph.show_backrefs([obj], max_depth=10, filename='tmp.dot')
with open('tmp.dot', 'r') as f:
for line in f:
print line

if refcount != proxycount:
if obj.add_to_magic_network:
all_objects.add(obj)
all_objects.update(_get_contained_objects(obj))
if obj.invalidates_magic_network:
valid_refs.add(weakref.ref(obj))

# check whether we should restart time, continue time, or raise an
# error
valid_refs = set(r for r in BrianObject.__instances__() if r().invalidates_magic_network)
inter = valid_refs.intersection(self._previous_refs)

if len(inter)==0:
Expand All @@ -133,23 +193,43 @@ def _update_magic_objects(self):
else:
raise MagicError("Brian cannot guess what you intend to do here, see docs for MagicNetwork for details")
self._previous_refs = valid_refs
self.objects[:] = [weakref.proxy(obj()) for obj in BrianObject.__instances__()]
self.objects[:] = list(all_objects)
logger.debug("Updated MagicNetwork to include {numobjs} objects "
"with names {names}".format(
numobjs=len(self.objects),
names=', '.join(obj.name for obj in self.objects)))
names=', '.join(obj.name for obj in self.objects)),
name_suffix='magic_objects')

def check_dependencies(self):
for obj in self.objects:
if not obj.active:
continue # object is already inactive, no need to check it
for dependency in obj._dependencies:
if not dependency in self:
logger.info(('"%s" has been included in the network but '
'not "%s" on which it depends. Setting "%s" '
'to inactive.') % (obj.name,
dependency,
obj.name),
name_suffix='missing_dependency')
obj.active = False
break

def before_run(self, run_namespace, level=0):
def before_run(self, run_namespace=None, level=0):
self._update_magic_objects()
Network.before_run(self, run_namespace, level=level+1)

def after_run(self):
self.objects[:] = []

def reinit(self):
'''
See `Network.reinit`.
'''
self._update_magic_objects()
super(MagicNetwork, self).reinit()

self.objects[:] = []

def __str__(self):
return 'MagicNetwork()'
__repr__ = __str__
Expand Down Expand Up @@ -208,18 +288,16 @@ def run(duration, report=None, report_period=60*second, namespace=None,
See Also
--------
Network.run, MagicNetwork, reinit, stop, clear
Raises
------
MagicError
Error raised when it was not possible for Brian to safely guess the
intended use. See `MagicNetwork` for more details.
'''
magic_network.run(duration, report=report, report_period=report_period,
namespace=namespace, level=2+level)
return magic_network.run(duration, report=report, report_period=report_period,
namespace=namespace, level=2+level)
run.__module__ = __name__

def reinit():
Expand Down
5 changes: 0 additions & 5 deletions brian2/core/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,6 @@ def get_local_namespace(level):
hasattr(v, '_arg_units') and
hasattr(v, '_return_unit'))) and
not k.startswith('_')):
# If possible, add a weak reference
try:
v = weakref.proxy(v)
except TypeError:
pass
namespace[k] = v
del frame
return namespace
Expand Down
55 changes: 30 additions & 25 deletions brian2/core/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from brian2.units.fundamentalunits import check_units
from brian2.units.allunits import second
from brian2.core.preferences import brian_prefs
from brian2.core.namespace import get_local_namespace
from brian2.devices.device import device_override

__all__ = ['Network']
Expand Down Expand Up @@ -37,11 +36,6 @@ class Network(Nameable):
`~Network.add`.
name : str, optional
An explicit name, if not specified gives an automatically generated name
weak_references : bool, optional
Whether to only store weak references to the objects (defaults to
``False``), i.e. other references to the objects need to exist to keep
them alive. This is used by the magic system, otherwise it would keep
all objects alive all the time.
Notes
-----
Expand Down Expand Up @@ -85,19 +79,16 @@ class Network(Nameable):

def __init__(self, *objs, **kwds):
#: The list of objects in the Network, should not normally be modified
#: directly
#:
#: Stores references or `weakref.proxy` references to the objects
#: (depending on `weak_references`)
#: directly.
#: Note that in a `MagicNetwork`, this attribute only contains the
#: objects during a run: it is filled in `before_run` and emptied in
#: `after_run`
self.objects = []

name = kwds.pop('name', 'network*')

#: Whether the network only stores weak references to the objects
self.weak_references = kwds.pop('weak_references', False)
if kwds:
raise TypeError("Only keyword arguments to Network are name "
"and weak_references")
raise TypeError("Only keyword argument to Network is 'name'.")

Nameable.__init__(self, name=name)

Expand Down Expand Up @@ -137,6 +128,12 @@ def __delitem__(self, key):

raise KeyError('No object with name "%s" found' % key)

def __contains__(self, item):
for obj in self.objects:
if obj.name == item:
return True
return False

def __len__(self):
return len(self.objects)

Expand All @@ -157,15 +154,17 @@ def add(self, *objs):
"""
for obj in objs:
if isinstance(obj, BrianObject):
if self.weak_references and not isinstance(obj,
(weakref.ProxyType,
weakref.CallableProxyType)):
obj = weakref.proxy(obj)
self.objects.append(obj)
self.add(obj.contained_objects)
else:
try:
for o in obj:
if o is obj:
# This prevents infinite recursion for some corner
# cases, e.g. when a string was provided (a string
# is iterable, and each element is yet another
# iterable string)
raise TypeError()
self.add(o)
except TypeError:
raise TypeError("Can only add objects of type BrianObject, "
Expand All @@ -185,11 +184,6 @@ def remove(self, *objs):
'''
for obj in objs:
if isinstance(obj, BrianObject):
if self.weak_references and not isinstance(obj,
(weakref.ProxyType,
weakref.CallableProxyType)):
obj = weakref.proxy(obj)
# note that weakref.proxy(obj) is weakref.proxy(obj) is True
self.objects.remove(obj)
self.remove(obj.contained_objects)
else:
Expand Down Expand Up @@ -251,7 +245,15 @@ def _sort_objects(self):
'''
when_to_int = dict((when, i) for i, when in enumerate(self.schedule))
self.objects.sort(key=lambda obj: (when_to_int[obj.when], obj.order))


def check_dependencies(self):
for obj in self.objects:
for dependency in obj._dependencies:
if not dependency in self:
raise ValueError(('"%s" has been included in the network '
'but not "%s" on which it '
'depends.') % (obj.name, dependency))

@device_override('network_before_run')
def before_run(self, run_namespace=None, level=0):
'''
Expand Down Expand Up @@ -282,7 +284,9 @@ def before_run(self, run_namespace=None, level=0):
numobj=len(self.objects),
objnames=', '.join(obj.name for obj in self.objects)),
"before_run")


self.check_dependencies()

for obj in self.objects:
obj.before_run(run_namespace, level=level+2)

Expand Down Expand Up @@ -391,6 +395,7 @@ def run(self, duration, report=None, report_period=60*second,

if report is not None:
print 'Took ', current-start, 's in total.'

self.after_run()

@device_override('network_stop')
Expand Down
1 change: 1 addition & 0 deletions brian2/core/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class NetworkOperation(BrianObject):
network_operation, Network, BrianObject
"""
add_to_magic_network = True
def __init__(self, function, when=None):
BrianObject.__init__(self, when=when, name='networkoperation*')

Expand Down
4 changes: 2 additions & 2 deletions brian2/core/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
from brian2.core.base import weakproxy_with_fallback
from brian2.utils.stringtools import get_identifiers, word_substitute
from brian2.units.fundamentalunits import (Quantity, Unit,
fail_for_dimension_mismatch,
have_same_dimensions)
from brian2.units.allunits import second
from brian2.utils.proxy import Proxy

from .preferences import brian_prefs

Expand Down Expand Up @@ -627,7 +627,7 @@ class VariableView(object):
def __init__(self, name, variable, group, unit=None):
self.name = name
self.variable = variable
self.group = weakproxy_with_fallback(group)
self.group = Proxy(group)
self.unit = unit

@property
Expand Down
Loading

0 comments on commit a888fbb

Please sign in to comment.