From ca734a8ce8dfe97d4d434e68fe6793d2448f8de1 Mon Sep 17 00:00:00 2001 From: Brandon Zhang <58379435+ztqakita@users.noreply.github.com> Date: Wed, 9 Mar 2022 15:39:22 +0800 Subject: [PATCH 01/88] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8f7eda05..6d9c7a10 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,7 @@ See [brainmodels.synapses](https://brainmodels.readthedocs.io/en/brainpy-2.x/api - [Continuous-attractor Neural Network](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/dynamics_analysis/highdim_CANN.html) - [Gap junction-coupled FitzHugh-Nagumo Model](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/dynamics_analysis/highdim_gj_coupled_fhn.html) +### Contributors # BrainPy 1.x -- 2.34.1 From 60d37a74a4ab7ed817fbde84ae5f9493b73fa3ee Mon Sep 17 00:00:00 2001 From: Brandon Zhang <58379435+ztqakita@users.noreply.github.com> Date: Wed, 9 Mar 2022 15:41:03 +0800 Subject: [PATCH 02/88] Update contributors.yml --- .github/workflows/contributors.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml index 3b262934..40a19d61 100644 --- a/.github/workflows/contributors.yml +++ b/.github/workflows/contributors.yml @@ -2,6 +2,8 @@ name: Add contributors on: schedule: - cron: '20 20 * * *' + push: + branches: [ master ] jobs: add-contributors: -- 2.34.1 From 90d188862cc54960617bba167c5b7234601349bb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Mar 2022 07:41:27 +0000 Subject: [PATCH 03/88] docs(README): update contributors --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/README.md b/README.md index 6d9c7a10..2b51f137 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,62 @@ See [brainmodels.synapses](https://brainmodels.readthedocs.io/en/brainpy-2.x/api ### Contributors +
+
+
+ + Chaoming Wang + + |
+
+
+
+ + Brandon Zhang + + |
+
+
+
+ + Xiaoyu Chen + + |
+
+
+
+ + Yingqian Jiang + + |
+
+
+
+ + Xinyu Liu + + |
+
+
+
+ + charlielam0615 + + |
+
+
+
+ + Terry Pang + + |
+
- -
- - - - - -:clap::clap: **CHEERS**: A new version of BrainPy (>=2.0.0, long term support) has been released! :clap::clap: - - - -# Why use BrainPy - -``BrainPy`` is an integrative framework for computational neuroscience and brain-inspired computation based on the Just-In-Time (JIT) compilation (built on top of [JAX](https://github.com/google/jax)). Core functions provided in BrainPy includes - -- **JIT compilation** for class objects. -- **Numerical solvers** for ODEs, SDEs, and others. -- **Dynamics simulation tools** for various brain objects, like neurons, synapses, networks, soma, dendrites, channels, and even more. -- **Dynamics analysis tools** for differential equations, including phase plane analysis and bifurcation analysis, and linearization analysis. -- **Seamless integration with deep learning models**. -- And more ...... - -`BrainPy` is designed to effectively satisfy your basic requirements: - -- **Pythonic**: BrainPy is based on Python language and has a Pythonic coding style. -- **Flexible and transparent**: BrainPy endows the users with full data/logic flow control. Users can code any logic they want with BrainPy. -- **Extensible**: BrainPy allows users to extend new functionality just based on Python code. Almost every part of the BrainPy system can be extended to be customized. -- **Efficient**: All codes in BrainPy can be just-in-time compiled (based on [JAX](https://github.com/google/jax)) to run on CPU, GPU, or TPU devices, thus guaranteeing its running efficiency. - - - -# How to use BrainPy - -## Step 1: installation - -``BrainPy`` is based on Python (>=3.6), and the following packages are required to be installed to use ``BrainPy``: `numpy >= 1.15`, `matplotlib >= 3.4`, and `jax >= 0.2.10` ([how to install jax?](https://brainpy.readthedocs.io/en/latest/quickstart/installation.html#dependency-2-jax)) - -``BrainPy`` can be installed on Linux (Ubuntu 16.04 or later), macOS (10.12 or later), and Windows platforms. Use the following instructions to install ``brainpy``: - -```bash -pip install brain-py -U -``` - -*For the full installation details please see documentation: [Quickstart/Installation](https://brainpy.readthedocs.io/en/latest/quickstart/installation.html)* - - - - -## Step 2: useful links - -- **Documentation:** https://brainpy.readthedocs.io/ -- **Bug reports:** https://github.com/PKU-NIP-Lab/BrainPy/issues -- **Examples from papers**: https://brainpy-examples.readthedocs.io/ -- **Canonical brain models**: https://brainmodels.readthedocs.io/ - - - -## Step 3: inspirational examples - -Here we list several examples of BrainPy. For more detailed examples and tutorials please see [**BrainModels**](https://brainmodels.readthedocs.io) or [**BrainPy-Examples**](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/). - - - -### Neuron models - -- [Leaky integrate-and-fire neuron model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.neurons.LIF.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/neurons/LIF.py) -- [Exponential integrate-and-fire neuron model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.neurons.ExpIF.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/neurons/ExpIF.py) -- [Quadratic integrate-and-fire neuron model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.neurons.QuaIF.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/neurons/QuaIF.py) -- [Adaptive Quadratic integrate-and-fire model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.neurons.AdQuaIF.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/neurons/AdQuaIF.py) -- [Adaptive Exponential integrate-and-fire model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.neurons.AdExIF.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/neurons/AdExIF.py) -- [Generalized integrate-and-fire model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.neurons.GIF.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/neurons/GIF.py) -- [Hodgkin–Huxley neuron model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.neurons.HH.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/neurons/HH.py) -- [Izhikevich neuron model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.neurons.Izhikevich.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/neurons/Izhikevich.py) -- [Morris-Lecar neuron model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.neurons.MorrisLecar.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/neurons/MorrisLecar.py) -- [Hindmarsh-Rose bursting neuron model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.neurons.HindmarshRose.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/neurons/HindmarshRose.py) - -See [brainmodels.neurons](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/neurons.html) to find more. - - - -### Synapse models - -- [Voltage jump synapse model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.synapses.VoltageJump.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/synapses/voltage_jump.py) -- [Exponential synapse model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.synapses.ExpCUBA.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/synapses/exponential.py) -- [Alpha synapse model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.synapses.AlphaCUBA.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/synapses/alpha.py) -- [Dual exponential synapse model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.synapses.DualExpCUBA.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/synapses/dual_exp.py) -- [AMPA synapse model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.synapses.AMPA.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/synapses/AMPA.py) -- [GABAA synapse model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.synapses.GABAa.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/synapses/GABAa.py) -- [NMDA synapse model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.synapses.NMDA.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/synapses/NMDA.py) -- [Short-term plasticity model](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/generated/brainmodels.synapses.STP.html), [source code](https://github.com/PKU-NIP-Lab/BrainModels/blob/brainpy-2.x/brainmodels/synapses/STP.py) - -See [brainmodels.synapses](https://brainmodels.readthedocs.io/en/brainpy-2.x/apis/synapses.html) to find more. - - - -### Network models - -- **[CANN]** [*(Si Wu, 2008)* Continuous-attractor Neural Network](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/cann/Wu_2008_CANN.html) -- [*(Vreeswijk & Sompolinsky, 1996)* E/I balanced network](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/ei_nets/Vreeswijk_1996_EI_net.html) -- [*(Sherman & Rinzel, 1992)* Gap junction leads to anti-synchronization](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/gj_nets/Sherman_1992_gj_antisynchrony.html) -- [*(Wang & Buzsáki, 1996)* Gamma Oscillation](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/oscillation_synchronization/Wang_1996_gamma_oscillation.html) -- [*(Brunel & Hakim, 1999)* Fast Global Oscillation](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/oscillation_synchronization/Brunel_Hakim_1999_fast_oscillation.html) -- [*(Diesmann, et, al., 1999)* Synfire Chains](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/oscillation_synchronization/Diesmann_1999_synfire_chains.html) -- **[Working Memory]** [*(Mi, et. al., 2017)* STP for Working Memory Capacity](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/working_memory/Mi_2017_working_memory_capacity.html) -- **[Working Memory]** [*(Bouchacourt & Buschman, 2019)* Flexible Working Memory Model](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/working_memory/Bouchacourt_2019_Flexible_working_memory.html) -- **[Decision Making]** [*(Wang, 2002)* Decision making spiking model](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/decision_making/Wang_2002_decision_making_spiking.html) - - - -### Dynamics training - -- [Train Integrator RNN with BP](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/recurrent_networks/integrator_rnn.html) - -- [*(Sussillo & Abbott, 2009)* FORCE Learning](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/recurrent_networks/Sussillo_Abbott_2009_FORCE_Learning.html) - -- [*(Laje & Buonomano, 2013)* Robust Timing in RNN](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/recurrent_networks/Laje_Buonomano_2013_robust_timing_rnn.html) -- [*(Song, et al., 2016)*: Training excitatory-inhibitory recurrent network](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/recurrent_networks/Song_2016_EI_RNN.html) -- **[Working Memory]** [*(Masse, et al., 2019)*: RNN with STP for Working Memory](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/recurrent_networks/Masse_2019_STP_RNN.html) - - - - -### Low-dimensional dynamics analysis - -- [[1D] Simple systems](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/dynamics_analysis/1d_simple_systems.html) -- [[2D] NaK model analysis](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/dynamics_analysis/2d_NaK_model.html) -- [[3D] Hindmarsh Rose Model](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/dynamics_analysis/3d_hindmarsh_rose_model.html) -- **[Decision Making Model]** [[2D] Decision making rate model](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/decision_making/Wang_2006_decision_making_rate.html) - - - -### High-dimensional dynamics analysis - -- [*(Yang, 2020)*: Dynamical system analysis for RNN](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/recurrent_networks/Yang_2020_RNN_Analysis.html) -- [Continuous-attractor Neural Network](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/dynamics_analysis/highdim_CANN.html) -- [Gap junction-coupled FitzHugh-Nagumo Model](https://brainpy-examples.readthedocs.io/en/brainpy-2.x/dynamics_analysis/highdim_gj_coupled_fhn.html) - - - -# BrainPy 1.x - -If you are using ``brainpy==1.x``, you can find *documentation*, *examples*, and *models* through the following links: - -- **Documentation:** https://brainpy.readthedocs.io/en/brainpy-1.x/ -- **Examples from papers**: https://brainpy-examples.readthedocs.io/en/brainpy-1.x/ -- **Canonical brain models**: https://brainmodels.readthedocs.io/en/brainpy-1.x/ - -The changes from ``brainpy==1.x`` to ``brainpy==2.x`` can be inspected through [API documentation: release notes](https://brainpy.readthedocs.io/en/latest/apis/auto/changelog.html). - - -# Contributors -- 2.34.1 From 6c0a6e4c11c946f2741ba06c89a0f8a32f7cfd91 Mon Sep 17 00:00:00 2001 From: chaoming?ki zKeU5;nSL5GYZl_K@OE7&Js-Bu|Dv7)e6UZO6s(Ke`RvBVo2hH9zx*+iyv};dj!RqD z8GcLyouC?~{Ipwh-HAJ!x1Kn0!u@nW;dz?fbkf^u8?-5*X8b!-!2d9PCU-j55{k|v z92FoqnUxd+}Y(r+$8!Xut6HWM)XT^rlx`Dvu?}b5s@$; zlN)h0qEC=bTZ)3{bMXHENtZL}?P<+u5QE4g^#_n%se^;(OKbdw(hB_(-!s~)EJC=tGx zq&FEJO)dyZdf*9D4T;5dIrH3#%e ;J9^2M=1}B{Zt%jdO6=U{HX2dEp#!kR z0>RJ+bH{*{FBuN_xdmTG18#FiG0a9pUyteMpc;kb!N`T^#-Ovwo*}9)W9fT*UDCas zNz3}CQO?qE=sf*x?p4H4-XfODYc_YlMvyr!;dh)2V)$3`Xb3`Ro-uMMy;};*FYVrL zJW#@&Jg`{kUqg?|bHUFJG76(K+!5okHja>pk=br}J8)@bIA~`>kzsObP=1o9kE%Ic zQJL_E$(yF4qsc`<`42p&%RGbfO&kb@=E(y=(o*T?NRoF-w4oZtR#f{c9!{f>itNtG zb94) poB4AHZ&>p)lUdj_c9r8}t-Mof(4 z(BvR#v?pBk8@)$X?{2;fs8t(kkf_PpxH4m7mk$mb7|5duvsCmW*#Z)!&<(j`u+*b6 zfOFrzYt6OPVMRoH2EH2XBOG(VR(_8VM6qx&h ;D{0aED7om5m+YnPARgJ z?}zUY2wJd(F%x#!7?S)fni@st&Yf0AvI$u(Ye7m4MV864LHSrTKXtv9ya^LDw#!%I z7-VF!#G6=MFChmhtC~!pI6@Y6I%`^I{wQ}X;SRpVmsKtD(Ek4GL0&ruD<%szl6&Dh zi$8li_7_zNpKHMU*Pqn}sL)0xmzCJE(SW@i0)#z|4iG-W&OIWnb^+nxBs_W?^vol5 zz586au^Uk;b-UK#1`iEMS)hB6Rq%p^G~9R^`Ms#y135z4WcSV~qeeK^eIOix4A}&_ z)6Gc7B9WoZ5b8>P+h!%L;&mT$0GX3gOY8_Ki#mek5DyRj3bU|gW?dv5h5J-Bl_lq2 zxE@sD jeW{`^l8&Ri)0Ru9QlN{ZS$J2r#bN^Qxy25g`Xa zbrnR}RLwKF5zBVPjqM)Q$dCi`wj@c=dC#bhm}?;itlVVb8Zq !ASr|#2od6d27$*y9l`4d3%j$gAa3-dHtF2d1Pg VM=g#igYL}9KY2Cskhnk9d9a7r2devn*t-!`L#q;|b_e*K?-q39R~EZ7 z_$`W{k2_P(X}vt@0H1IvbcQOJ>;PE!c)50b^0F0`GQIqnfv=q?Vcx(^s*ww(qh@SG zI!WJk=#1B6qF$<-KW|LVh+G%kEb4=kUGhyD9#F%N%JYaA5V|VNb|b^TF^Y6zYAPi` z*Wg-tCw<~4*_$$4E30OcS+K&`(NsVT%56=M6_{xrsD)1$`2Rk2ZkmuLn8di2B-u zlW$^QiyL*hbbz^<0cvJ6_C@4-WsnIt^?Kw_&s61{RX`>_mZOs|)!9uGq}La)(PU1R z?HW+Bo!65z-(g4kPz}J{G0EDI$(L&ECMg}l`0bOi*;hayuL~VV?stM8K!VqgTLW%z zXhIXJ0qE0Z5fVIPz~j0e1nqpt?!BHGsx^1a7tl+}#f5CdFIH5PIy{;q8mRhbR$g0x z?)bHMNC+O-Lhv2nFA2aP@k%#^Y7C?-%27k=s}unb3C$zqF3pZz0^PCDqMszix5@WP zT@{Jdc4&O^r83(+a`_tuGn S*PD{bg_nlE#({ako-0Ov!i zzbl_?>{>gGDH70(5k> a<6XUYh_`m(65Keg^)F-WZMO$idbfkl2`NaDlzl+7$zIhMkmY%ShN zELv~dlMQ2gd>=ZGjF=hQfYBBR< NolN` z?@?r@T`9{(Y_p;UgAjA5fL_ 8Cwn#J EJLt1Q zoh|i4!5Z}z{zUV3!|_?}WdB24nfV@f^HjI=?AjhpRy`VABnoL)1FlKgCVveix=8&V zp%eZgf>4gb4`DYLK6tiP_2nPcTDz eB za|hzS=pEix`m5FB@nysD$wpZ?t$4Y+$Wb_TF3WbXLK;<5FE@_i>fYe+8 pi9K7&4gH}~J zQlS@2jG7}) OXJ1827!FOG((dc9S>N!Po#1RstfK{Xl@K8LJS zebuxY=c1dtk*+&&WrD}AgE7^=`XeAHe^lgYb)jYRxH5}(mOhlgDzb|fE_LUd4K^m2 zsa09p-SCiMV6 ~2NJowadxL49X4Eic`InA^M?>W^9VK03f zYopK3HPG*4@GGvP%;Dk+|1$_e7bTpgEa;!jZ0%HVhG#`qcWDSWb13N)Ie6i3?=JeG zmU`*T7DU%#C^1ZV+BT)TBxFG%%N9-0Le&@`$sDE(H&@r6=DL;urKE%_-jlKX6CeiM) e9ryWWkaoDsc%+?mZTr<5ha~B$=c$O5}Upj1rB1N?IgcUm0 zTB6YKEUwWkLKfa`xZwKU!z#I;=R7Z00%ajdd@(ZVsCvD_y+37f7Y11cUup4(4jX0h zg4^QiKfO>-dm!^U!R*4Ll;H2NVTz}QLFyTguXrfv#^Br-gl%_=%$y$c28`AvVO2ih z%VNqSGq^c(R^^ln8$VNGI)dTO6iMWk7iE^Xiu~w-nX00?Bi%UVh6|=KaYMbOMZ-np zyX{Uv6f5Qx=>Qkpcp QtwR0Zm)xw>bEp(PCm!KUo$1(@htqc$!MKgJ*gY5Yc{bCpXWsJc ze)1{C)w?})@rH^&Po1-vd~~H!4nE*O+f&wB2($Na(5JnQDeNjY@U6%hrd`CqWZ}{& zjlhVrmVvWG)2~kabPFf0hMPE-%a<&Wk^5j>!*tb8l$gMUdswO7EFTbjk%0Y{v|-~c zqFn70T@ZKt7CFVw<-+~#i*AXS0$G_^p%rIQbrX5^a5|+afd?sgxbE>#^+XX2ML7~f z8VXAw7(0wx!Sas#(XPud#(M)BJKcm9hP&1B9Ji1g$u<|L3z;{fq^%|j3E=U$ePuzF zF_YFd2nVZvvTJE9NlUkJ&ZOAhYp>!i-)~vhsk%zROOmsiy~Q2MoGH0V4R~~Q1)2+% zGqf;2TE;BHXcyn+FL}1ZhpjCGA3U85A<|W;Z}Q(=%(+tTJ>`bh&6U4Mj*ux~8F3I( z$_toR4uNgSN1+C{i7v`*Oe%72Y^#ZTJ;*^?;*Xx{me8dSD8~?uo6L_Nb>B&A*spu* z7JGeZPUbG-pet*WThW7-siw8xpTF_PZ@A2vVqW#L7q%6}edfJ-)AdXi<2}W#L{WAL z5SLK64ViPxm!-O31~+b3FYGfpmHK43(!vp~3A{$AldZ;~3pr`bIOk8U>dYg{`-pV< za8w3`u<$|ZnbmB>4c;RP;Uhx-KwM3=yH1m5p$1tKdYxsDA#O$cW #W_E_JCF_sR^=!)IR+bgG+pBJk9*+jgpk{a{f)Lei z(B`_L`)!wVJ8AI2)b`hbhR#{)z>6y9Xq@RS+Q*L_N9&U=(g;rJO7_rQ@Rb|&PhsY| zk?+*6HT5};?~`q=YScuBL}V55@;$O9BMVg-yCE9$gNh*wz#?6=j3B8@KhWr-wGBE+ zOE|$IQu;ZCa75Vf%%J6BY?%NqxA8IRxA>wNdzy!|V!4tmYa!?Q!N#sBN2x)pyD&Gr zQ^;>E`;IE+=4?U3F-4K*f{2K7aJ}o3U?wv>=lw;zNe?IcuBUwOzT9uAudgZm;Qd~l zj{T&WeO`xs5bt`fF1b;;^{S_A^Qn;Ww_HJbE`j-Z?77Yb>A4x#fEhH5+y{ujD9hwp z^o;ApfQiFB!3$b(HH(n>m)&-F?n9 7PB>cbeK2KsIc5RjOW~jIjoC8Bq^K*)qG-DW zZVjHag%Xl4 )<(3#((_K1c zw}8>q=3}xWHD}mV&6yF4LKqn+JS?5)#_8=~JRd5{R$@^6T4hJ8oIIWLi0K5n?=ZBt ze0W?@nFMY7xpPk;o-i|h->+h7yui`#<@1VYGD233ZSvMXk-LDBt=XkQAX{6~q563- z$t*6_9{EpIWVi(pB44_((O``vi{U$!tYc27*D&b%lTQ1|0h{dYWj_VcX6L%@mLqge zv>I_d`$Dt&G|RE|ePx5L9{4Hg#NYv3=~R?mZ%jR<2`^`;Doge-j*gkUTF*LKD#@Gn z-8+r#H^O!5Z~5h=1E<%3M=In! JEFs zB9EpI#X;e+SPdT(tRBbZBAj(Y)X}^HWR{CnJvZrnfC`# #lodP2f$50t}7HV1%J&H4}ck`d<+dZ>OJSpIs%o?rN`l+ZF+)jXGJu7F#q z_GEwY_HDqV$b5JGluDp_nq>>8D9pQH5?&)vuO^kTEb}`vxn;AKa@%Q96tZ;(csJQU zk6Fm;A4?&ahPtQ*^CF@-0Yz$6|03I>V@zmmk!bx6(~nh+yAI8Y+<8SVM|Pt 5k*MEjj|KsAg9I~?tYiWvag`yL$Sq3?A^ zsFRMH2q3pVO(pdd@qU8NMt|tMgTBHt4<+WXbz=hc+<7>Id<-qM;8{xPM6ae&p?YA` zhY~9U36^uwmqVUzxyoJ&`!7Q+VA!F&hBAwJupB(wzPwSh96j!g$Rhrp`uHWav}9>{ zXO?Gk?>Kmb{~Che*1_rNnmbfW%q~osZI+cwpJiRpNGa#HTs9dwW-eX8i?QUVfFg*n z+bC)bxi=$pCuMuS-R(0Mlw=2NbDE^_1l$zz-TR~O@WP%>%N-7=fHTq(H(-;v-<>Tn z3 YYF KoECfuK6q*1F%La;l7AqV0qpaLnX7$<`wHdww>hzlw=Hy6C+3 z Q<1POnl=b3i #+XU*uSUY7%daDv*h@2^-<)7^^}L=PA@MNycfPE~xDl%VjZqlyx;ia% zN0_TS*_31?ipFBa%fKPPUL{jz&W1H0fWHi!jJx|9q4lh*{K0n302=nBTJSS9_5SjE zC8E{Lg(frf1ykwk-Vb1?Z}0G(2Y35#dQuPCc06LKq!G`&qw1@7=e%L5B&Tyr=MzNJ z?~IwV_hFQqOEu`yDctW}ghQhX^_slr9#Tpt%M!+;ybf@+ed` )9lcqIC5jyfYCwl11_(@h&rA^ zh5<$kBTk_xMrAH=sth&{6Gb`8U*4JbdVDfOiJdO-y;Csz)&UJ!rnkRNu~mB~Yr0uh zqT6Y+Dez)hEIE+hty_qdil|;Y*p*_!S)g=0yT4}1IsW)HwVQFkYV17a*U_@lsk5|i zPpi%`F*>=2d8%6-JWp2zWHl!6-qR|2LOr1TXyrJvQwUiieQ)s ai@8n!O z4=YEVEuCd?+nR!r)i`8-Su|gh^_`5~Cz -f9KRePE@EbKM#XDIBeo@VA_?CKAX$n|1( zCK8=yYtU`hNH18#)tm!z%_mYlTc&nVRM~5BBhQzrh>&U`y}EJH;%9YqWFr|^U`4}R z-Ge8bO^z6&BHSOrl+i(1!22$*B+P<}Xkn&T70%WqPPydLgAlH`Jn Z!zWe^~`dhBHhU_Cb=iXauE3c%RyO>(}9i1 $M$tke6Di74;R^fi#hU zt6sxvk57(SQ4OLw0#4IXd%3%~%URzm+82`feOr>=BS$-(_s)MWr!LH6-A^sN&?q(v zRA!shRlFV(J-8UdPUgf#jN`eR#_J9bJ2IJ_@ya|u-5%%?K{NZ_3AC1P;~b<|fuB+u z`!J2LMz*9Ydmx&~O9*$vQ#X^Nm#edELAiXJ-8UZauS@s51#fE9Ks|FL$Bg1Q;LW{> zF~q*QpPT6*l=JNlf3p$9e36cU>OuA8W}(D3jz&Zehy2^n=6XoQnBKO??aq0VQmLPb zz)s LxS78CW zexN$%__po6x`z~(fqh#LOwSN%ti~2B0O_58J1*0a*FrV|c~I>`j$T+Ayt>n2GWJiD z67?&CYuAW#Nwz>=x*`KvOYVXIo5ar^Xpc?$SW!iJKK`J`3OCOv14vksL=6)4PKh@5 zkGa^k_O{2>7}MZFZme+HypSU1CvkGSl3uYQT+Gjb15jfXcFYPlpipBe3liNrwiN{G zxax%^UQ2Bxin10reQ%jo_ogi72qP)nSmfY0kAEdoJ4g2bHh)3N`%QUno0>&E0-46Q zZz+!bzjXdbYN0G4nj;|ul6C(2Ye0kf)(Z*JJDihhfb`0pvh?(lEE%{E$WoH!R+`Ll z<(2C|cS?2uT6FR n`4tp_BAJ{5h+9`Gab ze36_=js%`bMH&AmHQ`srG0u<~mc`8Xf1&kf9Vi%72J`EXGekPzoFEh5)LN~();o3? zhzwHjdQ@Z(47^ !^r9oe)X@^AF zAe=VVm*^JUtljw`JbxU#SQ&+vB-*e{)K>!q{3JMea;0Y6l6Ar-W>nXsvNEiC93os+ z@SdtjDk*&>?L%x`KYIhwv-!q@S`eO|Y_5MKw|E$5B5MF|w#~g#UECxqNs(Ncbp>&s zg{vx<>Oqc5-ch|FxP&=<5v#Z|ec0l?me0}b0&VYuR_>&hJI3yxY^*9**sYju#iqEj z+2l)=FzI`yvfigdCSGS03f;(tq@F?V2uY1EQnmCY;`QbCh9Fh^p}_~0XR=kd2n##E zsHyZdO|`;2AJ=sBI$x4JJmP&IY$f8Wdlp;EqR^mREeaa%eSb{j?vttsJIkF7v2XiH zHoqLxm`|qfMvcC00tK%4 o+#m!=#My{|!@)0_hyEN)eEmF}XKOU{`JlB7;roiK) zuNDETKFw08>ynbge3t&M*1AXBdx|6CL?f=!cG;=aDahd #VIDAYs)A2Ul z?NzV}bXFZmTm3qD3H+k|NyM=)=IQ5TTkRgK*@@)_Tx+Nc&tMnbJYVMv6+c^7pQP`AS5 zR&~E*{p?yK4XYT4Yr)zF T}!mo >sqBR2AJ0m5*0oc@8gl?^SKBwr#y4T=RP9AkMWs;Is$M z$Uz#>;!K^TaVG;i*APelOwiIU-p@^!EMqV}d0^jNCHA2YM^c?Q*maLOnF9;Dm8C=1 zuzKy#O2$x(^laigZ}?&$1&i7?>KiT-!nphJFn1^V+k$LI@~YDOIUBcg2)EKZnGa~0 z;E{C}W2-E_ 9c8vC z)mU+)eveW=rv7$HEn97MX!N%nG bB0hHcBWxSFI7oxeYbb>&wkcz>_9 zJ(9&f`W)Qa8W_;q68oa7vP=IqvM*eUIewfkyE|~@{2%68De1=B=s?y6(Hd~~5&gWY zLEU+sRkc!P(AayKc@07q_dws~@_kB{{*F*Kk-?#L-)Hj229EsbP`Gq8``#7hx}Q6U zA5c+oHLVWDT$8yn)oo*ng 57~W^YHug?6s7X#fV4Y>zGv;pIdJ| zer~77+Im9YKGJ4+17_ BTMi~}y=uF&0J|wvXfhG>&T-Ynm%HI6BwUIAE?ctBm2MAt=ctI^ zuUH-%3lu9!^%Z!IedMmG6_vgP+VS{m<5Rk)GQ&fvH~iq*Wb&;@8$Q#El6-Wujph_m zGtni#YTp$Gxy$g~z=EL+r3=b#>P!D>y8|r@{=w-Xa)&=L%7SsXP8HhURQ=niU>NRo zOu{thQ+St0IdfNROYiz1%WtbuZ$zQKH|@#W?6Vh<1BK{Ix?}>7eOTo!X+K`} h+yg0}f`GJd&tWy^D}=|4z6Si5i5Lt(qY?k^C+7o*lcYDjGI24`b-X ze789^^GU4uun(w^qWaFyDdEeE&ZYS0Ew>bBPC(96%$t6NW*1zThJY6{ZvC+4erJ (o}dXU6WPZZT0jUNk?|~=&|c{n<{h4!Z+kX z?N(#oTdo%>^ZnT?!c+ZaXzRr1L6ua(i*x1TfPGJ4e3lmRT+N*CoP)SUtX3U8wguYI z_yK9@ncSp)_h|)IkvJJC` s`v=L>-pRFvt0yK z>wF?kXUlR`KcP%BD5sIh+TtXLZ z<(%Vc0xdc|geR@<{8)fhoKZh7R?}cbw?^It2d}|zf`aT|k81ZQ!p?F*Zjp3^+-e6U z?k0r5aEkL~oh$fVN6Msmeebg(x**ioS-w5)n4oatrEn8 XtkwF4+q!Tsp<>v<8&+P~7^?!vB%qpR_CB zB880P0RFW(gUFYHx^0oCeyq~ArIrR{lx8`J=PX8eUI)`b$j5@qmlbL+BCq{q`SO4` zhJb!@uFeq;FrO`~t^5o}#EshQC*2wxsW)E^_pK^-XO}ajx8}#>!MUOO o|@^=u1 z+}Y>QPpj{OV=1MStD>$(yBOi|Ex|qxkS>kP6P+d+tI^M|wXb8$(najn*;o1c7oDjG z7$~lU{dc8tE3q?yoU3cae4O{DRG;)VJ**26^U#4_4Jxz+Zy0ngca_)GHt_Pj|JKH9 zYHoFE6h9S}@lFrwGx}Pjlcwb~v6p-&Yl imJW8wc2|59pvD>+#tbf z1mf=Y#bCwK?8#ySopiLdScyCFUUly8oj(_smB7bW-}7Gft2E$Z8b#8pD&wb?cGAQ# z>tXhY+l%7tC!s51ojS>`Mt@xP9$)jUaIjdZZEucj;=cM92Ph6RU;Wo9`H3vCs$r`V zTp;#Mi6uOwa_&{Ysu@>>Kc?whULuZien(Pq??obgO&9AQbT5CPL(EOd*OS~ge|Pk^ z>O`ABe>;PC_W%D=XNa%;Zf$VYa@@4@R+#`RIO!UmiRBeGrdQQSPsHiF?UV^OT} z$A;1@(AvH*velI5dq~^cP}&T)MjVQ%4P{fmI4dYn%5o0rUsI aXdNAqHBNX*@lJ Ma7qb z);yq8gNcuZ2OpleuG1<|d^vVe2;qIn=t3Tw8oH6J#N*5;hKqJf=nuadhqnrr&OaLBD(;zF@7ZrHTEp6% zqV*r((aq11T5zymx%G{zhr6vSrp(kElovtsMN<|;yj)|VTRqU_;lkf5%jREqC2=joI-_g7~gYZM1 z=U(N*+Fzk^^mv}Cn>4&(tArr6>n_~>A9Q19;GFgJ- t~!d1E~5W=74KSog2agYmXr5pduR^8US<7GJqTN8ZP z_!X=$ikqXSPAenOcnO=+Y6Z9Ph>g#nP|89_aip1#7c!|@&k$v3;9Nb$cplJlmEtI8 zYW_~RpzVvvfl IHZt43koD0ZO8t%nHcr;Tg ztDqsf491eqEB8FLK&WWl8lpItR!S*w=f24=MZj~q7ZbQNeP2vGK)aqX@h950(z%3d zEm(<1V;3bYfT?Y-2EEH;7vsI)ws7(aIX4FI&VTojhb4>lLMZkfen?-()aTLoxdp_2 z9-RVYS@NCNuj_nh`YK*p0{T^KWOZe5WInzRQd`#jpJ}~y?4ku`+-NcMT&iit+5L;5 zc~6Ojb_gs6|ivT zx!|@O#pEwVV^#{{swazCnT8AhjhRq+(NaxnUx@Nk#9{#a&_ha5UcoX`U+AwtS0 z(RLkFEP<;seaMGmNdNNR^b^15(9s`MJU41EkcNLf6ah6e-vhj;egONNMf^8`OqQ~L zsrx2YGI-^?JOh8XPcm_vrsTB6+`otFXAKwxR^ _RSU)=5-xrWolA04%QXZt6w>iw&`eskT!PDAbN<)6=gy57He6EEL%(v4Y62| zD_j}J2Fw9j(16fy=Zs;6Lwb)Qy-ELwA(U3CedP2L&&VDl2HsD5mrXJb$X^;~fOiG| zGBRG#UZ|>;@tD%UZzKnX6|?OB6dB6M_2)?;yObV^243-F?&Jsi-&(lb4e3^fO9cUQ zX&E(MAu`bCe&cQbh&4e`y-{a6Ekn-AQBLtQn2bCu?gGY%5 )ZHFG^>NN-n5AeksyGnLdT0DIIf;Z-G9g%sf|vQY31 z8ABy8JAxbOBR4F&Jf^OLla(q(d99$RVh8+HGB}-E9~9C?^cCm$dRL~+u`c{YJhy+$ zWB!yl2PZykU;;su{78MRCcN};Gu=E#;pi2N6V@EZx!eAF{)D*sf@ryEV!Yb?;$jko zlXQ4By{$Lw_TrC|H7;F$%9hgm=i7fI&Pd{P8L;92l8E7%yyvT#Nbv-sX0=gcS?-V@ z^~cG#_uZ_mk4Fby#H?l#!QyTW{>kNPm+$>Vc}>>A@wcng#EaMLAJlMSYOCV>Xt-na zFB$@QO7Lp-vIrsSQ*EqfHCgK+S~+H;alOVs>eb{G)Ff-sZ#5NqgZCpvDb5K7;l#{o z{DZk{U@i=txH~_R0_1+#vLRq`PB9xIGH!=8ZBmqdJUgM(F_yEM(9{Vg0x4b~eY>i- z4vc7RIQ}AF;oNE__Rnci^R_>yMYB;qnESai(1=wdmg0$D%PKWFal$zLxM(dt_r;he z&QdllyNFZZX%9uo-0(~^Mw|+5)fCO|O}}R$?2*?JzRgQR=Q>u;xb;5rIgSzQ |RU2%say42rPV+O {6n8>SwLQ)C^IP3+a#Guo6v{fp8g0l3OvCv#9z{ zWgh%e;EltLrG>KU|6|=nMlHP$sPN#Z$o)k@Np5p58K!UQ0o0}-BYGb&rDEu6w0|vQ z`BOCvZkjJFiW$Vzj#B?4*P_}ppu6EWQU9^-Mg+PC2qu9M^!-ag3DZqfoMW#Lyg 9tS_N#QI6^q~kWfuu{D`)JGjLiRk>!gyppRzm@+83MOpsGLP@(h&sH}wZN|`c& z04koJ{+jeb1S^Vmug)3k7JKLf6j8n58A#7gL>SL6^EJ*$d6F;FdZewxyO#YJ+uuK| zWH?xW`dl=8QW})*9WScAT700v{UcIhh4KKX)Esg$X+4ZJ$$R!2VZ-B# bOpC%u1`YVnr*m}qZURIIuLmHjI|Us1W(HT5Aq(^*%7 z@8sx#;EeqNmHa&j(N*Qe;u;-`p+q9`EBq)Mf1&so)jurFq^%Z}g_aMLQ$YsEaR3d> zscNfU_GoVgcjrZhg4Dy{XYtI~7*pMeh?8RpKy6pjiq{lfUV{2P%lnxN=DB(FVZGv| zG6lZm9RQ05I2+x(E$;9aEW64)S?=qm7mMToX&G8iqUW*7Lig-ik fnqwc%1+WQ z$TBkB;Pw|#6cGzPV1A>H4Wm#-EQQthZk)Xl6FN6hxHe1@dFLI2%rX?A?(uSLV4wI6Ny zlFtCzn#B$x=bN`8fnzvXKC`}+qmDr`5h43@h>xyx{4UY+CPw05acK7CEy94hC~*4I zk}Nqm9+=ybRffx{VN-xNq?TCB=}J9tx)@-v{|*AMy#;=m gFN$-VUg7S$2L22u!4wyoXHP~??Y|6;H(E#XUuj}tc zLFv$#`s0E}%7o8*WKV2yVwP;X(Kx5Azi5-OQv<4fz{V?|cvW08idbPx9$B$vsN#v; zoPbm3EwAs16U;{HC0BF!4GFEorgV kq!=gsU`|#UIVb!Ds2ogg z&xXQ*)31n=JFhFXY{r9iSzn${&J4;gUOC~TdS9y{ow=fHJTjdBYV>~6c4yr~3c#^E zk%oG2&0UgS(fVeM_E9>6VCEy @`{bg8PB8BeMUyOrC*){B_a~TFw?3I7T9Pb`yH(z ze$+F%gYZYlLZvjko+WJmhCdZ1E*T$kdNdfAR$^gQL*FJbz%KPxGdnpl*^rp<&Nu&^ zz0y)s!ZG;$Ln8y>ap7*leVuC8Dj5nn-omkH5holGk|zKzkXDMehaaw)evR_^s8xK$ z(n8O6%nUh-W;`}N(OPFrdXBw!1{N+ewW~gZm(A^8lm5P@sSqtQrZet2Van@wk3~#Y zRg+c5M39|;F+ShwWb1jzOVmZ^Fm`{AE4hfd?n2$3Z*?QTva2&ELLEey`UKTB#1b7+ zi?n>d0w*<19-~Y58+>myJLy%)ri6V>^xW`H$H2z&)jqtBTO97SPsqMSCE6wv|E~r& zJd#7x9)8Rui b zSA -vbKjc z>4it}!t6f$#O26q1B s{Pz Y7{ZeOj!&mj|s1hb?2u08fARkHhM4W$ZrwP-NIIedat~-Y%Ph# zIJti;KdPENsoX<4YBsXq*%G_ofuFr59V@+Q%uIKmzslkk{;PiIa@BXsuWyG5FWK zLCTt7w3fYf8F$6*&<>vl^$uFKxq(inhmgSMA;q!$_LX*sP30{hq60&eJ~d^ZV7 z)hSee492Wn4s*DU{TKl$4u-$HSz$oN(wWO=qtEr1b&P78XYi6J8Q25mX!^w`lrFMH zaMDD(;mBl&8avK|`z?>GG9fPa{Z0F3XgXBw +t}&B z_p^I5zZZ=%QH!0u@n(;eGbU^7RCQaY=sn@5VW=CY?WcDMqSEqZvoSp=TfDw61?P%i zqevzcX*q~|2l2u!8a99gI`f*iu5XO#opJ&oNaWx`5m>HqmoIJcUc0v5am%E{Qr~MX z#<5oZkXP~ZFJtJ}A`Y%eu6M>fzKnVAa>DmOvhFCC+qOP;;VHiQR?d4Z5vq$pXJCYi zFZlwtW*-tjT4BWa5wjdsHChcLO-9xvTUpi&lp7EP>g4Y7fpFec#2f!Mzc_}TJ7!DU zETB9Mb`xs5upbkBySB)`lF7@mH4P}}U>_NovO?4y8oWopEY(tCru$wtmjbr=2=-Am z8~omQz(Xi7v$cN1mLk7v?rXJQEdGw>xu3RS6(AF}1jv?;q!eR(L~_I2-rLc{6e*`S zRUS528o)* e6XjlFb07cENPGJo1(VD3iNO~4HQ7e5F%28lEa;hH z@GCisl^(`h4}LS}RJ90>SO2wCTur5j-c}1ceE`Dk9r2O~AglfLjI?vLM_dgY_@8~3 zM&rjR<1vQbbSdv}@&Ty94WbHFP=w7l?iUs2w` 741Ay-HK$@`JF;zIIE6`A=1I#$$d2IOXqFHme^aV30b z7gP`+uXFJcZrJl{Zdj6Ol%W-M1^@>Qt(|orpZmS)Ix)-M^jO0)V@Vw4QS5^%iRXe% zY)aSNgsm1Y@$*zGt-~UI*L!+R2^865_ClZ5GFtRTj*Thkw?B--a=fk-NAQbo&hEL? zspF{X+2MYfj2Av9I6#C|x@47EhCxhJ$i64tR#XHuI@p5oRJ~Xl9(y73cc+=8?FNCK zjhlL$s;OsN^ouWfy&;}YeSR$|e*hCntoGeW40?}7q4e%poiEfSXJ`gQpUdV@n1okq zz8&taWZ`%x0#U1p95nz}N8S!mT5j0HzNrlChdF&cHteQ_R}SoFD9EGqtlS4v2Azes zZ#!GcMeG8AJrmmIQ#+CLiA_VG;xG*@I|7zzgDZ^kaV7cE+FpQV)r&z4^7qv0@_m>& zH>|r<4I#Tkp_k(^TgW7<%vn12<0^mCdm{}B&uL$OOdqwbQ}N6r^sn*Ht)tI%-c2B$ zPJQeyc-XeizPS&6&a^dzoOK&Ca!EraEFSH%w>+@~9wo?(^X%wNR23sSlT?n79Pykl z0BS8rOXYF)HZhpw8jyb2q-j(U9v?I@YA3vYA3zpe!0-48^yMY-RBEaSt~o$Ic0vSS z05xCuH*C{>v@5*?z~9!Aw>A=?G;Tb91^`xwI29*q7f> %p4lU*;00;t1I=@09rv}83x`~tW ze{Q)KKqyYo3|MNA>5TU2$w2rhg1*EOEwqKv!*K!r`u9X|7RT%kjZMB(V#nDUAi0O@ zV>kvvL@4bp1c0IirPz7p5G_&>S`wd~Mexq*UI38q(GeNsOs)#aIsyQ0aIsuyKpSVQ z1&wS`(kVXgl}r3rykRjAbki7?TFYhmi%HseWfRRrfK|*%i2?u!`k2p?kcF=@@cQy; zq!))qo$n33D40Y>-wN3Ws+plNZxfzX?FNgATRimY{=)D*n~uQa=El#}8<5OhZL2 z&&v0XDFAEZP>M0~W97C!5yNLAiM=-FBQnNRL2YR4jas%FIRF5129TP)&lFaQ5TEt$ z&56LrmFCk*(2+!yLcwsk!L}A>Nl=~Pe&~*d1V0H|P2m58{I}VNc`ND#lpRJz5~K=% zFkiD=XP5jCFo^0)YH%2!=KJ3Y=pjd61kPr#)uV<9VpwXO;xo%k+-&dL*}U10mjMe0 z>P5Eq-ioWy1e;W@Xb%EA9#!24Il;{k8BksD$y4$9Tc2nb+peVQI++206i!vSAz>c? z3zGQge9`=X)VQ(=l+B6>6g{$>=(kl#ySOF5mAf1o7V9bE)wVmT M_A^Y{`4fE+tp&W_lNTK2^QIN+~F z(ujKlCxH<$Skv6wdD?PB22(MVwF wqYODasvXcA~-I& zz%7t@ie>E#nFly*!W;E-yOhB(YH{dhBQv;V$>x;ZJLLy zy%N(7YlWO#xZ^H@8h1`9A%uz)0^v3=4s<_}{7bZU{n`~$=-3^q=!xu+$@*VRM-Bks zyZpQiJd?Z0E{$9ZV9|?VG IcF{|#8%CSYh+T7MKuh WloC!C zpwwOX92@`InRN*wp!=GtQ6`PnzZP_@v}&}Fh1GxGvG@M=e}ldc9~AAoMe+!^V(x?^ zlKs0p WY8PpL|{xw>6hE+9`42L9=eT$CnDR*X<7!tob_ z7T(k8wWS5OGwuR=UJNP=QdPMfl;W&9NAJ;4dNvYElmt*5c>ri+&P9;Ue*yj{FAbq{ zo%sC69&Y?UFz$$ ;PIvOq;O*LUa?gD~P@5HS zGeZoq7uuQ<{%a?VJJ78HUTTC|Zb2@fA*GSuAOpo*5mZv-3<@3KzY!RC|9bmD9dP>r z?_X~}$URn5GE>BycIQ*8u}IRx!=wNl=V`aOkW2pwl!$r$(;`Pmo4Pd=Id z-!F_XkpKX6694bq6_FIP(q*3~f@SKCg1`xk?X0S$csEj_2eO+HP74}fh#~f4?T|1& ze79%|lj9*@1MG?xPFF1saXi!opLe0Lxe@I5?X?mh&Hw3sk8-s{&&DFN +ShLwh(C&t2lsT#=`Kj$WE)QiU3MkebP+?pA8h6oV{agyeY$<~s9Z zW_8S4J8Q-sx`gi*-A}>@ZTuhZ@zDR00rj3jBfV2fu@6fR01x;Qlv{gQX(bBn|0~|v k^8fe>nyY!;LOJ+>moXnUd}WL7p!wPQ{K>)-*M9xK0AMZIWdHyG literal 0 HcmV?d00001 diff --git a/docs/tutorial_training/network_training.ipynb b/docs/tutorial_training/network_training.ipynb new file mode 100644 index 00000000..d63ac57b --- /dev/null +++ b/docs/tutorial_training/network_training.ipynb @@ -0,0 +1,474 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": true, + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "# Network Training" + ] + }, + { + "cell_type": "markdown", + "source": [ + "@[Chaoming Wang](mailto:adaduo@outlook.com)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "To maker your model powerful, you need to train your created network models. In this section, we are going to talk about how to train your network models." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 1, + "outputs": [], + "source": [ + "import brainpy as bp\n", + "import brainpy.math as bm\n", + "\n", + "bp.math.set_platform('cpu')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Setup a ``RNNTrainer``" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Once you create a model, setuping a structural trainer is just to instantiating a ``RNNTrainer``." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "source": [ + "model = (\n", + " bp.nn.Input(1)\n", + " >>\n", + " bp.nn.VanillaRNN(100)\n", + " >>\n", + " bp.nn.Dense(1)\n", + ")\n", + "model.initialize(1)\n", + "\n", + "# set up a ridge regression trainer\n", + "trainer = bp.nn.BPTT(model, loss='mean_squared_error')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, + "execution_count": 2, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "In the next, all you need is to provide your training data to the ``.fit()`` function." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "The **training data** feeding into the ``.fit()`` function can be a tuple or a list of ``(X, Y)`` pair, or a callable function which generate ``(x, y)`` data pairs.\n", + "\n", + "- If the providing training data is the ``(X, Y)`` data pair, ``X`` should be the input data which has the shape of `(num_sample, num_time, num_feature)`, ``Y`` should be the target data which has the shape of `(num_sample, num_time, num_feature)` for ``many-to-many`` training data mapping, or a data with the shape of `(num_sample, num_feature)` for ``many-to-final`` training data mapping.\n", + "\n", + "![](../_static/rnn_training_mapping.png)\n" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "- If the training data is a callable function, it should generate a Python generator which yield the pair of ``(X, Y)`` data for training. For example,\n", + "\n", + "```python\n", + "\n", + "# when calling this function,\n", + "# it will create a Python generator.\n", + "\n", + "def train_data():\n", + " num_data = 10\n", + " for _ in range(num_data):\n", + " # The (X, Y) data pair should be:\n", + " # - \"X\" is a tensor has the shape of\n", + " # \"(num_batch, num_time, num_feature)\"\n", + " # - \"Y\" is a tensor has the shape of\n", + " # \"(num_batch, num_time, num_feature)\"\n", + " # or \"(num_batch, num_feature)\"\n", + " xs = bm.random.rand(1, 20, 2)\n", + " ys = bm.random.random((1, 20, 2))\n", + " yield xs, ys\n", + "```\n" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "However, all these data constraints can be released when you customize your training procedures. Please see XXXXXX." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "It is worthy to note that before fitting your data by calling ``.fit()`` function, you need to **initialize the model** by specifying the batch size your data are using. Otherwise, an error will cause." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Supported training algorithms" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Currently, BrainPy provides several ways to train recurrent neural networks, including ridge regression, FORCE learning, and back-propagation through time algorithms, etc. The full list of the supported training algorithms please see the [API documentation](../apis/auto/nn/runners.rst). Here we only talk about few of them." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Ridge regression" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Shared parameters" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Sometimes, there are some global parameters which are shared across all nodes. For example, the training or testing phase control parameter ``train=True/False``. Here, we use one simple model to demonstrate how to provide shared parameters when we calling models." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [], + "source": [ + "model = (\n", + " bp.nn.Input(1)\n", + " >>\n", + " bp.nn.VanillaRNN(100)\n", + " >>\n", + " bp.nn.Dropout(0.3)\n", + " >>\n", + " bp.nn.Dense(1)\n", + ")\n", + "model.initialize(3)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "These shared parameters can be provided as two kinds of ways:\n", + "\n", + "- When you are using the instantiated model, you can directly provide them when calling this model." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [ + { + "data": { + "text/plain": "JaxArray([[-2.1306934],\n [ 1.4046229],\n [ 1.2039466]], dtype=float32)" + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model(bm.random.rand(3, 1), train=True)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [ + { + "data": { + "text/plain": "JaxArray([[-0.18169183],\n [-0.09682302],\n [-0.09607743]], dtype=float32)" + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model(bm.random.rand(3, 1), train=False)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "- When you are using the structural runners like ``brainpy.nn.RNNRunner`` or ``brainpy.nn.BPTT`` trainer, you can warp all shared parameters in an argument ``shared_kwargs``." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [], + "source": [ + "runner = bp.nn.RNNRunner(model)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [ + { + "data": { + "text/plain": " 0%| | 0/10 [00:00, ?it/s]", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "ea880617f374408d9a5cd758d136dd01" + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": "JaxArray([[[-0.34313297],\n [-0.23117486],\n [-0.30522805],\n [-0.32745296],\n [ 0.34081894],\n [ 0.15859577],\n [-0.12556326],\n [-0.08884146],\n [-0.11673015],\n [ 0.07351794]],\n\n [[ 0.58661526],\n [-0.01611713],\n [-0.28982073],\n [-0.06223251],\n [-0.02380377],\n [-0.25092548],\n [ 0.1635698 ],\n [-0.21967886],\n [ 0.20144288],\n [-0.37496752]],\n\n [[-0.30316865],\n [-0.30312598],\n [-0.19248168],\n [-0.0053116 ],\n [-0.19092566],\n [-0.10831024],\n [-0.04929686],\n [ 0.00246187],\n [-0.00367273],\n [-0.03972476]]], dtype=float32)" + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "runner.predict(bm.random.random((3, 10, 1)),\n", + " shared_kwargs={'train': True})" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "data": { + "text/plain": " 0%| | 0/10 [00:00, ?it/s]", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "0270dd8d9e284ed4a4621221aa7933d1" + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": "JaxArray([[[-0.04736265],\n [-0.03275637],\n [ 0.07194265],\n [-0.07143855],\n [-0.07852352],\n [ 0.0554626 ],\n [-0.07382635],\n [-0.00669023],\n [-0.04883794],\n [-0.0540965 ]],\n\n [[-0.06040927],\n [ 0.10728423],\n [-0.09903762],\n [-0.04275577],\n [ 0.03387596],\n [-0.07266021],\n [-0.01200375],\n [ 0.00651347],\n [-0.01666619],\n [-0.08301818]],\n\n [[-0.02191623],\n [-0.10554922],\n [ 0.01432531],\n [-0.03919244],\n [-0.05116755],\n [ 0.10237596],\n [-0.13605613],\n [ 0.02815703],\n [-0.02858308],\n [-0.08084894]]], dtype=float32)" + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "runner.predict(bm.random.random((3, 10, 1)),\n", + " shared_kwargs={'train': False})" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + } + ], + "metadata": { + "kernelspec": { + "name": "brainpy", + "language": "python", + "display_name": "brainpy" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/docs/tutorial_training/node_specification.ipynb b/docs/tutorial_training/node_specification.ipynb index 392c6c35..1d0fa630 100644 --- a/docs/tutorial_training/node_specification.ipynb +++ b/docs/tutorial_training/node_specification.ipynb @@ -34,7 +34,7 @@ "import brainpy as bp\n", "import brainpy.math as bm\n", "\n", - "# bp.math.set_platform('cpu')" + "bp.math.set_platform('cpu')" ] }, { @@ -86,9 +86,7 @@ "It also has several boolean attributes:\n", "\n", "- ``trainable``: whether the node can be trained by ``brainpy.nn.RNNTrainer`` or other specified training methods. \n", - "- ``is_ff_initialized``: whether the node has initialized the feedforward (input) connections. \n", - "- ``is_fb_initialized``: whether the node has initialized the feedback (input) connections. \n", - "- ``is_state_initialized``: whether the node state has been initialized. \n" + "- ``is_initialized``: whether the node has been initialized.\n" ] }, { @@ -412,15 +410,15 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "id": "8b9dc0a2", "metadata": {}, "outputs": [ { "data": { - "text/plain": "TrainVar([[-0.24284273, 0.1005571 , -0.9604599 ],\n [ 0.09039249, 0.4681486 , 0.666192 ],\n [ 0.24261971, 0.55806404, -0.30615446],\n [-0.99454516, -0.06997029, -0.04132903]], dtype=float32)" + "text/plain": "TrainVar([[ 0.40094635, 0.42721465, -0.17949589],\n [-0.11491717, -0.16497128, 0.02432666],\n [-0.850292 , -0.24328166, 0.67059135],\n [-0.0637897 , 1.0190561 , -0.7600854 ]], dtype=float32)" }, - "execution_count": 10, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -431,13 +429,89 @@ "\n", "l.weights" ] + }, + { + "cell_type": "markdown", + "source": [ + "Moreover, for a subclass of ``brainpy.nn.RecurrentNode``, the ``state`` can be set to be trainable or not trainable by ``state_trainable`` argument. When setting ``state_trainable=True`` for an instance of ``brainpy.nn.RecurrentNode``, a new attribute *.train_state* will be created." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "data": { + "text/plain": "TrainVar([0.09885836, 0.67417526, 0.5645789 ], dtype=float32)" + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rnn = bp.nn.VanillaRNN(3, input_shape=(1,), state_trainable=True)\n", + "rnn.initialize(3)\n", + "\n", + "rnn.train_state" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Note the difference between the *.train_state* and the original *.state*:\n", + "\n", + "1. *.train_state* has no batch axis.\n", + "2. When using `node.init_state()` or `node.initialize()` function, all values in the *.state* will be filled with *.train_state*." + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [ + { + "data": { + "text/plain": "Variable([[0.09885836, 0.67417526, 0.5645789 ],\n [0.09885836, 0.67417526, 0.5645789 ],\n [0.09885836, 0.67417526, 0.5645789 ]], dtype=float32)" + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rnn.state" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "name": "brainpy", "language": "python", - "name": "python3" + "display_name": "brainpy" }, "language_info": { "codemirror_mode": { -- 2.34.1 From deeffa8a8205e1e4859365bee775b41af728b70c Mon Sep 17 00:00:00 2001 From: chaoming Date: Tue, 15 Mar 2022 22:05:52 +0800 Subject: [PATCH 14/88] update docs --- docs/quickstart/dynamics_intro.ipynb | 523 ------------------ docs/tutorial_training/index.rst | 2 + .../training_customization.ipynb | 37 ++ 3 files changed, 39 insertions(+), 523 deletions(-) delete mode 100644 docs/quickstart/dynamics_intro.ipynb create mode 100644 docs/tutorial_training/training_customization.ipynb diff --git a/docs/quickstart/dynamics_intro.ipynb b/docs/quickstart/dynamics_intro.ipynb deleted file mode 100644 index 17b1255e..00000000 --- a/docs/quickstart/dynamics_intro.ipynb +++ /dev/null @@ -1,523 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Dynamics Programming Introduction" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "@[Chaoming Wang](https://github.com/chaoming0625)\n", - "@[Xiaoyu Chen](mailto:c-xy17@tsinghua.org.cn)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "> What I cannot create, I do not understand. --- Richard Feynman" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The brain is a complex dynamical system. To simulate the dynamics of the brain, one of the most important things is to model the dynamically changed states of each component. Mathematically, the dynamics of a system can be expressed as\n", - "\n", - "$$\n", - "\\dot{X} = f(X, t)\n", - "$$\n", - "\n", - "where $X$ is the state of the system, $t$ is the time, and $f$ is a function describes the time dependence of the system state. \n", - "\n", - "Simulation of such dynamical systems is called **dynamic modeling**. BrainPy provides users with various tools and convenient interface for neurodynamic modeling, including **dynamic building**, **dynamic simulation**, **dynamic analysis** and **dynamic training**. This section helps users to get familiar with the basic structure and common operations of neurodynamic modeling in BrainPy." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2021-03-25T03:02:48.939126Z", - "start_time": "2021-03-25T03:02:47.073698Z" - } - }, - "outputs": [], - "source": [ - "import brainpy as bp\n", - "import brainpy.math as bm\n", - "\n", - "bp.math.set_platform('cpu')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dynamical System Building" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In BrainPy, [``brainpy.DynamicalSystem``](../apis/auto/building/generated/brainpy.building.brainobjects.DynamicalSystem.rst) is used to define dynamic brain objects. Various children classes are implemented to build different elements, such as [brainpy.NeuGroup](../apis/auto/building/generated/brainpy.building.brainobjects.NeuGroup.rst) for neuron group modeling, [brainpy.TwoEndConn](../apis/auto/building/generated/brainpy.building.brainobjects.TwoEndConn.rst) for synaptic computation, [brainpy.Network](../apis/auto/building/generated/brainpy.building.brainobjects.Network.rst) for network modeling, etc. Arbitrary composition of these objects is also an instance of ``brainpy.DynamicalSystem``. Therefore, ``brainpy.DynamicalSystem`` is the universal language to define dynamical models in BrainPy. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "According to the definition of a dynamical system, any subclass of ``brainpy.DynamicalSystem`` must implement the updating rule in the *update* function (``def update(self, _t, _dt)``), and dynamically changed variables should be defined in the system." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "class YourDynamicalSystem(bp.DynamicalSystem):\n", - " \n", - " def __init__(self):\n", - " # define dynamically changed variables\n", - " pass\n", - " \n", - " def update(self, _t, _dt):\n", - " # update the variables\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we illustrate how to build a dynamcial system by using the well known [FitzHugh–Nagumo neuron model](https://brainmodels.readthedocs.io/en/latest/apis/generated/brainmodels.neurons.FHN.html), whose dynamics is given by: \n", - "\n", - "$$\n", - "{\\dot {v}}=v-{\\frac {v^{3}}{3}}-w+I, \\\\\n", - "\\tau {\\dot {w}}=v+a-bw.\n", - "$$" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This model contains two differential equations. In BrainPy, the numerical integration of ordinary differential equations can be accomplished with [brainpy.odeint](../apis/integrators/generated/brainpy.integrators.odeint.rst) (please see [Numerical Integrator](../tutorial_intg/index.rst) for more details). \n", - "\n", - "The above two differential equations as Python functions can be defined as:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def dV(V, t, w, Iext=0.): \n", - " return V - V * V * V / 3 - w + Iext\n", - " \n", - "def dw(w, t, V, a=0.7, b=0.8): \n", - " return (V + a - b * w) / self.tau" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "where ``t`` is the time variable, **arguments before ``t`` are variables, and arguments after ``t`` are parameters**.\n", - "\n", - "Thereafter, the numerical solvers for the two equations can be defined as:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "int_V = bp.odeint(dV, method='euler')\n", - "\n", - "int_w = bp.odeint(dw, method='euler')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "where the ``method`` defines the numerical integration method to use (all implemented methods can be referred to in [Numerical Solvers for ODEs](../tutorial_intg/ode_numerical_solvers.ipynb)). " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The FitzHugh–Nagumo neuron model can be defined as a Python class, in which the parameters, variables, and integral functions are defined in the constructor ``__init__()``, and the updating rule from the current time $\\mathrm{\\_t}$ to the next time $\\mathrm{\\_t + \\_dt}$ can be defined in the update function ``update()``." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "class FitzHughNagumoModel(bp.DynamicalSystem):\n", - " def __init__(self, num, method='exp_auto'):\n", - " super(FitzHughNagumoModel, self).__init__()\n", - "\n", - " # parameters\n", - " self.a = 0.7\n", - " self.b = 0.8\n", - " self.tau = 12.5\n", - "\n", - " # variables\n", - " self.V = bm.Variable(bm.zeros(num))\n", - " self.w = bm.Variable(bm.zeros(num))\n", - " self.Iext = bm.Variable(bm.zeros(num)) # to receive the external input\n", - "\n", - " # functions\n", - " def dV(V, t, w, Iext=0.): \n", - " return V - V * V * V / 3 - w + Iext\n", - " def dw(w, t, V, a=0.7, b=0.8): \n", - " return (V + a - b * w) / self.tau\n", - " self.int_V = bp.odeint(dV, method=method)\n", - " self.int_w = bp.odeint(dw, method=method)\n", - "\n", - " def update(self, _t, _dt):\n", - " self.V.value = self.int_V(self.V, _t, self.w, self.Iext, _dt)\n", - " self.w.value = self.int_w(self.w, _t, self.V, self.a, self.b, _dt)\n", - " self.Iext[:] = 0." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "# instantiation\n", - "fhn = FitzHughNagumoModel(2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In BrainPy, any dynamical model can be defined as a Python class. More advanced usage of dynamical system building can be obtained in [Dynamics Building](../tutorial_building/index.rst)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dynamical System Simulation" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Dynamics simulation in BrainPy is highly efficient. It can deploy models to CPUs or GPUs. To switch the backend device, you can use ``brainpy.math.set_platform(\"cpu\" or \"gpu\")`` at the top of your script. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Runners are used for dynamic simulation. They can [**monitor**](../tutorial_simulation/monitors_and_inputs.ipynb) variable trajectories and give [**inputs**](../tutorial_simulation/monitors_and_inputs.ipynb) to target variables during simulation. Currently, BrainPy provides several [runners](../apis/auto/simulation/runner.rst) to satisfy different simulation requirements. Here, we use ``brainpy.StructRunner`` to run the above instance ``fhn``. During simulation, we monitor variables ``V`` and ``w``, and give inputs to ``Iext`` variable. " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "dcefc71b42c64080916703e550f1b365", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - " 0%| | 0/1000 [00:00, ?it/s]" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "0.1874241828918457" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "runner = bp.StructRunner(fhn, monitors=['V', 'w'], inputs=['Iext', 1.])\n", - "\n", - "# run 100 time units\n", - "runner.run(100.)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The monitored values of variables over time are recorded in ``runner.mon`` and can be visualized through [brainpy.visualize](../apis/auto/visualization.rst)." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "