-
Notifications
You must be signed in to change notification settings - Fork 239
Fix issue run at #1602
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Fix issue run at #1602
Changes from 1 commit
738d92e
f4c7eb0
a42c5f8
f8975be
1b5a0cf
9019e10
a586a11
629b0fa
4b51bbe
46f34ad
ceeed3d
9a20d0c
a842da8
37fbafe
8bc0dfe
dd43b8c
7c37d40
fd4b352
bd16629
2402c23
2085b7f
49b5f71
c30592b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
|
||
__docformat__ = "restructuredtext en" | ||
|
||
from abc import ABC, abstractmethod | ||
|
||
import numpy as np | ||
|
||
from brian2.core.names import Nameable | ||
|
@@ -37,7 +39,7 @@ def check_dt(new_dt, old_dt, target_t): | |
------ | ||
ValueError | ||
If using the new dt value would lead to a difference in the target | ||
time of more than Clock.epsilon_dt times `new_dt (by default, | ||
time of more than `Clock.epsilon_dt` times ``new_dt`` (by default, | ||
0.01% of the new dt). | ||
|
||
Examples | ||
|
@@ -63,9 +65,12 @@ def check_dt(new_dt, old_dt, target_t): | |
) | ||
|
||
|
||
class BaseClock(VariableOwner): | ||
class BaseClock(VariableOwner, ABC): | ||
""" | ||
Base class for all clocks in the simulator. | ||
Abstract base class for all clocks in the simulator. | ||
|
||
This class should never be instantiated directly, use one of the subclasses | ||
like Clock or EventClock instead. | ||
|
||
Parameters | ||
---------- | ||
|
@@ -76,6 +81,8 @@ class BaseClock(VariableOwner): | |
epsilon = 1e-14 | ||
|
||
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) | ||
self.variables = Variables(self) | ||
self.variables.add_array( | ||
|
@@ -98,20 +105,22 @@ def __init__(self, name): | |
self._i_end = None | ||
logger.diagnostic(f"Created clock {self.name}") | ||
|
||
@abstractmethod | ||
def advance(self): | ||
""" | ||
Advance the clock to the next time step. | ||
Must be implemented by subclasses. | ||
""" | ||
raise NotImplementedError("This method must be implemented by subclasses") | ||
pass | ||
|
||
@abstractmethod | ||
@check_units(start=second, end=second) | ||
def set_interval(self, start, end): | ||
""" | ||
Set the start and end time of the simulation. | ||
Must be implemented by subclasses. | ||
""" | ||
raise NotImplementedError("This method must be implemented by subclasses") | ||
pass | ||
|
||
def __lt__(self, other): | ||
return ( | ||
|
@@ -131,6 +140,7 @@ def __le__(self, other): | |
def __ge__(self, other): | ||
return self.__gt__(other) or self.same_time(other) | ||
|
||
@abstractmethod | ||
def same_time(self, other): | ||
""" | ||
Check if two clocks are at the same time (within epsilon). | ||
|
@@ -145,10 +155,7 @@ def same_time(self, other): | |
bool | ||
True if both clocks are at the same time | ||
""" | ||
t1 = self.variables["t"].get_value().item() | ||
t2 = other.variables["t"].get_value().item() | ||
|
||
return abs(t1 - t2) < self.epsilon | ||
pass | ||
|
||
|
||
class EventClock(BaseClock): | ||
|
@@ -171,7 +178,7 @@ def __init__(self, times, name="eventclock*"): | |
fail_for_dimension_mismatch( | ||
De-Cri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
times, | ||
second.dim, | ||
error_message="'times' must have dimensions of time, got %(dim)s", | ||
error_message="'times' must have dimensions of time", | ||
dim=times, | ||
) | ||
self._times = sorted(times) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel it is a bit confusing that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I created the times array, does it make sense to use a numpy array instead of a list there or maybe just sorting manually. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When you create the Brian variable |
||
|
@@ -187,7 +194,16 @@ def __init__(self, times, name="eventclock*"): | |
"The times provided to EventClock must not contain duplicates. " | ||
f"Duplicates found: {duplicates}" | ||
) | ||
|
||
self._times.append(np.inf * ms) | ||
self.variables.add_array( | ||
"times", | ||
dimensions=second.dim, | ||
size=len(self._times), | ||
values=self._times, | ||
dtype=np.float64, | ||
read_only=True, | ||
) | ||
self.variables["t"].set_value(self._times[0]) | ||
|
||
logger.diagnostic(f"Created event clock {self.name}") | ||
|
@@ -198,7 +214,9 @@ def advance(self): | |
""" | ||
new_ts = self.variables["timestep"].get_value().item() | ||
if self._i_end is not None and new_ts + 1 > self._i_end: | ||
return | ||
raise StopIteration( | ||
"EventClock has reached the end of its available times." | ||
) | ||
new_ts += 1 | ||
self.variables["timestep"].set_value(new_ts) | ||
self.variables["t"].set_value(self._times[new_ts]) | ||
|
@@ -246,8 +264,8 @@ def same_time(self, other): | |
""" | ||
Check if two clocks are at the same time. | ||
|
||
For comparisons with Clock objects, uses the Clock's dt and epsilon_dt. | ||
For comparisons with other EventClocks or BaseClock objects, uses the base | ||
For comparisons with `Clock` objects, uses the Clock's dt and epsilon_dt. | ||
For comparisons with other `EventClock` or `BaseClock` objects, uses the base | ||
epsilon value. | ||
|
||
Parameters | ||
|
@@ -289,11 +307,11 @@ class Clock(BaseClock): | |
|
||
Notes | ||
----- | ||
Clocks are run in the same Network.run iteration if ~Clock.t is the | ||
Clocks are run in the same `Network.run` iteration if `~Clock.t` is the | ||
same. The condition for two | ||
clocks to be considered as having the same time is | ||
`abs(t1-t2)<epsilon*abs(t1), a standard test for equality of floating | ||
point values. The value of `epsilon is 1e-14. | ||
``abs(t1-t2)<epsilon*abs(t1)``, a standard test for equality of floating | ||
point values. The value of ``epsilon`` is ``1e-14``. | ||
""" | ||
|
||
#: The relative difference for times (in terms of dt) so that they are | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
from brian2 import * | ||
from brian2.core.clocks import EventClock | ||
from brian2.tests.test_network import NameLister | ||
from brian2.units.fundamentalunits import DimensionMismatchError | ||
from brian2.utils.logger import catch_logs | ||
|
||
|
||
|
@@ -61,19 +62,31 @@ def test_set_interval_warning(): | |
|
||
@pytest.mark.codegen_independent | ||
def test_event_clock(): | ||
times = [0.0 * ms, 0.1 * ms, 0.2 * ms, 0.3 * ms] | ||
times = [0.0 * ms, 0.3 * ms, 0.5 * ms, 0.6 * ms] | ||
event_clock = EventClock(times) | ||
|
||
assert_equal(event_clock.variables["t"].get_value(), 0.0) | ||
assert_equal(event_clock[1], 0.1 * ms) | ||
for i in range(4): | ||
print(event_clock[i]) | ||
Comment on lines
+68
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. left-over print |
||
|
||
assert_equal(event_clock.variables["t"].get_value(), 0.0 * ms) | ||
assert_equal(event_clock[1], 0.3 * ms) | ||
|
||
event_clock.advance() | ||
assert_equal(event_clock.variables["timestep"].get_value(), 1) | ||
assert_equal(event_clock.variables["t"].get_value(), 0.0001) | ||
assert_equal(event_clock.variables["t"].get_value(), 0.0003) | ||
|
||
event_clock.set_interval(0.1 * ms, 0.3 * ms) | ||
event_clock.set_interval(0.3 * ms, 0.6 * ms) | ||
assert_equal(event_clock.variables["timestep"].get_value(), 1) | ||
assert_equal(event_clock.variables["t"].get_value(), 0.0001) | ||
assert_equal(event_clock.variables["t"].get_value(), 0.0003) | ||
event_clock.advance() | ||
event_clock.advance() | ||
|
||
with pytest.raises(StopIteration): | ||
event_clock.advance() | ||
|
||
invalid_times = [0.0 * volt, 0.5 * volt] | ||
with pytest.raises(DimensionMismatchError) as excinfo: | ||
EventClock(invalid_times) | ||
|
||
|
||
@pytest.mark.codegen_independent | ||
|
@@ -98,7 +111,6 @@ def test_combined_clocks_with_run_at(): | |
|
||
# Expected output: "x" at 0,1,2,3,4ms = 5 times | ||
# "y" at 0.5, 2.5, 4.0ms = 3 times | ||
# We don't care about exact timing here, just the sequence | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not mean to only remove the comment, but also to actually care about the exact output in the test below 😊 |
||
expected_x_count = 5 | ||
expected_y_count = 3 | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.