diff --git a/neet/synchronous.py b/neet/synchronous.py
index 1c6ed88..8f59454 100644
--- a/neet/synchronous.py
+++ b/neet/synchronous.py
@@ -35,7 +35,7 @@ def trajectory(net, state, timesteps=1, encode=False):
     :param state: the network state
     :param timesteps: the number of steps in the trajectory
     :param encode: encode the states as integers
-    :yields: the next state in the trajectory
+    :returns: the trajectory as a list
     :raises TypeError: if net is not a network
     :raises ValueError: if ``timesteps < 1``
     """
@@ -44,6 +44,7 @@ def trajectory(net, state, timesteps=1, encode=False):
     if timesteps < 1:
         raise ValueError("number of steps must be positive, non-zero")
 
+    traj = []
     state = copy.copy(state)
     if encode:
         if is_fixed_sized(net):
@@ -51,23 +52,24 @@ def trajectory(net, state, timesteps=1, encode=False):
         else:
             state_space = net.state_space(len(state))
 
-        yield state_space._unsafe_encode(state)
+        traj.append(state_space._unsafe_encode(state))
 
         net.update(state)
-        yield state_space._unsafe_encode(state)
+        traj.append(state_space._unsafe_encode(state))
 
         for _ in range(1,timesteps):
             net._unsafe_update(state)
-            yield state_space._unsafe_encode(state)
+            traj.append(state_space._unsafe_encode(state))
     else:
-        yield copy.copy(state)
+        traj.append(copy.copy(state))
 
         net.update(state)
-        yield copy.copy(state)
+        traj.append(copy.copy(state))
 
         for _ in range(1, timesteps):
             net._unsafe_update(state)
-            yield copy.copy(state)
+            traj.append(copy.copy(state))
+    return traj
 
 def transitions(net, size=None, encode=False):
     """
@@ -94,7 +96,7 @@ def transitions(net, size=None, encode=False):
     :param net: the network
     :param size: the size of the network (``None`` if fixed sized)
     :param encode: encode the states as integers
-    :yields: the one-state transitions
+    :returns: the one-state transitions as an array
     :raises TypeError: if ``net`` is not a network
     :raises ValueError: if ``net`` is fixed sized and ``size`` is not ``None``
     :raises ValueError: if ``net`` is not fixed sized and ``size`` is ``None``
@@ -111,12 +113,15 @@ def transitions(net, size=None, encode=False):
             raise ValueError("size must not be None for variable sized networks")
         state_space = net.state_space(size)
 
+    trans = []
     for state in state_space:
         net._unsafe_update(state)
         if encode:
-            yield state_space._unsafe_encode(state)
+            trans.append(state_space._unsafe_encode(state))
         else:
-            yield state
+            trans.append(state)
+
+    return trans
 
 def transition_graph(net, size=None):
     """
@@ -172,15 +177,14 @@ def attractors(net, size=None):
 
     :param net: the network or the transition graph
     :param size: the size of the network (``None`` if fixed sized)
-    :returns: a generator of attractors
+    :returns: a list of attractor cycles
     :raises TypeError: if ``net`` is not a network or a ``networkx.DiGraph``
     :raises ValueError: if ``net`` is fixed sized and ``size`` is not ``None``
     :raises ValueError: if ``net`` is a transition graph and ``size`` is not ``None``
     :raises ValueError: if ``net`` is not fixed sized and ``size`` is ``None``
     """
     if isinstance(net, nx.DiGraph):
-        for attr in nx.simple_cycles(net):
-            yield attr
+        return list(nx.simple_cycles(net))
     elif not is_network(net):
         raise TypeError("net must be a network or a networkx DiGraph")
     elif is_fixed_sized(net) and size is not None:
@@ -188,6 +192,7 @@ def attractors(net, size=None):
     elif not is_fixed_sized(net) and size is None:
         raise ValueError("variable sized networks require a size")
     else:
+        cycles = []
         # Get the state transitions
         # (array of next state indexed by current state)
         trans = list(transitions(net, size=size, encode=True))
@@ -259,7 +264,8 @@ def attractors(net, size=None):
 
             # Yield the cycle if we found one
             if len(cycle) != 0:
-                yield cycle
+                cycles.append(cycle)
+    return cycles
 
 def basins(net, size=None):
     """

Death to Generators
Generators are great when you may have to iterate over computations that are too large to store in memory. This is certainly a possibility for much of Neet's algorithms. It was for this reason that @dglmoore over-engineered Neet's functions to `yield` instead of `return` where possible.

While it may seem like this is a good thing, it is really just cumbersome for users, implementers and maintainers. The problem it aimed to solve is a non-issue as most of the time there are computational limits other than memory that come into play around the same time as memory issues.

We should refactor to use lists in place of generators unless it makes sense to do otherwise.
diff --git a/test/test_synchronous.py b/test/test_synchronous.py
index ec1bb85..eed14a2 100644
--- a/test/test_synchronous.py
+++ b/test/test_synchronous.py
@@ -20,13 +20,13 @@ class TestSynchronous(unittest.TestCase):
         ``trajectory`` should raise a type error if ``net`` is not a network
         """
         with self.assertRaises(TypeError):
-            list(trajectory(5, [1, 2, 3]))
+            trajectory(5, [1, 2, 3])
 
         with self.assertRaises(TypeError):
-            list(trajectory(MockObject(), [1, 2, 3]))
+            trajectory(MockObject(), [1, 2, 3])
 
         with self.assertRaises(TypeError):
-            list(trajectory(MockFixedSizedNetwork, [1, 2, 3]))
+            trajectory(MockFixedSizedNetwork, [1, 2, 3])
 
     def test_trajectory_too_short(self):
         """
@@ -34,10 +34,10 @@ class TestSynchronous(unittest.TestCase):
         than 1
         """
         with self.assertRaises(ValueError):
-            list(trajectory(MockFixedSizedNetwork(), [1, 2, 3], timesteps=0))
+            trajectory(MockFixedSizedNetwork(), [1, 2, 3], timesteps=0)
 
         with self.assertRaises(ValueError):
-            list(trajectory(MockFixedSizedNetwork(), [1, 2, 3], timesteps=-1))
+            trajectory(MockFixedSizedNetwork(), [1, 2, 3], timesteps=-1)
 
     def test_trajectory_eca(self):
         """
@@ -45,14 +45,14 @@ class TestSynchronous(unittest.TestCase):
         """
         rule30 = ECA(30)
         with self.assertRaises(ValueError):
-            list(trajectory(rule30, []))
+            trajectory(rule30, [])
 
         xs = [0, 1, 0]
-        got = list(trajectory(rule30, xs))
+        got = trajectory(rule30, xs)
         self.assertEqual([0, 1, 0], xs)
         self.assertEqual([[0, 1, 0], [1, 1, 1]], got)
 
-        got = list(trajectory(rule30, xs, timesteps=2))
+        got = trajectory(rule30, xs, timesteps=2)
         self.assertEqual([0, 1, 0], xs)
         self.assertEqual([[0, 1, 0], [1, 1, 1], [0, 0, 0]], got)
 
@@ -62,14 +62,14 @@ class TestSynchronous(unittest.TestCase):
         """
         rule30 = ECA(30)
         with self.assertRaises(ValueError):
-            list(trajectory(rule30, [], encode=True))
+            trajectory(rule30, [], encode=True)
 
         state = [0, 1, 0]
-        got = list(trajectory(rule30, state, encode=True))
+        got = trajectory(rule30, state, encode=True)
         self.assertEqual([0, 1, 0], state)
         self.assertEqual([2, 7], got)
 
-        got = list(trajectory(rule30, state, timesteps=2, encode=True))
+        got = trajectory(rule30, state, timesteps=2, encode=True)
         self.assertEqual([0, 1, 0], state)
         self.assertEqual([2, 7, 0], got)
 
@@ -84,11 +84,11 @@ class TestSynchronous(unittest.TestCase):
         )
 
         state = [0, 0]
-        got = list(trajectory(net, state))
+        got = trajectory(net, state)
         self.assertEqual([0, 0], state)
         self.assertEqual([[0, 0], [0, 1]], got)
 
-        got = list(trajectory(net, state, timesteps=3))
+        got = trajectory(net, state, timesteps=3)
         self.assertEqual([0, 0], state)
         self.assertEqual([[0, 0], [0, 1], [0, 1], [0, 1]], got)
 
@@ -103,11 +103,11 @@ class TestSynchronous(unittest.TestCase):
         )
 
         state = [0, 0]
-        got = list(trajectory(net, state, encode=True))
+        got = trajectory(net, state, encode=True)
         self.assertEqual([0, 0], state)
         self.assertEqual([0, 2], got)
 
-        got = list(trajectory(net, state, timesteps=3, encode=True))
+        got = trajectory(net, state, timesteps=3, encode=True)
         self.assertEqual([0, 0], state)
         self.assertEqual([0, 2, 2, 2], got)
 
@@ -119,7 +119,7 @@ class TestSynchronous(unittest.TestCase):
                             ((0, 2), {'01', '10', '11'}),
                             ((0, 1), {'11'})])
         state = [0, 1, 0]
-        got = list(trajectory(net, state, 3))
+        got = trajectory(net, state, 3)
         self.assertEqual([[0, 1, 0], [1, 0, 0], [0, 1, 0], [1, 0, 0]], got)
         self.assertEqual([0, 1, 0], state)
 
@@ -131,7 +131,7 @@ class TestSynchronous(unittest.TestCase):
         ``transitions`` should raise a type error if ``net`` is not a network
         """
         with self.assertRaises(TypeError):
-            list(transitions(MockObject(), 5))
+            transitions(MockObject(), 5)
 
     def test_transitions_not_fixed_sized(self):
         """
@@ -139,7 +139,7 @@ class TestSynchronous(unittest.TestCase):
         and ``size`` is ``None``
         """
         with self.assertRaises(ValueError):
-            list(transitions(ECA(30), size=None))
+            transitions(ECA(30), size=None)
 
     def test_transitions_fixed_sized(self):
         """
@@ -147,7 +147,7 @@ class TestSynchronous(unittest.TestCase):
         ``size`` is not ``None``
         """
         with self.assertRaises(ValueError):
-            list(transitions(MockFixedSizedNetwork, size=3))
+            transitions(MockFixedSizedNetwork, size=3)
 
     def test_transitions_eca(self):
         """
@@ -155,13 +155,13 @@ class TestSynchronous(unittest.TestCase):
         """
         rule30 = ECA(30)
 
-        got = list(transitions(rule30, size=1))
+        got = transitions(rule30, size=1)
         self.assertEqual([[0], [0]], got)
 
-        got = list(transitions(rule30, size=2))
+        got = transitions(rule30, size=2)
         self.assertEqual([[0, 0], [1, 0], [0, 1], [0, 0]], got)
 
-        got = list(transitions(rule30, size=3))
+        got = transitions(rule30, size=3)
         self.assertEqual([[0, 0, 0], [1, 1, 1], [1, 1, 1], [1, 0, 0],
                           [1, 1, 1], [0, 0, 1], [0, 1, 0], [0, 0, 0]], got)
 
@@ -171,13 +171,13 @@ class TestSynchronous(unittest.TestCase):
         """
         rule30 = ECA(30)
 
-        got = list(transitions(rule30, size=1, encode=True))
+        got = transitions(rule30, size=1, encode=True)
         self.assertEqual([0, 0], got)
 
-        got = list(transitions(rule30, size=2, encode=True))
+        got = transitions(rule30, size=2, encode=True)
         self.assertEqual([0, 1, 2, 0], got)
 
-        got = list(transitions(rule30, size=3, encode=True))
+        got = transitions(rule30, size=3, encode=True)
         self.assertEqual([0, 7, 7, 1, 7, 4, 2, 0], got)
 
     def test_transitions_wtnetwork(self):
@@ -190,7 +190,7 @@ class TestSynchronous(unittest.TestCase):
             theta=WTNetwork.positive_threshold
         )
 
-        got = list(transitions(net))
+        got = transitions(net)
         self.assertEqual([[0, 1], [1, 0], [0, 1], [1, 1]], got)
 
     def test_transitions_wtnetwork_encoded(self):
@@ -203,7 +203,7 @@ class TestSynchronous(unittest.TestCase):
             theta=WTNetwork.positive_threshold
         )
 
-        got = list(transitions(net, encode=True))
+        got = transitions(net, encode=True)
         self.assertEqual([2, 1, 2, 3], got)
 
     def test_transitions_logicnetwork(self):
@@ -211,7 +211,7 @@ class TestSynchronous(unittest.TestCase):
         test `transitions` on `LogicNetwork`s
         """
         net = LogicNetwork([((1,), {'0', '1'}), ((0,), {'1'})])
-        got = list(transitions(net))
+        got = transitions(net)
         self.assertEqual([[1, 0], [1, 1], [1, 0], [1, 1]], got)
 
     def test_transitions_logicnetwork_encoded(self):
@@ -219,7 +219,7 @@ class TestSynchronous(unittest.TestCase):
         test `transitions` on `LogicNetwork`s, states encoded
         """
         net = LogicNetwork([((1,), {'0', '1'}), ((0,), {'1'})])
-        got = list(transitions(net, encode=True))
+        got = transitions(net, encode=True)
         self.assertEqual([1, 3, 1, 3], got)
 
     def test_transition_graph_not_network(self):
@@ -268,13 +268,13 @@ class TestSynchronous(unittest.TestCase):
         nor a networkx digraph
         """
         with self.assertRaises(TypeError):
-            list(attractors('blah'))
+            attractors('blah')
 
         with self.assertRaises(TypeError):
-            list(attractors(MockObject()))
+            attractors(MockObject())
 
         with self.assertRaises(TypeError):
-            list(attractors(nx.Graph()))
+            attractors(nx.Graph())
 
     def test_attractors_variable_sized(self):
         """
@@ -282,7 +282,7 @@ class TestSynchronous(unittest.TestCase):
         network and ``size`` is ``None``
         """
         with self.assertRaises(ValueError):
-            list(attractors(ECA(30), size=None))
+            attractors(ECA(30), size=None)
 
     def test_attractors_fixed_sized(self):
         """
@@ -290,10 +290,10 @@ class TestSynchronous(unittest.TestCase):
         network or a networkx digraph, and ``size`` is not ``None``
         """
         with self.assertRaises(ValueError):
-            list(attractors(MockFixedSizedNetwork(), size=5))
+            attractors(MockFixedSizedNetwork(), size=5)
 
         #  with self.assertRaises(ValueError):
-        #      list(attractors(nx.DiGraph(), size=5))
+        #      attractors(nx.DiGraph(), size=5)
 
     def test_attractors_eca(self):
         """
@@ -304,7 +304,7 @@ class TestSynchronous(unittest.TestCase):
                     (ECA(110), 3, 1), (ECA(110), 4, 3), (ECA(110), 5, 1),
                     (ECA(110), 6, 3)]
         for rule, width, size in networks:
-            self.assertEqual(size, len(list(attractors(rule, width))))
+            self.assertEqual(size, len(attractors(rule, width)))
 
     def test_attractors_wtnetworks(self):
         """
@@ -312,14 +312,14 @@ class TestSynchronous(unittest.TestCase):
         """
         networks = [(s_pombe, 13), (s_cerevisiae, 7), (c_elegans, 5)]
         for net, size in networks:
-            self.assertEqual(size, len(list(attractors(net))))
+            self.assertEqual(size, len(attractors(net)))
 
     def test_attractors_transition_graph(self):
         """
         test ``attractors`` on ``s_pombe`` transition graph
         """
-        att_from_graph = list(attractors(transition_graph(s_pombe)))
-        att_from_network = list(attractors(s_pombe))
+        att_from_graph = attractors(transition_graph(s_pombe))
+        att_from_network = attractors(s_pombe)
 
         for (a, b) in zip(att_from_graph, att_from_network):
             a.sort()

pip install -e .
parse_log_pytest
requirements.txt
nose pytest
apt-get update apt-get install -y gcc
3.5
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
decorator==4.4.2
importlib-metadata==4.8.3
iniconfig==1.1.1
-e git+https://github.com/ELIFE-ASU/Neet.git@5a45a92c1b3a564290487ecc1483b4866e26eaf5#egg=neet
networkx==2.5.1
nose==1.3.7
numpy==1.19.5
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyinform==0.2.0
pyparsing==3.1.4
pytest==7.0.1
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: Neet
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - decorator==4.4.2
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - networkx==2.5.1
      - nose==1.3.7
      - numpy==1.19.5
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyinform==0.2.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/Neet

test/test_synchronous.py::TestSynchronous::test_attractors_eca test/test_synchronous.py::TestSynchronous::test_attractors_fixed_sized test/test_synchronous.py::TestSynchronous::test_attractors_invalid_net test/test_synchronous.py::TestSynchronous::test_attractors_transition_graph test/test_synchronous.py::TestSynchronous::test_attractors_variable_sized test/test_synchronous.py::TestSynchronous::test_attractors_wtnetworks test/test_synchronous.py::TestSynchronous::test_trajectory_eca test/test_synchronous.py::TestSynchronous::test_trajectory_eca_encoded test/test_synchronous.py::TestSynchronous::test_trajectory_logicnetwork test/test_synchronous.py::TestSynchronous::test_trajectory_not_network test/test_synchronous.py::TestSynchronous::test_trajectory_too_short test/test_synchronous.py::TestSynchronous::test_trajectory_wtnetwork test/test_synchronous.py::TestSynchronous::test_trajectory_wtnetwork_encoded test/test_synchronous.py::TestSynchronous::test_transitions_eca test/test_synchronous.py::TestSynchronous::test_transitions_eca_encoded test/test_synchronous.py::TestSynchronous::test_transitions_fixed_sized test/test_synchronous.py::TestSynchronous::test_transitions_logicnetwork test/test_synchronous.py::TestSynchronous::test_transitions_logicnetwork_encoded test/test_synchronous.py::TestSynchronous::test_transitions_not_fixed_sized test/test_synchronous.py::TestSynchronous::test_transitions_not_network test/test_synchronous.py::TestSynchronous::test_transitions_wtnetwork test/test_synchronous.py::TestSynchronous::test_transitions_wtnetwork_encoded
test/test_synchronous.py::TestSynchronous::test_basin_entropy_eca test/test_synchronous.py::TestSynchronous::test_basin_entropy_transition_graph test/test_synchronous.py::TestSynchronous::test_basin_entropy_wtnetwork test/test_synchronous.py::TestSynchronous::test_basin_entropy_wtnetwork_base10 test/test_synchronous.py::TestSynchronous::test_basins_eca test/test_synchronous.py::TestSynchronous::test_basins_transition_graph test/test_synchronous.py::TestSynchronous::test_basins_wtnetwork
test/test_synchronous.py::TestSynchronous::test_basin_entropy_fixed_sized test/test_synchronous.py::TestSynchronous::test_basin_entropy_invalid_net test/test_synchronous.py::TestSynchronous::test_basin_entropy_variable_sized test/test_synchronous.py::TestSynchronous::test_basins_fixed_sized test/test_synchronous.py::TestSynchronous::test_basins_invalid_net test/test_synchronous.py::TestSynchronous::test_basins_variable_sized test/test_synchronous.py::TestSynchronous::test_timeseries_eca test/test_synchronous.py::TestSynchronous::test_timeseries_fixed_sized test/test_synchronous.py::TestSynchronous::test_timeseries_not_network test/test_synchronous.py::TestSynchronous::test_timeseries_too_short test/test_synchronous.py::TestSynchronous::test_timeseries_variable_sized test/test_synchronous.py::TestSynchronous::test_timeseries_wtnetworks test/test_synchronous.py::TestSynchronous::test_transition_graph_eca test/test_synchronous.py::TestSynchronous::test_transition_graph_fixed_sized test/test_synchronous.py::TestSynchronous::test_transition_graph_not_network test/test_synchronous.py::TestSynchronous::test_transition_graph_s_pombe test/test_synchronous.py::TestSynchronous::test_transition_graph_variable_sized test/test_synchronous.py::TestLandscape::test_attractor_lengths test/test_synchronous.py::TestLandscape::test_attractors_eca test/test_synchronous.py::TestLandscape::test_attractors_wtnetworks test/test_synchronous.py::TestLandscape::test_basin_entropy_eca test/test_synchronous.py::TestLandscape::test_basin_entropy_wtnetwork test/test_synchronous.py::TestLandscape::test_basin_sizes test/test_synchronous.py::TestLandscape::test_basins_eca test/test_synchronous.py::TestLandscape::test_canary test/test_synchronous.py::TestLandscape::test_graph_eca test/test_synchronous.py::TestLandscape::test_graph_wtnetworks test/test_synchronous.py::TestLandscape::test_heights test/test_synchronous.py::TestLandscape::test_in_degree test/test_synchronous.py::TestLandscape::test_init_fixed_sized test/test_synchronous.py::TestLandscape::test_init_not_fixed_sized test/test_synchronous.py::TestLandscape::test_init_not_network test/test_synchronous.py::TestLandscape::test_is_state_space test/test_synchronous.py::TestLandscape::test_recurrence_times test/test_synchronous.py::TestLandscape::test_timeseries_eca test/test_synchronous.py::TestLandscape::test_timeseries_too_short test/test_synchronous.py::TestLandscape::test_timeseries_wtnetworks test/test_synchronous.py::TestLandscape::test_trajectory_eca test/test_synchronous.py::TestLandscape::test_trajectory_logicnetwork test/test_synchronous.py::TestLandscape::test_trajectory_too_short test/test_synchronous.py::TestLandscape::test_trajectory_wtnetwork test/test_synchronous.py::TestLandscape::test_transitions_eca test/test_synchronous.py::TestLandscape::test_transitions_logicnetwork test/test_synchronous.py::TestLandscape::test_transitions_spombe test/test_synchronous.py::TestLandscape::test_transitions_wtnetwork
neet/synchronous.py
neet/synchronous.py