From e86fc5aa2949666e4bdf1d39e361cae3b6f724fb Mon Sep 17 00:00:00 2001 From: Marcel Stimberg Date: Fri, 21 Jun 2024 00:08:22 +0200 Subject: [PATCH] remove many tests --- brian2/tests/test_cpp_standalone.py | 476 ++++++++++++++++++++++++++++ 1 file changed, 476 insertions(+) diff --git a/brian2/tests/test_cpp_standalone.py b/brian2/tests/test_cpp_standalone.py index 051c511a0..7dc5f947e 100644 --- a/brian2/tests/test_cpp_standalone.py +++ b/brian2/tests/test_cpp_standalone.py @@ -44,3 +44,479 @@ def test_cpp_standalone(): assert len(M.t) == len(M.i) assert M.t[0] == 0.0 reset_device() + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_delete_code_data(): + set_device("cpp_standalone", build_on_run=True, directory=None) + group = NeuronGroup(10, "dv/dt = -v / (10*ms) : volt", method="exact") + group.v = np.arange(10) * mV # uses the static array mechanism + run(defaultclock.dt) + results_dir = os.path.join(device.project_dir, "results") + assert os.path.exists(results_dir) and os.path.isdir(results_dir) + # There should be 3 files for the clock, 2 for the neurongroup (index + v), + # and the "last_run_info.txt" file + assert len(os.listdir(results_dir)) == 6 + device.delete(data=True, code=False, directory=False) + assert os.path.exists(results_dir) and os.path.isdir(results_dir) + assert len(os.listdir(results_dir)) == 0 + assert len(os.listdir(os.path.join(device.project_dir, "static_arrays"))) > 0 + assert len(os.listdir(os.path.join(device.project_dir, "code_objects"))) > 0 + device.delete(data=False, code=True, directory=False) + assert len(os.listdir(os.path.join(device.project_dir, "static_arrays"))) == 0 + assert len(os.listdir(os.path.join(device.project_dir, "code_objects"))) == 0 + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_delete_directory(): + set_device("cpp_standalone", build_on_run=True, directory=None) + group = NeuronGroup(10, "dv/dt = -v / (10*ms) : volt", method="exact") + group.v = np.arange(10) * mV # uses the static array mechanism + run(defaultclock.dt) + # Add a new file + dummy_file = os.path.join(device.project_dir, "results", "dummy.txt") + open(dummy_file, "w").flush() + assert os.path.isfile(dummy_file) + with catch_logs() as logs: + device.delete(directory=True) + assert len(logs) == 1 + assert os.path.isfile(dummy_file) + with catch_logs() as logs: + device.delete(directory=True, force=True) + assert len(logs) == 0 + # everything should be deleted + assert not os.path.exists(device.project_dir) + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_multiple_standalone_runs(): + # see github issue #1189 + set_device("cpp_standalone", directory=None) + + network = Network() + Pe = NeuronGroup(1, "v : 1", threshold="False") + C_ee = Synapses(Pe, Pe, on_pre="v += 1") + C_ee.connect() + network.add(Pe, C_ee) + network.run(defaultclock.dt) + + device.reinit() + device.activate(directory=None) + + network2 = Network() + Pe = NeuronGroup(1, "v : 1", threshold="False") + C_ee = Synapses(Pe, Pe, on_pre="v += 1") + C_ee.connect() + network2.add(Pe, C_ee) + network2.run(defaultclock.dt) + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_continued_standalone_runs(): + # see github issue #1237 + set_device("cpp_standalone", build_on_run=False) + + source = SpikeGeneratorGroup(1, [0], [0] * ms) + target = NeuronGroup(1, "v : 1") + C_ee = Synapses(source, target, on_pre="v += 1", delay=2 * ms) + C_ee.connect() + run(1 * ms) + # Spike has not been delivered yet + run(2 * ms) + + device.build(directory=None) + assert target.v[0] == 1 # Make sure the spike got delivered + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_constant_replacement(): + # see github issue #1276 + set_device("cpp_standalone") + x = 42 + G = NeuronGroup(1, "y : 1") + G.y = "x" + run(0 * ms) + assert G.y[0] == 42.0 + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_change_parameter_without_recompile(): + prefs.core.default_float_dtype = np.float32 + set_device("cpp_standalone", directory=None, with_output=True, debug=True) + on_off = TimedArray([True, False, True], dt=defaultclock.dt, name="on_off") + stim = TimedArray( + np.arange(30).reshape(3, 10) * nA, dt=defaultclock.dt, name="stim" + ) + G = NeuronGroup( + 10, + """ + x : 1 (constant) + v : volt (constant) + n : integer (constant) + b : boolean (constant) + s = int(on_off(t))*stim(t, i) : amp + """, + name="neurons", + ) + G.x = np.arange(10) + G.n = np.arange(10) + G.b = np.arange(10) % 2 == 0 + G.v = np.arange(10) * volt + mon = StateMonitor(G, "s", record=True) + run(3 * defaultclock.dt) + assert array_equal(G.x, np.arange(10)) + assert array_equal(G.n, np.arange(10)) + assert array_equal(G.b, np.arange(10) % 2 == 0) + assert array_equal(G.v, np.arange(10) * volt) + assert_allclose( + mon.s.T / nA, + np.array( + [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # on_off(t) == False + [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], + ] + ), + ) + + device.run( + run_args=[ + "neurons.x=5", + "neurons.v=3", + "neurons.n=17", + "neurons.b=True", + "on_off.values=True", + ] + ) + assert array_equal(G.x, np.ones(10) * 5) + assert array_equal(G.n, np.ones(10) * 17) + assert array_equal(G.b, np.ones(10, dtype=bool)) + assert array_equal(G.v, np.ones(10) * 3 * volt) + assert_allclose( + mon.s.T / nA, + np.array( + [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], + [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], + ] + ), + ) + ar = np.arange(10) * 2.0 + ar.astype(G.x.dtype).tofile(os.path.join(device.project_dir, "init_values_x1.dat")) + ar.astype(G.n.dtype).tofile(os.path.join(device.project_dir, "init_values_n1.dat")) + (np.arange(10) % 2 != 0).tofile( + os.path.join(device.project_dir, "init_values_b1.dat") + ) + ar.astype(G.v.dtype).tofile(os.path.join(device.project_dir, "init_values_v1.dat")) + ar2 = 2 * np.arange(30).reshape(3, 10) * nA + ar2.astype(stim.values.dtype).tofile( + os.path.join(device.project_dir, "init_stim_values.dat") + ) + device.run( + run_args=[ + "neurons.v=init_values_v1.dat", + "neurons.x=init_values_x1.dat", + "neurons.b=init_values_b1.dat", + "neurons.n=init_values_n1.dat", + "stim.values=init_stim_values.dat", + ] + ) + assert array_equal(G.x, ar) + assert array_equal(G.n, ar) + assert array_equal(G.b, np.arange(10) % 2 != 0) + assert array_equal(G.v, ar * volt) + assert_allclose( + mon.s.T / nA, + np.array( + [ + [0, 2, 4, 6, 8, 10, 12, 14, 16, 18], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # on_off(t) == False + [40, 42, 44, 46, 48, 50, 52, 54, 56, 58], + ] + ), + ) + reset_device() + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_change_parameter_without_recompile_errors(): + set_device("cpp_standalone", directory=None, with_output=False) + G = NeuronGroup(10, "v:volt", name="neurons") + G.v = np.arange(10) * volt + + run(0 * ms) + + with pytest.raises(DimensionMismatchError): + device.run(run_args={G.v: 5}) + with pytest.raises(DimensionMismatchError): + device.run(run_args={G.v: 5 * siemens}) + with pytest.raises(TypeError): + device.run(run_args={G.v: np.arange(9) * volt}) + + reset_device() + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_change_parameter_without_recompile_dict_syntax(): + set_device("cpp_standalone", directory=None, with_output=False) + on_off = TimedArray([True, False, True], dt=defaultclock.dt, name="on_off") + stim = TimedArray( + np.arange(30).reshape(3, 10) * nA, dt=defaultclock.dt, name="stim" + ) + G = NeuronGroup( + 10, + """ + x : 1 (constant) + n : integer (constant) + b : boolean (constant) + v : volt (constant) + s = int(on_off(t))*stim(t, i) : amp + """, + name="neurons", + ) + G.x = np.arange(10) + G.n = np.arange(10) + G.b = np.arange(10) % 2 == 0 + G.v = np.arange(10) * volt + mon = StateMonitor(G, "s", record=True) + run(3 * defaultclock.dt) + assert array_equal(G.x, np.arange(10)) + assert array_equal(G.n, np.arange(10)) + assert array_equal(G.b, np.arange(10) % 2 == 0) + assert array_equal(G.v, np.arange(10) * volt) + assert_allclose( + mon.s.T / nA, + np.array( + [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # on_off(t) == False + [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], + ] + ), + ) + device.run(run_args={G.x: 5, G.v: 3 * volt, G.n: 17, G.b: True, on_off: True}) + assert array_equal(G.x, np.ones(10) * 5) + assert array_equal(G.n, np.ones(10) * 17) + assert array_equal(G.b, np.ones(10, dtype=bool)) + assert array_equal(G.v, np.ones(10) * 3 * volt) + assert_allclose( + mon.s.T / nA, + np.array( + [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], + [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], + ] + ), + ) + ar = np.arange(10) * 2.0 + ar2 = 2 * np.arange(30).reshape(3, 10) * nA + device.run( + run_args={ + G.x: ar, + G.v: ar * volt, + G.n: ar, + G.b: np.arange(10) % 2 != 0, + stim: ar2, + } + ) + assert array_equal(G.x, ar) + assert array_equal(G.n, ar) + assert array_equal(G.b, np.arange(10) % 2 != 0) + assert array_equal(G.v, ar * volt) + assert_allclose( + mon.s.T / nA, + np.array( + [ + [0, 2, 4, 6, 8, 10, 12, 14, 16, 18], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # on_off(t) == False + [40, 42, 44, 46, 48, 50, 52, 54, 56, 58], + ] + ), + ) + reset_device() + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_change_synapse_parameter_without_recompile_dict_syntax(): + set_device("cpp_standalone", directory=None, with_output=False) + G = NeuronGroup(10, "", name="neurons") + S = Synapses(G, G, "w:1", name="Synapses") + S.connect(j="i") + S.w = np.arange(10) + run(0 * ms) + assert array_equal(S.w, np.arange(10)) + + device.run(run_args={S.w: 17}) + assert array_equal(S.w, np.ones(10) * 17) + + ar = np.arange(10) * 2.0 + device.run(run_args={S.w: ar}) + assert array_equal(S.w, ar) + + reset_device() + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_change_parameter_without_recompile_dependencies(): + set_device("cpp_standalone", directory=None, with_output=False) + G = NeuronGroup( + 10, + """ + v:volt + w:1 + """, + name="neurons", + ) + G.v = np.arange(10) * volt + device.apply_run_args() + G.w = "v/volt*2" + + run(0 * ms) + assert array_equal(G.v, np.arange(10)) + assert array_equal(G.w, np.arange(10) * 2) + + device.run(run_args=["neurons.v=5"]) + assert array_equal(G.v, np.ones(10) * 5 * volt) + assert array_equal(G.w, np.ones(10) * 5 * 2) + + ar = np.arange(10) * 2.0 + ar.astype(G.v.dtype).tofile(os.path.join(device.project_dir, "init_values_v2.dat")) + device.run(run_args=[f"neurons.v=init_values_v2.dat"]) + assert array_equal(G.v, ar * volt) + assert array_equal(G.w, ar * 2) + + reset_device() + + +class RunSim: + def __init__(self): + self.device = get_device() + self.G = NeuronGroup( + 10, + """ + v:volt + w:1 + x:1 + """, + name="neurons", + ) + run(0 * ms) + + def run_sim(self, idx): + # Ugly hack needed for windows + device_module.active_device = self.device + device.run( + results_directory=f"results_{idx}", + run_args={ + self.G.v: idx * volt, + self.G.w: np.arange(10), # Same values for all processes + self.G.x: np.arange(10) * idx, # Different values + }, + ) + return self.G.v[:], self.G.w[:], self.G.x[:] + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_change_parameters_multiprocessing(): + set_device("cpp_standalone", directory=None) + sim = RunSim() + + import multiprocessing + + with multiprocessing.Pool() as p: + results = p.map(sim.run_sim, range(5)) + + for idx, result in zip(range(5), results): + v, w, x = result + assert array_equal(v, np.ones(10) * idx * volt) + assert array_equal(w, np.arange(10)) + assert array_equal(x, np.arange(10) * idx) + + +@pytest.mark.cpp_standalone +@pytest.mark.standalone_only +def test_header_file_inclusion(): + set_device("cpp_standalone", directory=None, debug=True) + with tempfile.TemporaryDirectory() as tmpdir: + with open(os.path.join(tmpdir, "foo.h"), "w") as f: + f.write( + """ + namespace brian_test_namespace { + extern double test_variable; + } + """ + ) + with open(os.path.join(tmpdir, "foo.cpp"), "w") as f: + f.write( + """ + namespace brian_test_namespace { + double test_variable = 42; + } + """ + ) + + @implementation( + "cpp", + """ + double brian_function(int index) { + using namespace brian_test_namespace; + return test_variable * index; + } + """, + headers=['"foo.h"'], + sources=[os.path.join(tmpdir, "foo.cpp")], + include_dirs=[tmpdir], + ) + @check_units(index=1, result=1) + def brian_function(index): + raise NotImplementedError() + + # Use the function in a somewhat convoluted way that exposes errors in the + # code generation process + G = PoissonGroup(5, rates="brian_function(i)*Hz") + S = Synapses(G, G, "rate_copy : Hz") + S.connect(j="i") + S.run_regularly("rate_copy = rates_pre") + run(defaultclock.dt) + assert_allclose(S.rate_copy[:], np.arange(len(G)) * 42 * Hz) + + +if __name__ == "__main__": + for t in [ + test_cpp_standalone, + test_multiple_connects, + test_storing_loading, + test_openmp_consistency, + test_duplicate_names_across_nets, + test_openmp_scalar_writes, + test_time_after_run, + test_array_cache, + test_run_with_debug, + test_changing_profile_arg, + test_profile_via_set_device_arg, + test_delete_code_data, + test_delete_directory, + test_multiple_standalone_runs, + test_change_parameter_without_recompile, + test_change_parameter_without_recompile_errors, + test_change_parameter_without_recompile_dict_syntax, + test_change_parameter_without_recompile_dependencies, + test_change_synapse_parameter_without_recompile_dict_syntax, + test_change_parameters_multiprocessing, + test_header_file_inclusion, + ]: + t() + reinit_and_delete()