Skip to content

Commit

Permalink
Merge pull request #420 from brian-team/tutorial
Browse files Browse the repository at this point in the history
Tutorial
  • Loading branch information
mstimberg committed Mar 10, 2015
2 parents 8e21ef2 + 0ec62e1 commit ccd7eaa
Show file tree
Hide file tree
Showing 21 changed files with 1,644 additions and 222 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ nosetests.xml
# pycharm project files
.idea
/Brian2.egg-info

# IPython
.ipynb_checkpoints
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "docs_sphinx/resources"]
path = docs_sphinx/resources
url = https://github.com/brian-team/brian2-doc-resources.git
8 changes: 7 additions & 1 deletion brian2/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class BrianObject(Nameable):
-----
The set of all `BrianObject` objects is stored in ``BrianObject.__instances__()``.
'''
'''
@check_units(dt=second)
def __init__(self, dt=None, clock=None, when='start', order=0, name='brianobject*'):

Expand Down Expand Up @@ -97,10 +97,16 @@ def __init__(self, dt=None, clock=None, when='start', order=0, name='brianobject

self._active = True

#: The scope key is used to determine which objects are collected by magic
self._scope_key = self._scope_current_key

logger.debug("Created BrianObject with name {self.name}, "
"clock={self._clock}, "
"when={self.when}, order={self.order}".format(self=self))

#: Global key value for ipython cell restrict magic
_scope_current_key = 0

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

Expand Down
19 changes: 17 additions & 2 deletions brian2/core/magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
from brian2.utils.logger import get_logger

from .network import Network
from .base import BrianObject, device_override
from .base import BrianObject, device_override

__all__ = ['MagicNetwork', 'magic_network',
'MagicError',
'run', 'reinit', 'stop', 'collect', 'store', 'restore'
'run', 'reinit', 'stop', 'collect', 'store', 'restore',
'start_scope',
]

logger = get_logger(__name__)
Expand Down Expand Up @@ -275,6 +276,10 @@ def collect(level=0):
for obj in get_objects_in_namespace(level=level+1):
obj = obj()
if obj.add_to_magic_network:
gk = BrianObject._scope_current_key
k = obj._scope_key
if gk!=k:
continue
all_objects.add(obj)
return all_objects

Expand Down Expand Up @@ -401,3 +406,13 @@ def stop():
Network.stop, run, reinit
'''
Network._globally_stopped = True


def start_scope():
'''
Starts a new scope for magic functions
All objects created before this call will no longer be automatically
included by the magic functions such as `run`.
'''
BrianObject._scope_current_key += 1
6 changes: 4 additions & 2 deletions brian2/only.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ def restore_initial_state():
'''
Restores internal Brian variables to the state they are in when Brian is imported
Resets ``defaultclock.dt = 0.1*ms`` and
`BrianGlobalPreferences._restore` preferences.
Resets ``defaultclock.dt = 0.1*ms``,
`BrianGlobalPreferences._restore` preferences, and set
`BrianObject._scope_current_key` back to 0.
'''
defaultclock.dt = 0.1*ms
prefs._restore()
BrianObject._scope_current_key = 0

# make the test suite available via brian2.test()
from brian2.tests import run as test
4 changes: 2 additions & 2 deletions brian2/sphinxext/generate_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ def main(rootpath, destdir):
output += '\n'.join([' ' + line for line in afterdoccode.split('\n')])
output += '\n\n'
basedir, _ = os.path.split(__file__)
eximgpat = os.path.join(basedir, '../../docs_sphinx/examples_images', '%s.*.png' % exname)
eximgpat = os.path.join(basedir, '../../docs_sphinx/resources/examples_images', '%s.*.png' % exname)
images = glob.glob(eximgpat)
for image in sorted(images):
_, image = os.path.split(image)
output += '.. image:: ../examples_images/%s\n\n' % image
output += '.. image:: ../resources/examples_images/%s\n\n' % image

open(os.path.join(destdir, exname + '.rst'), 'w').write(output)

Expand Down
87 changes: 53 additions & 34 deletions brian2/tests/test_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
restore_initial_state, MagicError, Synapses,
NeuronGroup, StateMonitor, SpikeMonitor,
PopulationRateMonitor, MagicNetwork, magic_network,
PoissonGroup, Hz, collect, store, restore, BrianLogger)
PoissonGroup, Hz, collect, store, restore, BrianLogger,
start_scope)
from brian2.utils.logger import catch_logs

@attr('codegen-independent')
Expand Down Expand Up @@ -752,40 +753,58 @@ def test_profile():
assert all([t>=0*second for _, t in info])


@attr('codegen-independent')
@with_setup(teardown=restore_initial_state)
def test_magic_scope():
'''
Check that `start_scope` works as expected.
'''
G1 = NeuronGroup(1, 'v:1', name='G1')
G2 = NeuronGroup(1, 'v:1', name='G2')
objs1 = {obj.name for obj in collect()}
start_scope()
G3 = NeuronGroup(1, 'v:1', name='G3')
G4 = NeuronGroup(1, 'v:1', name='G4')
objs2 = {obj.name for obj in collect()}
assert objs1=={'G1', 'G2'}
assert objs2=={'G3', 'G4'}


if __name__=='__main__':
for t in [
test_incorrect_network_use,
test_network_contains,
test_empty_network,
test_network_single_object,
test_network_two_objects,
test_network_different_clocks,
test_network_different_when,
test_magic_network,
test_network_stop,
test_network_operations,
test_network_active_flag,
test_network_t,
test_incorrect_dt_defaultclock,
test_incorrect_dt_custom_clock,
test_network_remove,
test_magic_weak_reference,
test_magic_unused_object,
test_invalid_magic_network,
test_multiple_networks_invalid,
test_network_access,
test_loop,
test_magic_collect,
test_progress_report,
test_progress_report_incorrect,
test_store_restore,
test_store_restore_magic,
test_defaultclock_dt_changes,
test_dt_restore,
test_continuation,
test_multiple_runs_defaultclock,
test_multiple_runs_defaultclock_incorrect,
test_profile
]:
test_incorrect_network_use,
test_network_contains,
test_empty_network,
test_network_single_object,
test_network_two_objects,
test_network_different_clocks,
test_network_different_when,
test_magic_network,
test_network_stop,
test_network_operations,
test_network_active_flag,
test_network_t,
test_incorrect_dt_defaultclock,
test_incorrect_dt_custom_clock,
test_network_remove,
test_magic_weak_reference,
test_magic_unused_object,
test_invalid_magic_network,
test_multiple_networks_invalid,
test_network_access,
test_loop,
test_magic_collect,
test_progress_report,
test_progress_report_incorrect,
test_store_restore,
test_store_restore_magic,
test_defaultclock_dt_changes,
test_dt_restore,
test_continuation,
test_multiple_runs_defaultclock,
test_multiple_runs_defaultclock_incorrect,
test_profile,
test_magic_scope,
]:
t()
restore_initial_state()
2 changes: 0 additions & 2 deletions dev/tools/docs/build_html_brian2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
import shutil
import sphinx
import sys
from download_examples_images_from_dropbox import download_examples_images_from_dropbox

download_examples_images_from_dropbox()
os.chdir('../../../docs_sphinx')
if os.path.exists('../docs'):
shutil.rmtree('../docs')
Expand Down
92 changes: 92 additions & 0 deletions dev/tools/docs/build_tutorials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import os
import shutil
import glob
import codecs

from IPython.nbformat.v4 import reads
from IPython.nbconvert.preprocessors import ExecutePreprocessor
from IPython.nbconvert.exporters.notebook import NotebookExporter
from IPython.nbconvert.exporters.rst import RSTExporter


src_dir = os.path.abspath('../../../tutorials')
target_dir = os.path.abspath('../../../docs_sphinx/resources/tutorials')

# Start from scratch to avoid left-over files due to renamed tutorials, changed
# cell numbers, etc.
if os.path.exists(target_dir):
shutil.rmtree(target_dir)
os.mkdir(target_dir)

tutorials = []
for fname in sorted(glob.glob1(src_dir, '*.ipynb')):
basename = fname[:-6]
output_ipynb_fname = os.path.join(target_dir, fname)
output_rst_fname = os.path.join(target_dir, basename + '.rst')

print 'Running', fname
notebook = reads(open(os.path.join(src_dir, fname), 'r').read())

# The first line of the tutorial file should give the title
title = notebook.cells[0]['source'].split('\n')[0].strip('# ')
tutorials.append((basename, title))

# Execute the notebook
preprocessor = ExecutePreprocessor()
notebook, _ = preprocessor.preprocess(notebook, {})

print 'Saving notebook and converting to RST'
exporter = NotebookExporter()
output, _ = exporter.from_notebook_node(notebook)
codecs.open(output_ipynb_fname, 'w', encoding='utf-8').write(output)

exporter = RSTExporter()
output, resources = exporter.from_notebook_node(notebook,
resources={'unique_key': basename+'_image'})
codecs.open(output_rst_fname, 'w', encoding='utf-8').write(output)

for image_name, image_data in resources['outputs'].iteritems():
open(os.path.join(target_dir, image_name), 'wb').write(image_data)

print 'Generating index.rst'

text = '''
Tutorial
========
The tutorial consists of a series of `IPython notebooks`_ [#]_. If you run such
a notebook on your own computer, you can interactively change the code in the
tutorial and experiment with it -- this is the recommend way to get started
with Brian. The first link for each tutorial below leads to a non-interactive
version of the notebook; use the links under "Notebook files" to get a file that
you can run on your computer. You can also copy such a link and paste it at
http://nbviewer.ipython.org -- this will get you a nicer (but still
non-interactive) rendering then the one you see in our documentation.
For more information about how to use IPython notebooks, see the
`IPython notebook documentation`_.
.. toctree::
:maxdepth: 1
:titlesonly:
'''
for tutorial, _ in tutorials:
text += ' ' + tutorial + '\n'
text += '''
Notebook files
--------------
'''
for tutorial, title in tutorials:
text += '* :download:`{title} <{tutorial}.ipynb>`\n'.format(title=title,
tutorial=tutorial)
text += '''
.. _`IPython notebooks`: http://ipython.org/notebook.html
.. _`Jupyter`: http://jupyter.org/
.. _`IPython notebook documentation`: http://ipython.org/ipython-doc/stable/notebook/index.html
.. [#] The project has been partly renamed to `Jupyter`_ recently
'''
open(os.path.join(target_dir, 'index.rst'), 'w').write(text)
38 changes: 0 additions & 38 deletions dev/tools/docs/copy_examples_images_to_dropbox.py

This file was deleted.

26 changes: 0 additions & 26 deletions dev/tools/docs/download_examples_images_from_dropbox.py

This file was deleted.

4 changes: 2 additions & 2 deletions dev/tools/run_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def runTest(self):
fname = os.path.relpath(fname, '../../examples')
fname = fname.replace('/', '.').replace('\\\\', '.')
fname = fname.replace('.py', '.%d.png' % fignum)
fname = '../../docs_sphinx/examples_images/'+fname
fname = '../../docs_sphinx/resources/examples_images/'+fname
print fname
ensure_directory_of_file(fname)
_mpl.pyplot.figure(fignum).savefig(fname)
Expand Down Expand Up @@ -119,7 +119,7 @@ def find_examples(self, name):
def loadTestsFromName(self, name, module=None, discovered=False):
all_examples = self.find_examples(name)
all_tests = []
for target in ['numpy', 'weave', 'cython']:
for target in ['numpy']:
for example in all_examples:
all_tests.append(RunTestCase(example, target))
return all_tests
Expand Down

0 comments on commit ccd7eaa

Please sign in to comment.