Repository: projectmesa/mesa
Pull request #1688 (2023-05-09 12:44:56)

breaking: space: Remove deprecations (find_empty, num_agents, position_agent)

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/projectmesa/mesa/pull/1688?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) Report
Patch coverage has no change and project coverage change: **`-1.17`** :warning:
> Comparison is base [(`000408d`)](https://app.codecov.io/gh/projectmesa/mesa/commit/000408ddc9fe344d81ed712032a8653f175bb77b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) 81.56% compared to head [(`20ef160`)](https://app.codecov.io/gh/projectmesa/mesa/pull/1688?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) 80.39%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1688      +/-   ##
==========================================
- Coverage   81.56%   80.39%   -1.17%     
==========================================
  Files          18       18              
  Lines        1410     1229     -181     
  Branches      273      203      -70     
==========================================
- Hits         1150      988     -162     
+ Misses        214      204      -10     
+ Partials       46       37       -9     
```


| [Impacted Files](https://app.codecov.io/gh/projectmesa/mesa/pull/1688?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) | Coverage Δ | |
|---|---|---|
| [mesa/space.py](https://app.codecov.io/gh/projectmesa/mesa/pull/1688?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa#diff-bWVzYS9zcGFjZS5weQ==) | `91.33% <ø> (+0.94%)` | :arrow_up: |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/projectmesa/mesa/pull/1688/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/projectmesa/mesa/pull/1688?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa).

rht: I have fixed the CI errors.

Patch:
diff --git a/docs/best-practices.rst b/docs/best-practices.rst
index 4519d688..a73c125f 100644
--- a/docs/best-practices.rst
+++ b/docs/best-practices.rst
@@ -27,7 +27,7 @@ organize them. For example, if the visualization uses image files, put those in
 an ``images`` directory.
 
 The `Schelling
-<https://github.com/projectmesa/mesa-examples/tree/main/examples/Schelling>`_ model is
+<https://github.com/projectmesa/mesa-examples/tree/main/examples/schelling>`_ model is
 a good example of a small well-packaged model.
 
 It's easy to create a cookiecutter mesa model by running ``mesa startproject``
diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb
index 8f053d17..fe76ea03 100644
--- a/docs/tutorials/intro_tutorial.ipynb
+++ b/docs/tutorials/intro_tutorial.ipynb
@@ -53,7 +53,7 @@
     "Install Mesa:\n",
     "\n",
     "```bash\n",
-    "pip install mesa\n",
+    "pip install --upgrade mesa\n",
     "```\n",
     "\n",
     "Install Jupyter Notebook (optional):\n",
@@ -130,8 +130,8 @@
    "source": [
     "import mesa\n",
     "\n",
-    "# Data visualization tool.\n",
-    "import matplotlib.pyplot as plt\n",
+    "# Data visualization tools.\n",
+    "import seaborn as sns\n",
     "\n",
     "# Has multi-dimensional arrays and matrices. Has a large collection of\n",
     "# mathematical functions to operate on these arrays.\n",
@@ -509,6 +509,7 @@
     "If you are running from a text editor or IDE, you'll also need to add this line, to make the graph appear.\n",
     "\n",
     "```python\n",
+    "import matplotlib.pyplot as plt\n",
     "plt.show()\n",
     "```"
    ]
@@ -531,7 +532,11 @@
     "import matplotlib.pyplot as plt\n",
     "\n",
     "agent_wealth = [a.wealth for a in model.schedule.agents]\n",
-    "plt.hist(agent_wealth)"
+    "# Create a histogram with seaborn\n",
+    "g = sns.histplot(agent_wealth, discrete=True)\n",
+    "g.set(\n",
+    "    title=\"Wealth distribution\", xlabel=\"Wealth\", ylabel=\"Number of agents\"\n",
+    ");  # The semicolon is just to avoid printing the object representation"
    ]
   },
   {
@@ -571,7 +576,9 @@
     "    for agent in model.schedule.agents:\n",
     "        all_wealth.append(agent.wealth)\n",
     "\n",
-    "plt.hist(all_wealth, bins=range(max(all_wealth) + 1))"
+    "# Use seaborn\n",
+    "g = sns.histplot(all_wealth, discrete=True)\n",
+    "g.set(title=\"Wealth distribution\", xlabel=\"Wealth\", ylabel=\"Number of agents\");"
    ]
   },
   {
@@ -758,7 +765,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Let's create a model with 50 agents on a 10x10 grid, and run it for 20 steps."
+    "Let's create a model with 100 agents on a 10x10 grid, and run it for 20 steps."
    ]
   },
   {
@@ -772,7 +779,7 @@
    },
    "outputs": [],
    "source": [
-    "model = MoneyModel(50, 10, 10)\n",
+    "model = MoneyModel(100, 10, 10)\n",
     "for i in range(20):\n",
     "    model.step()"
    ]
@@ -802,11 +809,10 @@
     "    cell_content, x, y = cell\n",
     "    agent_count = len(cell_content)\n",
     "    agent_counts[x][y] = agent_count\n",
-    "plt.imshow(agent_counts, interpolation=\"nearest\")\n",
-    "plt.colorbar()\n",
-    "\n",
-    "# If running from a text editor or IDE, remember you'll need the following:\n",
-    "# plt.show()"
+    "# Plot using seaborn, with a size of 5x5\n",
+    "g = sns.heatmap(agent_counts, cmap=\"viridis\", annot=True, cbar=False, square=True)\n",
+    "g.figure.set_size_inches(4, 4)\n",
+    "g.set(title=\"Number of agents on each cell of the grid\");"
    ]
   },
   {
@@ -923,7 +929,7 @@
    },
    "outputs": [],
    "source": [
-    "model = MoneyModel(50, 10, 10)\n",
+    "model = MoneyModel(100, 10, 10)\n",
     "for i in range(100):\n",
     "    model.step()"
    ]
@@ -947,7 +953,9 @@
    "outputs": [],
    "source": [
     "gini = model.datacollector.get_model_vars_dataframe()\n",
-    "gini.plot()"
+    "# Plot the Gini coefficient over time\n",
+    "g = sns.lineplot(data=gini)\n",
+    "g.set(title=\"Gini Coefficient over Time\", ylabel=\"Gini Coefficient\");"
    ]
   },
   {
@@ -976,7 +984,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "You'll see that the DataFrame's index is pairings of model step and agent ID. You can analyze it the way you would any other DataFrame. For example, to get a histogram of agent wealth at the model's end:"
+    "You'll see that the DataFrame's index is pairings of model step and agent ID. This is because the data collector stores the data in a dictionary, with the step number as the key, and a dictionary of agent ID and variable value pairs as the value. The data collector then converts this dictionary into a DataFrame, which is why the index is a pair of (model step, agent ID). You can analyze it the way you would any other DataFrame. For example, to get a histogram of agent wealth at the model's end:"
    ]
   },
   {
@@ -990,8 +998,15 @@
    },
    "outputs": [],
    "source": [
-    "end_wealth = agent_wealth.xs(99, level=\"Step\")[\"Wealth\"]\n",
-    "end_wealth.hist(bins=range(agent_wealth.Wealth.max() + 1))"
+    "last_step = agent_wealth.index.get_level_values(\"Step\").max()\n",
+    "end_wealth = agent_wealth.xs(last_step, level=\"Step\")[\"Wealth\"]\n",
+    "# Create a histogram of wealth at the last step\n",
+    "g = sns.histplot(end_wealth, discrete=True)\n",
+    "g.set(\n",
+    "    title=\"Distribution of wealth at the end of simulation\",\n",
+    "    xlabel=\"Wealth\",\n",
+    "    ylabel=\"Number of agents\",\n",
+    ");"
    ]
   },
   {
@@ -1012,8 +1027,12 @@
    },
    "outputs": [],
    "source": [
+    "# Get the wealth of agent 14 over time\n",
     "one_agent_wealth = agent_wealth.xs(14, level=\"AgentID\")\n",
-    "one_agent_wealth.Wealth.plot()"
+    "\n",
+    "# Plot the wealth of agent 14 over time\n",
+    "g = sns.lineplot(data=one_agent_wealth, x=\"Step\", y=\"Wealth\")\n",
+    "g.set(title=\"Wealth of agent 14 over time\");"
    ]
   },
   {
@@ -1235,10 +1254,17 @@
    },
    "outputs": [],
    "source": [
+    "# Filter the results to only contain the data of one agent (the Gini coefficient will be the same for the entire population at any time) at the 100th step of each episode\n",
     "results_filtered = results_df[(results_df.AgentID == 0) & (results_df.Step == 100)]\n",
-    "N_values = results_filtered.N.values\n",
-    "gini_values = results_filtered.Gini.values\n",
-    "plt.scatter(N_values, gini_values)"
+    "results_filtered[[\"iteration\", \"N\", \"Gini\"]].reset_index(\n",
+    "    drop=True\n",
+    ").head()  # Create a scatter plot\n",
+    "g = sns.scatterplot(data=results_filtered, x=\"N\", y=\"Gini\")\n",
+    "g.set(\n",
+    "    xlabel=\"Number of agents\",\n",
+    "    ylabel=\"Gini coefficient\",\n",
+    "    title=\"Gini coefficient vs. number of agents\",\n",
+    ");"
    ]
   },
   {
diff --git a/mesa/space.py b/mesa/space.py
index 0820aee3..96e1a4da 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -454,16 +454,8 @@ class _Grid:
         x, y = pos
         return self._grid[x][y] == self.default_val()
 
-    def move_to_empty(self, agent: Agent, num_agents: int | None = None) -> None:
+    def move_to_empty(self, agent: Agent) -> None:
         """Moves agent to a random empty cell, vacating agent's old cell."""
-        if num_agents is not None:
-            warn(
-                (
-                    "`num_agents` is being deprecated since it's no longer used "
-                    "inside `move_to_empty`. It shouldn't be passed as a parameter."
-                ),
-                DeprecationWarning,
-            )
         num_empty_cells = len(self.empties)
         if num_empty_cells == 0:
             raise Exception("ERROR: No empty cells")
@@ -486,26 +478,6 @@ class _Grid:
         self.remove_agent(agent)
         self.place_agent(agent, new_pos)
 
-    def find_empty(self) -> Coordinate | None:
-        """Pick a random empty cell."""
-        import random
-
-        warn(
-            (
-                "`find_empty` is being phased out since it uses the global "
-                "`random` instead of the model-level random-number generator. "
-                "Consider replacing it with having a model or agent object "
-                "explicitly pick one of the grid's list of empty cells."
-            ),
-            DeprecationWarning,
-        )
-
-        if self.exists_empty_cells():
-            pos = random.choice(sorted(self.empties))
-            return pos
-        else:
-            return None
-
     def exists_empty_cells(self) -> bool:
         """Return True if any cells empty else False."""
         return len(self.empties) > 0
@@ -523,42 +495,6 @@ class SingleGrid(_Grid):
         torus: Boolean which determines whether to treat the grid as a torus.
     """
 
-    def position_agent(
-        self, agent: Agent, x: int | str = "random", y: int | str = "random"
-    ) -> None:
-        """Position an agent on the grid.
-        This is used when first placing agents! Setting either x or y to "random"
-        gives the same behavior as 'move_to_empty()' to get a random position.
-        If x or y are positive, they are used.
-        Use 'swap_pos()' to swap agents positions.
-        """
-        warn(
-            (
-                "`position_agent` is being deprecated; use instead "
-                "`place_agent` to place an agent at a specified "
-                "location or `move_to_empty` to place an agent "
-                "at a random empty cell."
-            ),
-            DeprecationWarning,
-        )
-
-        if not (isinstance(x, int) or x == "random"):
-            raise Exception(
-                "x must be an integer or a string 'random'."
-                f" Actual type: {type(x)}. Actual value: {x}."
-            )
-        if not (isinstance(y, int) or y == "random"):
-            raise Exception(
-                "y must be an integer or a string 'random'."
-                f" Actual type: {type(y)}. Actual value: {y}."
-            )
-
-        if x == "random" or y == "random":
-            self.move_to_empty(agent)
-        else:
-            coords = (x, y)
-            self.place_agent(agent, coords)
-
     def place_agent(self, agent: Agent, pos: Coordinate) -> None:
         """Place the agent at the specified location, and set its pos variable."""
         if self.is_cell_empty(pos):
@@ -1069,22 +1005,27 @@ class NetworkGrid:
         self.G.nodes[node_id]["agent"].append(agent)
         agent.pos = node_id
 
-    def get_neighbors(
+    def get_neighborhood(
         self, node_id: int, include_center: bool = False, radius: int = 1
     ) -> list[int]:
         """Get all adjacent nodes within a certain radius"""
         if radius == 1:
-            neighbors = list(self.G.neighbors(node_id))
+            neighborhood = list(self.G.neighbors(node_id))
             if include_center:
-                neighbors.append(node_id)
+                neighborhood.append(node_id)
         else:
             neighbors_with_distance = nx.single_source_shortest_path_length(
                 self.G, node_id, radius
             )
             if not include_center:
                 del neighbors_with_distance[node_id]
-            neighbors = sorted(neighbors_with_distance.keys())
-        return neighbors
+            neighborhood = sorted(neighbors_with_distance.keys())
+        return neighborhood
+
+    def get_neighbors(self, node_id: int, include_center: bool = False) -> list[Agent]:
+        """Get all agents in adjacent nodes."""
+        neighborhood = self.get_neighborhood(node_id, include_center)
+        return self.get_cell_list_contents(neighborhood)
 
     def move_agent(self, agent: Agent, node_id: int) -> None:
         """Move an agent from its current node to a new node."""
diff --git a/mesa/visualization/ModularVisualization.py b/mesa/visualization/ModularVisualization.py
index 6e594f79..2df25938 100644
--- a/mesa/visualization/ModularVisualization.py
+++ b/mesa/visualization/ModularVisualization.py
@@ -106,7 +106,7 @@ import tornado.ioloop
 import tornado.web
 import tornado.websocket
 
-from mesa.visualization.UserParam import UserParam, UserSettableParameter
+from mesa.visualization.UserParam import UserParam
 
 # Suppress several pylint warnings for this file.
 # Attributes being defined outside of init is a Tornado feature.
@@ -121,9 +121,7 @@ CHART_JS_FILE = "external/chart-3.6.1.min.js"
 
 
 def is_user_param(val):
-    return isinstance(val, UserSettableParameter) or issubclass(
-        val.__class__, UserParam
-    )
+    return issubclass(val.__class__, UserParam)
 
 
 class VisualizationElement:
diff --git a/mesa/visualization/UserParam.py b/mesa/visualization/UserParam.py
index 7fd2103b..d97d196c 100644
--- a/mesa/visualization/UserParam.py
+++ b/mesa/visualization/UserParam.py
@@ -1,5 +1,4 @@
 import numbers
-from warnings import warn
 
 NUMBER = "number"
 CHECKBOX = "checkbox"
@@ -8,134 +7,6 @@ SLIDER = "slider"
 STATIC_TEXT = "static_text"
 
 
-class UserSettableParameter:
-    """A class for providing options to a visualization for a given parameter.
-
-    UserSettableParameter can be used instead of keyword arguments when specifying model parameters in an
-    instance of a `ModularServer` so that the parameter can be adjusted in the UI without restarting the server.
-
-    Validation of correctly-specified params happens on startup of a `ModularServer`. Each param is handled
-    individually in the UI and sends callback events to the server when an option is updated. That option is then
-    re-validated, in the `value.setter` property method to ensure input is correct from the UI to `reset_model`
-    callback.
-
-    Parameter types include:
-        - 'number' - a simple numerical input
-        - 'checkbox' - boolean checkbox
-        - 'choice' - String-based dropdown input, for selecting choices within a model
-        - 'slider' - A number-based slider input with settable increment
-        - 'static_text' - A non-input textbox for displaying model info.
-
-    Examples:
-
-    # Simple number input
-    number_option = UserSettableParameter('number', 'My Number', value=123)
-
-    # Checkbox input
-    boolean_option = UserSettableParameter('checkbox', 'My Boolean', value=True)
-
-    # Choice input
-    choice_option = UserSettableParameter('choice', 'My Choice', value='Default choice',
-                                          choices=['Default Choice', 'Alternate Choice'])
-
-    # Slider input
-    slider_option = UserSettableParameter('slider', 'My Slider', value=123, min_value=10, max_value=200, step=0.1)
-
-    # Static text
-    static_text = UserSettableParameter('static_text', value="This is a descriptive textbox")
-    """
-
-    NUMBER = NUMBER
-    CHECKBOX = CHECKBOX
-    CHOICE = CHOICE
-    SLIDER = SLIDER
-    STATIC_TEXT = STATIC_TEXT
-
-    TYPES = (NUMBER, CHECKBOX, CHOICE, SLIDER, STATIC_TEXT)
-
-    _ERROR_MESSAGE = "Missing or malformed inputs for '{}' Option '{}'"
-
-    def __init__(
-        self,
-        param_type=None,
-        name="",
-        value=None,
-        min_value=None,
-        max_value=None,
-        step=1,
-        choices=None,
-        description=None,
-    ):
-        warn(
-            "UserSettableParameter is deprecated in favor of UserParam objects "
-            "such as Slider, Checkbox, Choice, StaticText, NumberInput. "
-            "See the examples folder for how to use them. "
-            "UserSettableParameter will be removed in the next major release."
-        )
-        if choices is None:
-            choices = []
-        if param_type not in self.TYPES:
-            raise ValueError(f"{param_type} is not a valid Option type")
-        self.param_type = param_type
-        self.name = name
-        self._value = value
-        self.min_value = min_value
-        self.max_value = max_value
-        self.step = step
-        self.choices = choices
-        self.description = description
-
-        # Validate option types to make sure values are supplied properly
-        msg = self._ERROR_MESSAGE.format(self.param_type, name)
-        valid = True
-
-        if self.param_type == self.NUMBER:
-            valid = self.value is not None
-
-        elif self.param_type == self.SLIDER:
-            valid = not (
-                self.value is None or self.min_value is None or self.max_value is None
-            )
-
-        elif self.param_type == self.CHOICE:
-            valid = not (self.value is None or len(self.choices) == 0)
-
-        elif self.param_type == self.CHECKBOX:
-            valid = isinstance(self.value, bool)
-
-        elif self.param_type == self.STATIC_TEXT:
-            valid = isinstance(self.value, str)
-
-        if not valid:
-            raise ValueError(msg)
-
-    @property
-    def value(self):
-        return self._value
-
-    @value.setter
-    def value(self, value):
-        self._value = value
-        if self.param_type == self.SLIDER:
-            if self._value < self.min_value:
-                self._value = self.min_value
-            elif self._value > self.max_value:
-                self._value = self.max_value
-        elif (self.param_type == self.CHOICE) and self._value not in self.choices:
-            print(
-                "Selected choice value not in available choices, selected first choice from 'choices' list"
-            )
-            self._value = self.choices[0]
-
-    @property
-    def json(self):
-        result = self.__dict__.copy()
-        result["value"] = result.pop(
-            "_value"
-        )  # Return _value as value, value is the same
-        return result
-
-
 class UserParam:
     _ERROR_MESSAGE = "Missing or malformed inputs for '{}' Option '{}'"
 
diff --git a/setup.py b/setup.py
index 00e88cbe..7eff8bb3 100644
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,14 @@ extras_require = {
     # Explicitly install ipykernel for Python 3.8.
     # See https://stackoverflow.com/questions/28831854/how-do-i-add-python3-kernel-to-jupyter-ipython
     # Could be removed in the future
-    "docs": ["sphinx<7", "ipython", "nbsphinx", "ipykernel", "pydata_sphinx_theme"],
+    "docs": [
+        "sphinx<7",
+        "ipython",
+        "nbsphinx",
+        "ipykernel",
+        "pydata_sphinx_theme",
+        "seaborn",
+    ],
 }
 
 version = ""


Test patch:
diff --git a/tests/test_grid.py b/tests/test_grid.py
index a24db60a..d263c90b 100644
--- a/tests/test_grid.py
+++ b/tests/test_grid.py
@@ -265,25 +265,6 @@ class TestSingleGridEnforcement(unittest.TestCase):
                 self.grid.place_agent(a, (x, y))
         self.num_agents = len(self.agents)
 
-    @patch.object(MockAgent, "model", create=True)
-    def test_position_agent(self, mock_model):
-        a = MockAgent(100, None)
-        with self.assertRaises(Exception) as exc_info:
-            self.grid.position_agent(a, (1, 1))
-        expected = (
-            "x must be an integer or a string 'random'."
-            " Actual type: <class 'tuple'>. Actual value: (1, 1)."
-        )
-        assert str(exc_info.exception) == expected
-        with self.assertRaises(Exception) as exc_info:
-            self.grid.position_agent(a, "(1, 1)")
-        expected = (
-            "x must be an integer or a string 'random'."
-            " Actual type: <class 'str'>. Actual value: (1, 1)."
-        )
-        assert str(exc_info.exception) == expected
-        self.grid.position_agent(a, "random")
-
     @patch.object(MockAgent, "model", create=True)
     def test_enforcement(self, mock_model):
         """
@@ -297,28 +278,28 @@ class TestSingleGridEnforcement(unittest.TestCase):
 
         # Place the agent in an empty cell
         mock_model.schedule.get_agent_count = Mock(side_effect=lambda: len(self.agents))
-        self.grid.position_agent(a)
+        self.grid.move_to_empty(a)
         self.num_agents += 1
         # Test whether after placing, the empty cells are reduced by 1
         assert a.pos not in self.grid.empties
         assert len(self.grid.empties) == 8
         for _i in range(10):
-            self.grid.move_to_empty(a, num_agents=self.num_agents)
+            self.grid.move_to_empty(a)
         assert len(self.grid.empties) == 8
 
         # Place agents until the grid is full
         empty_cells = len(self.grid.empties)
         for i in range(empty_cells):
             a = MockAgent(101 + i, None)
-            self.grid.position_agent(a)
+            self.grid.move_to_empty(a)
             self.num_agents += 1
         assert len(self.grid.empties) == 0
 
         a = MockAgent(110, None)
         with self.assertRaises(Exception):
-            self.grid.position_agent(a)
+            self.grid.move_to_empty(a)
         with self.assertRaises(Exception):
-            self.move_to_empty(self.agents[0], num_agents=self.num_agents)
+            self.move_to_empty(self.agents[0])
 
 
 # Number of agents at each position for testing
diff --git a/tests/test_space.py b/tests/test_space.py
index a7aba252..8691dc45 100644
--- a/tests/test_space.py
+++ b/tests/test_space.py
@@ -306,11 +306,9 @@ class TestSingleGrid(unittest.TestCase):
 
     def test_empty_cells(self):
         if self.space.exists_empty_cells():
-            pytest.deprecated_call(self.space.find_empty)
             for i, pos in enumerate(list(self.space.empties)):
                 a = MockAgent(-i, pos)
-                self.space.position_agent(a, x=pos[0], y=pos[1])
-        assert self.space.find_empty() is None
+                self.space.place_agent(a, pos)
         with self.assertRaises(Exception):
             self.space.move_to_empty(a)
 
@@ -354,10 +352,10 @@ class TestSingleNetworkGrid(unittest.TestCase):
             assert a.pos == pos
 
     def test_get_neighbors(self):
-        assert len(self.space.get_neighbors(0, include_center=True)) == 3
-        assert len(self.space.get_neighbors(0, include_center=False)) == 2
-        assert len(self.space.get_neighbors(2, include_center=True, radius=3)) == 7
-        assert len(self.space.get_neighbors(2, include_center=False, radius=3)) == 6
+        assert len(self.space.get_neighborhood(0, include_center=True)) == 3
+        assert len(self.space.get_neighborhood(0, include_center=False)) == 2
+        assert len(self.space.get_neighborhood(2, include_center=True, radius=3)) == 7
+        assert len(self.space.get_neighborhood(2, include_center=False, radius=3)) == 6
 
     def test_move_agent(self):
         initial_pos = 1
@@ -426,11 +424,11 @@ class TestMultipleNetworkGrid(unittest.TestCase):
 
     def test_get_neighbors(self):
         assert (
-            len(self.space.get_neighbors(0, include_center=True))
+            len(self.space.get_neighborhood(0, include_center=True))
             == TestMultipleNetworkGrid.GRAPH_SIZE
         )
         assert (
-            len(self.space.get_neighbors(0, include_center=False))
+            len(self.space.get_neighborhood(0, include_center=False))
             == TestMultipleNetworkGrid.GRAPH_SIZE - 1
         )
 
diff --git a/tests/test_usersettableparam.py b/tests/test_usersettableparam.py
index 9106d050..8f02fa4b 100644
--- a/tests/test_usersettableparam.py
+++ b/tests/test_usersettableparam.py
@@ -6,61 +6,50 @@ from mesa.visualization.UserParam import (
     NumberInput,
     Slider,
     StaticText,
-    UserSettableParameter,
 )
 
 
 class TestOption(TestCase):
     def setUp(self):
-        self.number_option = UserSettableParameter("number", value=123)
-        self.number_option_standalone = NumberInput("number", value=123)
-        self.checkbox_option = UserSettableParameter("checkbox", value=True)
-        self.checkbox_option_standalone = Checkbox(value=True)
-        self.choice_option = UserSettableParameter(
-            "choice",
+        self.number_option = NumberInput("number", value=123)
+        self.checkbox_option = Checkbox(value=True)
+        self.choice_option = Choice(
             value="I am your default choice",
             choices=["I am your default choice", "I am your other choice"],
         )
-        self.choice_option_standalone = Choice(
-            value="I am your default choice",
-            choices=["I am your default choice", "I am your other choice"],
-        )
-        self.slider_option = UserSettableParameter(
-            "slider", value=123, min_value=100, max_value=200
-        )
-        self.slider_option_standalone = Slider(value=123, min_value=100, max_value=200)
+        self.slider_option = Slider(value=123, min_value=100, max_value=200)
         self.static_text_option = StaticText("Hurr, Durr Im'a Sheep")
 
     def test_number(self):
-        for option in [self.number_option, self.number_option_standalone]:
-            assert option.value == 123
-            option.value = 321
-            assert option.value == 321
+        option = self.number_option
+        assert option.value == 123
+        option.value = 321
+        assert option.value == 321
 
     def test_checkbox(self):
-        for option in [self.checkbox_option, self.checkbox_option_standalone]:
-            assert option.value
-            option.value = False
-            assert not option.value
+        option = self.checkbox_option
+        assert option.value
+        option.value = False
+        assert not option.value
 
     def test_choice(self):
-        for option in [self.choice_option, self.choice_option_standalone]:
-            assert option.value == "I am your default choice"
-            option.value = "I am your other choice"
-            assert option.value == "I am your other choice"
-            option.value = "I am not an available choice"
-            assert option.value == "I am your default choice"
+        option = self.choice_option
+        assert option.value == "I am your default choice"
+        option.value = "I am your other choice"
+        assert option.value == "I am your other choice"
+        option.value = "I am not an available choice"
+        assert option.value == "I am your default choice"
 
     def test_slider(self):
-        for option in [self.slider_option, self.slider_option_standalone]:
-            assert option.value == 123
-            option.value = 150
-            assert option.value == 150
-            option.value = 0
-            assert option.value == 100
-            option.value = 300
-            assert option.value == 200
-            assert option.json["value"] == 200
+        option = self.slider_option
+        assert option.value == 123
+        option.value = 150
+        assert option.value == 150
+        option.value = 0
+        assert option.value == 100
+        option.value = 300
+        assert option.value == 200
+        assert option.json["value"] == 200
         with self.assertRaises(ValueError):
             Slider()
 
diff --git a/tests/test_visualization.py b/tests/test_visualization.py
index 86e05b0c..2c3ebd4c 100644
--- a/tests/test_visualization.py
+++ b/tests/test_visualization.py
@@ -7,7 +7,10 @@ from mesa.space import MultiGrid
 from mesa.time import SimultaneousActivation
 from mesa.visualization.ModularVisualization import ModularServer
 from mesa.visualization.modules import CanvasGrid, TextElement
-from mesa.visualization.UserParam import UserSettableParameter
+from mesa.visualization.UserParam import (
+    NumberInput,
+    Slider,
+)
 
 
 class MockAgent(mesa.Agent):
@@ -65,8 +68,8 @@ class TestModularServer(TestCase):
         self.user_params = {
             "width": 1,
             "height": 1,
-            "key1": UserSettableParameter("number", "Test Parameter", 101),
-            "key2": UserSettableParameter("slider", "Test Parameter", 200, 0, 300, 10),
+            "key1": NumberInput("Test Parameter", 101),
+            "key2": Slider("Test Parameter", 200, 0, 300, 10),
         }
 
         self.viz_elements = [
@@ -95,8 +98,6 @@ class TestModularServer(TestCase):
     def test_user_params(self):
         print(self.server.user_params)
         assert self.server.user_params == {
-            "key1": UserSettableParameter("number", "Test Parameter", 101).json,
-            "key2": UserSettableParameter(
-                "slider", "Test Parameter", 200, 0, 300, 10
-            ).json,
+            "key1": NumberInput("Test Parameter", 101).json,
+            "key2": Slider("Test Parameter", 200, 0, 300, 10).json,
         }