We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] Unusual if statement in _lower_bounding_numba.py
**Describe the bug**
<!--
A clear and concise description of what the bug is.
--> If statement with same code in both branches

**To Reproduce**
<!--
Add a Minimal, Complete, and Verifiable example (for more details, see e.g. https://stackoverflow.com/help/mcve

If the code is too long, feel free to put it in a public gist and link it in the issue: https://gist.github.com
--> See def create_shape_on_matrix, specifically lines 63-68 


**Expected behavior**
<!--
A clear and concise description of what you expected to happen.
--> In the else statement, I would expect ceil and floor to be exchanged


**Versions**
0.27.0

<!--
Please run the following code snippet and paste the output here:

from sktime import show_versions; show_versions()
-->
System:
    python: 3.12.0 (v3.12.0:0fb18b02c8, Oct  2 2023, 09:45:56) [Clang 13.0.0 (clang-1300.0.29.30)]
executable: /path
   machine: macOS-14.4-arm64-arm-64bit

Python dependencies:
          pip: 24.0
       sktime: 0.27.0
      sklearn: 1.4.1.post1
       skbase: 0.7.5
        numpy: 1.26.4
        scipy: 1.12.0
       pandas: 2.1.4
   matplotlib: 3.8.3
       joblib: 1.3.2
        numba: 0.59.0
  statsmodels: None
     pmdarima: None
statsforecast: None
      tsfresh: None
      tslearn: None
        torch: None
   tensorflow: None
tensorflow_probability: None
Backend MacOSX is interactive backend. Turning interactive mode on.

</details>

<!-- Thanks for contributing! -->


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sktime/distances/_lower_bounding_numba.py`
Content:
```
1 """Isolated numba imports for lower_bounding."""
2 
3 __author__ = ["chrisholder", "TonyBagnall"]
4 
5 import math
6 from typing import Union
7 
8 import numpy as np
9 
10 from sktime.utils.numba.njit import njit
11 
12 
13 @njit(cache=True)
14 def create_shape_on_matrix(
15     bounding_matrix: np.ndarray,
16     y_upper_line: np.ndarray,
17     y_lower_line: Union[np.ndarray, None] = None,
18     x_step_size: int = 1,
19     start_val: int = 0,
20 ) -> np.ndarray:
21     """Create a shape from a given upper line and lower line on a matrix.
22 
23     Parameters
24     ----------
25     bounding_matrix: np.ndarray (2d array)
26         Matrix of size mxn where m is len(x) and n is len(y). Values that
27         are inside the shape will be replaced with finite values (0.).
28     y_upper_line: np.ndarray (1d array)
29         Y points of the upper line.
30     y_lower_line: np.ndarray (1d array), defaults = None
31         Y points of the lower line. If no lower line specified, then y_upper_line
32         used as lower line.
33     x_step_size: int, defaults = 1
34         Step size each iteration will increase by
35     start_val: int, defaults = 0
36         Starting coordinate for x
37 
38     Returns
39     -------
40     np.ndarray (2d array)
41         Matrix with values of the shape set to 0. (finite), of the same shape
42         as the passed bounding_matrix.
43     """
44     y_size = bounding_matrix.shape[0]
45 
46     if y_lower_line is None:
47         y_lower_line = y_upper_line
48 
49     upper_line_y_values = y_upper_line.shape[0]
50     lower_line_y_values = y_lower_line.shape[0]
51 
52     if upper_line_y_values != lower_line_y_values:
53         raise ValueError(
54             "The number of upper line values must equal the number of lower line "
55             "values"
56         )
57 
58     half_way = math.floor(upper_line_y_values / 2)
59 
60     for i in range(start_val, upper_line_y_values):
61         x = i * x_step_size
62 
63         if i > half_way:
64             upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))
65             lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))
66         else:
67             upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))
68             lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))
69 
70         if upper_line_y_values == lower_line_y_values:
71             if upper_y == lower_y:
72                 bounding_matrix[upper_y, x] = 0.0
73             else:
74                 bounding_matrix[upper_y : (lower_y + 1), x] = 0.0
75         else:
76             bounding_matrix[upper_y, x] = 0.0
77             bounding_matrix[lower_y, x] = 0.0
78 
79     return bounding_matrix
80 
81 
82 @njit(cache=True)
83 def _check_line_steps(line: np.ndarray) -> np.ndarray:
84     """Check the next 'step' is along the line.
85 
86     Parameters
87     ----------
88     line: np.ndarray
89         line to check steps.
90 
91     Returns
92     -------
93     np.ndarray
94         Line with updated indexes.
95     """
96     prev = line[0]
97     for i in range(1, len(line)):
98         curr_val = line[i]
99         if curr_val > (prev + 1):
100             line[i] = prev + 1
101         elif curr_val < (prev - 1):
102             line[i] = prev - 1
103         prev = curr_val
104     return line
105 
106 
107 @njit(cache=True)
108 def no_bounding(x: np.ndarray, y: np.ndarray) -> np.ndarray:
109     """Create a matrix with no bounding.
110 
111     Parameters
112     ----------
113     x: np.ndarray (2d array)
114         First time series.
115     y: np.ndarray (2d array)
116         Second time series.
117 
118     Returns
119     -------
120     np.ndarray (2d of size mxn where m is len(x) and n is len(y)).
121         Bounding matrix where the values inside the bound are finite values (0s) and
122         outside the bounds are infinity (non finite).
123     """
124     return np.zeros((x.shape[1], y.shape[1]))
125 
126 
127 @njit(cache=True)
128 def sakoe_chiba(x: np.ndarray, y: np.ndarray, window: float) -> np.ndarray:
129     """Create a sakoe chiba lower bounding window on a matrix.
130 
131     Parameters
132     ----------
133     x: np.ndarray (2d array)
134         First time series.
135     y: np.ndarray (2d array)
136         Second time series.
137     window: float
138         Float that is the size of the window. Must be between 0 and 1.
139 
140     Returns
141     -------
142     np.ndarray (2d of size mxn where m is len(x) and n is len(y)).
143         Sakoe Chiba bounding matrix where the values inside the bound are finite
144         values (0s) and outside the bounds are infinity (non finite).
145 
146     Raises
147     ------
148     ValueError
149         If the sakoe_chiba_window_radius is not an integer.
150     """
151     if window < 0 or window > 1:
152         raise ValueError("Window must between 0 and 1")
153 
154     x_size = x.shape[1]
155     y_size = y.shape[1]
156     bounding_matrix = np.full((x_size, y_size), np.inf)
157     sakoe_chiba_window_radius = ((x_size / 100) * window) * 100
158 
159     x_upper_line_values = np.interp(
160         list(range(x_size)),
161         [0, x_size - 1],
162         [0 - sakoe_chiba_window_radius, y_size - sakoe_chiba_window_radius - 1],
163     )
164     x_lower_line_values = np.interp(
165         list(range(x_size)),
166         [0, x_size - 1],
167         [0 + sakoe_chiba_window_radius, y_size + sakoe_chiba_window_radius - 1],
168     )
169 
170     bounding_matrix = create_shape_on_matrix(
171         bounding_matrix, x_upper_line_values, x_lower_line_values
172     )
173 
174     return bounding_matrix
175 
176 
177 @njit(cache=True)
178 def itakura_parallelogram(
179     x: np.ndarray, y: np.ndarray, itakura_max_slope: float
180 ) -> np.ndarray:
181     """Create a itakura parallelogram bounding matrix.
182 
183     Parameters
184     ----------
185     x: np.ndarray (2d array)
186         First time series.
187     y: np.ndarray (2d array)
188         Second time series.
189     itakura_max_slope: float or int
190         Gradient of the slope must be between 0 and 1.
191 
192     Returns
193     -------
194     np.ndarray (2d of size mxn where m is len(x) and n is len(y)).
195         Sakoe Chiba bounding matrix where the values inside the bound are finite
196         values (0s) and outside the bounds are infinity (non finite).
197 
198     Raises
199     ------
200     ValueError
201         If the itakura_max_slope is not a float or int.
202     """
203     if itakura_max_slope < 0 or itakura_max_slope > 1:
204         raise ValueError("Window must between 0 and 1")
205     x_size = x.shape[1]
206     y_size = y.shape[1]
207     bounding_matrix = np.full((y_size, x_size), np.inf)
208     itakura_max_slope = math.floor(((x_size / 100) * itakura_max_slope) * 100) / 2
209 
210     middle_x_upper = math.ceil(x_size / 2)
211     middle_x_lower = math.floor(x_size / 2)
212     if middle_x_lower == middle_x_upper:
213         middle_x_lower = middle_x_lower - 1
214     middle_y = math.floor(y_size / 2)
215 
216     difference_from_middle_y = abs((middle_x_lower * itakura_max_slope) - middle_y)
217     middle_y_lower = middle_y + difference_from_middle_y
218     middle_y_upper = middle_y - difference_from_middle_y
219 
220     x_upper_line_values = np.interp(
221         list(range(x_size)),
222         [0, middle_x_lower, middle_x_upper, x_size - 1],
223         [0, middle_y_upper, middle_y_upper, y_size - 1],
224     )
225     x_lower_line_values = np.interp(
226         list(range(x_size)),
227         [0, middle_x_lower, middle_x_upper, x_size - 1],
228         [0, middle_y_lower, middle_y_lower, y_size - 1],
229     )
230 
231     if np.array_equal(x_upper_line_values, x_lower_line_values):
232         x_upper_line_values = _check_line_steps(x_upper_line_values)
233 
234     bounding_matrix = create_shape_on_matrix(
235         bounding_matrix, x_upper_line_values, x_lower_line_values
236     )
237 
238     return bounding_matrix
239 
240 
241 @njit(cache=True)
242 def numba_create_bounding_matrix(
243     x: np.ndarray,
244     y: np.ndarray,
245     window: float = -1.0,
246     itakura_max_slope: float = -1.0,
247 ) -> np.ndarray:
248     """Numba compiled way of creating bounding matrix.
249 
250     Parameters
251     ----------
252     x: np.ndarray (2d array)
253         First time series.
254     y: np.ndarray (2d array)
255         Second time series.
256     window: float, defaults = -1.
257         Float that is the % radius of the sakoe chiba window (if using Sakoe-Chiba
258         lower bounding). Must be between 0 and 1.
259     itakura_max_slope: float, defaults = -1.
260         Gradient of the slope for itakura parallelogram (if using Itakura
261         Parallelogram lower bounding). Must be between 0 and 1.
262     """
263     if window != -1.0:
264         bounding_matrix = sakoe_chiba(x, y, window)
265     elif itakura_max_slope != -1.0:
266         bounding_matrix = itakura_parallelogram(x, y, itakura_max_slope)
267     else:
268         bounding_matrix = no_bounding(x, y)
269 
270     return bounding_matrix
271
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sktime/distances/_lower_bounding_numba.py b/sktime/distances/_lower_bounding_numba.py
--- a/sktime/distances/_lower_bounding_numba.py
+++ b/sktime/distances/_lower_bounding_numba.py
@@ -55,26 +55,16 @@
             "values"
         )
 
-    half_way = math.floor(upper_line_y_values / 2)
-
     for i in range(start_val, upper_line_y_values):
         x = i * x_step_size
 
-        if i > half_way:
-            upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))
-            lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))
-        else:
-            upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))
-            lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))
-
-        if upper_line_y_values == lower_line_y_values:
-            if upper_y == lower_y:
-                bounding_matrix[upper_y, x] = 0.0
-            else:
-                bounding_matrix[upper_y : (lower_y + 1), x] = 0.0
-        else:
+        upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))
+        lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))
+
+        if upper_y == lower_y:
             bounding_matrix[upper_y, x] = 0.0
-            bounding_matrix[lower_y, x] = 0.0
+        else:
+            bounding_matrix[upper_y : (lower_y + 1), x] = 0.0
 
     return bounding_matrix

{"golden_diff": "diff --git a/sktime/distances/_lower_bounding_numba.py b/sktime/distances/_lower_bounding_numba.py\n--- a/sktime/distances/_lower_bounding_numba.py\n+++ b/sktime/distances/_lower_bounding_numba.py\n@@ -55,26 +55,16 @@\n             \"values\"\n         )\n \n-    half_way = math.floor(upper_line_y_values / 2)\n-\n     for i in range(start_val, upper_line_y_values):\n         x = i * x_step_size\n \n-        if i > half_way:\n-            upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))\n-            lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))\n-        else:\n-            upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))\n-            lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))\n-\n-        if upper_line_y_values == lower_line_y_values:\n-            if upper_y == lower_y:\n-                bounding_matrix[upper_y, x] = 0.0\n-            else:\n-                bounding_matrix[upper_y : (lower_y + 1), x] = 0.0\n-        else:\n+        upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))\n+        lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))\n+\n+        if upper_y == lower_y:\n             bounding_matrix[upper_y, x] = 0.0\n-            bounding_matrix[lower_y, x] = 0.0\n+        else:\n+            bounding_matrix[upper_y : (lower_y + 1), x] = 0.0\n \n     return bounding_matrix\n", "issue": "[BUG] Unusual if statement in _lower_bounding_numba.py\n**Describe the bug**\r\n<!--\r\nA clear and concise description of what the bug is.\r\n--> If statement with same code in both branches\r\n\r\n**To Reproduce**\r\n<!--\r\nAdd a Minimal, Complete, and Verifiable example (for more details, see e.g. https://stackoverflow.com/help/mcve\r\n\r\nIf the code is too long, feel free to put it in a public gist and link it in the issue: https://gist.github.com\r\n--> See def create_shape_on_matrix, specifically lines 63-68 \r\n\r\n\r\n**Expected behavior**\r\n<!--\r\nA clear and concise description of what you expected to happen.\r\n--> In the else statement, I would expect ceil and floor to be exchanged\r\n\r\n\r\n**Versions**\r\n0.27.0\r\n\r\n<!--\r\nPlease run the following code snippet and paste the output here:\r\n\r\nfrom sktime import show_versions; show_versions()\r\n-->\r\nSystem:\r\n    python: 3.12.0 (v3.12.0:0fb18b02c8, Oct  2 2023, 09:45:56) [Clang 13.0.0 (clang-1300.0.29.30)]\r\nexecutable: /path\r\n   machine: macOS-14.4-arm64-arm-64bit\r\n\r\nPython dependencies:\r\n          pip: 24.0\r\n       sktime: 0.27.0\r\n      sklearn: 1.4.1.post1\r\n       skbase: 0.7.5\r\n        numpy: 1.26.4\r\n        scipy: 1.12.0\r\n       pandas: 2.1.4\r\n   matplotlib: 3.8.3\r\n       joblib: 1.3.2\r\n        numba: 0.59.0\r\n  statsmodels: None\r\n     pmdarima: None\r\nstatsforecast: None\r\n      tsfresh: None\r\n      tslearn: None\r\n        torch: None\r\n   tensorflow: None\r\ntensorflow_probability: None\r\nBackend MacOSX is interactive backend. Turning interactive mode on.\r\n\r\n</details>\r\n\r\n<!-- Thanks for contributing! -->\r\n\n", "before_files": [{"content": "\"\"\"Isolated numba imports for lower_bounding.\"\"\"\n\n__author__ = [\"chrisholder\", \"TonyBagnall\"]\n\nimport math\nfrom typing import Union\n\nimport numpy as np\n\nfrom sktime.utils.numba.njit import njit\n\n\n@njit(cache=True)\ndef create_shape_on_matrix(\n    bounding_matrix: np.ndarray,\n    y_upper_line: np.ndarray,\n    y_lower_line: Union[np.ndarray, None] = None,\n    x_step_size: int = 1,\n    start_val: int = 0,\n) -> np.ndarray:\n    \"\"\"Create a shape from a given upper line and lower line on a matrix.\n\n    Parameters\n    ----------\n    bounding_matrix: np.ndarray (2d array)\n        Matrix of size mxn where m is len(x) and n is len(y). Values that\n        are inside the shape will be replaced with finite values (0.).\n    y_upper_line: np.ndarray (1d array)\n        Y points of the upper line.\n    y_lower_line: np.ndarray (1d array), defaults = None\n        Y points of the lower line. If no lower line specified, then y_upper_line\n        used as lower line.\n    x_step_size: int, defaults = 1\n        Step size each iteration will increase by\n    start_val: int, defaults = 0\n        Starting coordinate for x\n\n    Returns\n    -------\n    np.ndarray (2d array)\n        Matrix with values of the shape set to 0. (finite), of the same shape\n        as the passed bounding_matrix.\n    \"\"\"\n    y_size = bounding_matrix.shape[0]\n\n    if y_lower_line is None:\n        y_lower_line = y_upper_line\n\n    upper_line_y_values = y_upper_line.shape[0]\n    lower_line_y_values = y_lower_line.shape[0]\n\n    if upper_line_y_values != lower_line_y_values:\n        raise ValueError(\n            \"The number of upper line values must equal the number of lower line \"\n            \"values\"\n        )\n\n    half_way = math.floor(upper_line_y_values / 2)\n\n    for i in range(start_val, upper_line_y_values):\n        x = i * x_step_size\n\n        if i > half_way:\n            upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))\n            lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))\n        else:\n            upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))\n            lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))\n\n        if upper_line_y_values == lower_line_y_values:\n            if upper_y == lower_y:\n                bounding_matrix[upper_y, x] = 0.0\n            else:\n                bounding_matrix[upper_y : (lower_y + 1), x] = 0.0\n        else:\n            bounding_matrix[upper_y, x] = 0.0\n            bounding_matrix[lower_y, x] = 0.0\n\n    return bounding_matrix\n\n\n@njit(cache=True)\ndef _check_line_steps(line: np.ndarray) -> np.ndarray:\n    \"\"\"Check the next 'step' is along the line.\n\n    Parameters\n    ----------\n    line: np.ndarray\n        line to check steps.\n\n    Returns\n    -------\n    np.ndarray\n        Line with updated indexes.\n    \"\"\"\n    prev = line[0]\n    for i in range(1, len(line)):\n        curr_val = line[i]\n        if curr_val > (prev + 1):\n            line[i] = prev + 1\n        elif curr_val < (prev - 1):\n            line[i] = prev - 1\n        prev = curr_val\n    return line\n\n\n@njit(cache=True)\ndef no_bounding(x: np.ndarray, y: np.ndarray) -> np.ndarray:\n    \"\"\"Create a matrix with no bounding.\n\n    Parameters\n    ----------\n    x: np.ndarray (2d array)\n        First time series.\n    y: np.ndarray (2d array)\n        Second time series.\n\n    Returns\n    -------\n    np.ndarray (2d of size mxn where m is len(x) and n is len(y)).\n        Bounding matrix where the values inside the bound are finite values (0s) and\n        outside the bounds are infinity (non finite).\n    \"\"\"\n    return np.zeros((x.shape[1], y.shape[1]))\n\n\n@njit(cache=True)\ndef sakoe_chiba(x: np.ndarray, y: np.ndarray, window: float) -> np.ndarray:\n    \"\"\"Create a sakoe chiba lower bounding window on a matrix.\n\n    Parameters\n    ----------\n    x: np.ndarray (2d array)\n        First time series.\n    y: np.ndarray (2d array)\n        Second time series.\n    window: float\n        Float that is the size of the window. Must be between 0 and 1.\n\n    Returns\n    -------\n    np.ndarray (2d of size mxn where m is len(x) and n is len(y)).\n        Sakoe Chiba bounding matrix where the values inside the bound are finite\n        values (0s) and outside the bounds are infinity (non finite).\n\n    Raises\n    ------\n    ValueError\n        If the sakoe_chiba_window_radius is not an integer.\n    \"\"\"\n    if window < 0 or window > 1:\n        raise ValueError(\"Window must between 0 and 1\")\n\n    x_size = x.shape[1]\n    y_size = y.shape[1]\n    bounding_matrix = np.full((x_size, y_size), np.inf)\n    sakoe_chiba_window_radius = ((x_size / 100) * window) * 100\n\n    x_upper_line_values = np.interp(\n        list(range(x_size)),\n        [0, x_size - 1],\n        [0 - sakoe_chiba_window_radius, y_size - sakoe_chiba_window_radius - 1],\n    )\n    x_lower_line_values = np.interp(\n        list(range(x_size)),\n        [0, x_size - 1],\n        [0 + sakoe_chiba_window_radius, y_size + sakoe_chiba_window_radius - 1],\n    )\n\n    bounding_matrix = create_shape_on_matrix(\n        bounding_matrix, x_upper_line_values, x_lower_line_values\n    )\n\n    return bounding_matrix\n\n\n@njit(cache=True)\ndef itakura_parallelogram(\n    x: np.ndarray, y: np.ndarray, itakura_max_slope: float\n) -> np.ndarray:\n    \"\"\"Create a itakura parallelogram bounding matrix.\n\n    Parameters\n    ----------\n    x: np.ndarray (2d array)\n        First time series.\n    y: np.ndarray (2d array)\n        Second time series.\n    itakura_max_slope: float or int\n        Gradient of the slope must be between 0 and 1.\n\n    Returns\n    -------\n    np.ndarray (2d of size mxn where m is len(x) and n is len(y)).\n        Sakoe Chiba bounding matrix where the values inside the bound are finite\n        values (0s) and outside the bounds are infinity (non finite).\n\n    Raises\n    ------\n    ValueError\n        If the itakura_max_slope is not a float or int.\n    \"\"\"\n    if itakura_max_slope < 0 or itakura_max_slope > 1:\n        raise ValueError(\"Window must between 0 and 1\")\n    x_size = x.shape[1]\n    y_size = y.shape[1]\n    bounding_matrix = np.full((y_size, x_size), np.inf)\n    itakura_max_slope = math.floor(((x_size / 100) * itakura_max_slope) * 100) / 2\n\n    middle_x_upper = math.ceil(x_size / 2)\n    middle_x_lower = math.floor(x_size / 2)\n    if middle_x_lower == middle_x_upper:\n        middle_x_lower = middle_x_lower - 1\n    middle_y = math.floor(y_size / 2)\n\n    difference_from_middle_y = abs((middle_x_lower * itakura_max_slope) - middle_y)\n    middle_y_lower = middle_y + difference_from_middle_y\n    middle_y_upper = middle_y - difference_from_middle_y\n\n    x_upper_line_values = np.interp(\n        list(range(x_size)),\n        [0, middle_x_lower, middle_x_upper, x_size - 1],\n        [0, middle_y_upper, middle_y_upper, y_size - 1],\n    )\n    x_lower_line_values = np.interp(\n        list(range(x_size)),\n        [0, middle_x_lower, middle_x_upper, x_size - 1],\n        [0, middle_y_lower, middle_y_lower, y_size - 1],\n    )\n\n    if np.array_equal(x_upper_line_values, x_lower_line_values):\n        x_upper_line_values = _check_line_steps(x_upper_line_values)\n\n    bounding_matrix = create_shape_on_matrix(\n        bounding_matrix, x_upper_line_values, x_lower_line_values\n    )\n\n    return bounding_matrix\n\n\n@njit(cache=True)\ndef numba_create_bounding_matrix(\n    x: np.ndarray,\n    y: np.ndarray,\n    window: float = -1.0,\n    itakura_max_slope: float = -1.0,\n) -> np.ndarray:\n    \"\"\"Numba compiled way of creating bounding matrix.\n\n    Parameters\n    ----------\n    x: np.ndarray (2d array)\n        First time series.\n    y: np.ndarray (2d array)\n        Second time series.\n    window: float, defaults = -1.\n        Float that is the % radius of the sakoe chiba window (if using Sakoe-Chiba\n        lower bounding). Must be between 0 and 1.\n    itakura_max_slope: float, defaults = -1.\n        Gradient of the slope for itakura parallelogram (if using Itakura\n        Parallelogram lower bounding). Must be between 0 and 1.\n    \"\"\"\n    if window != -1.0:\n        bounding_matrix = sakoe_chiba(x, y, window)\n    elif itakura_max_slope != -1.0:\n        bounding_matrix = itakura_parallelogram(x, y, itakura_max_slope)\n    else:\n        bounding_matrix = no_bounding(x, y)\n\n    return bounding_matrix\n", "path": "sktime/distances/_lower_bounding_numba.py"}], "after_files": [{"content": "\"\"\"Isolated numba imports for lower_bounding.\"\"\"\n\n__author__ = [\"chrisholder\", \"TonyBagnall\"]\n\nimport math\nfrom typing import Union\n\nimport numpy as np\n\nfrom sktime.utils.numba.njit import njit\n\n\n@njit(cache=True)\ndef create_shape_on_matrix(\n    bounding_matrix: np.ndarray,\n    y_upper_line: np.ndarray,\n    y_lower_line: Union[np.ndarray, None] = None,\n    x_step_size: int = 1,\n    start_val: int = 0,\n) -> np.ndarray:\n    \"\"\"Create a shape from a given upper line and lower line on a matrix.\n\n    Parameters\n    ----------\n    bounding_matrix: np.ndarray (2d array)\n        Matrix of size mxn where m is len(x) and n is len(y). Values that\n        are inside the shape will be replaced with finite values (0.).\n    y_upper_line: np.ndarray (1d array)\n        Y points of the upper line.\n    y_lower_line: np.ndarray (1d array), defaults = None\n        Y points of the lower line. If no lower line specified, then y_upper_line\n        used as lower line.\n    x_step_size: int, defaults = 1\n        Step size each iteration will increase by\n    start_val: int, defaults = 0\n        Starting coordinate for x\n\n    Returns\n    -------\n    np.ndarray (2d array)\n        Matrix with values of the shape set to 0. (finite), of the same shape\n        as the passed bounding_matrix.\n    \"\"\"\n    y_size = bounding_matrix.shape[0]\n\n    if y_lower_line is None:\n        y_lower_line = y_upper_line\n\n    upper_line_y_values = y_upper_line.shape[0]\n    lower_line_y_values = y_lower_line.shape[0]\n\n    if upper_line_y_values != lower_line_y_values:\n        raise ValueError(\n            \"The number of upper line values must equal the number of lower line \"\n            \"values\"\n        )\n\n    for i in range(start_val, upper_line_y_values):\n        x = i * x_step_size\n\n        upper_y = max(0, min(y_size - 1, math.ceil(y_upper_line[i])))\n        lower_y = max(0, min(y_size - 1, math.floor(y_lower_line[i])))\n\n        if upper_y == lower_y:\n            bounding_matrix[upper_y, x] = 0.0\n        else:\n            bounding_matrix[upper_y : (lower_y + 1), x] = 0.0\n\n    return bounding_matrix\n\n\n@njit(cache=True)\ndef _check_line_steps(line: np.ndarray) -> np.ndarray:\n    \"\"\"Check the next 'step' is along the line.\n\n    Parameters\n    ----------\n    line: np.ndarray\n        line to check steps.\n\n    Returns\n    -------\n    np.ndarray\n        Line with updated indexes.\n    \"\"\"\n    prev = line[0]\n    for i in range(1, len(line)):\n        curr_val = line[i]\n        if curr_val > (prev + 1):\n            line[i] = prev + 1\n        elif curr_val < (prev - 1):\n            line[i] = prev - 1\n        prev = curr_val\n    return line\n\n\n@njit(cache=True)\ndef no_bounding(x: np.ndarray, y: np.ndarray) -> np.ndarray:\n    \"\"\"Create a matrix with no bounding.\n\n    Parameters\n    ----------\n    x: np.ndarray (2d array)\n        First time series.\n    y: np.ndarray (2d array)\n        Second time series.\n\n    Returns\n    -------\n    np.ndarray (2d of size mxn where m is len(x) and n is len(y)).\n        Bounding matrix where the values inside the bound are finite values (0s) and\n        outside the bounds are infinity (non finite).\n    \"\"\"\n    return np.zeros((x.shape[1], y.shape[1]))\n\n\n@njit(cache=True)\ndef sakoe_chiba(x: np.ndarray, y: np.ndarray, window: float) -> np.ndarray:\n    \"\"\"Create a sakoe chiba lower bounding window on a matrix.\n\n    Parameters\n    ----------\n    x: np.ndarray (2d array)\n        First time series.\n    y: np.ndarray (2d array)\n        Second time series.\n    window: float\n        Float that is the size of the window. Must be between 0 and 1.\n\n    Returns\n    -------\n    np.ndarray (2d of size mxn where m is len(x) and n is len(y)).\n        Sakoe Chiba bounding matrix where the values inside the bound are finite\n        values (0s) and outside the bounds are infinity (non finite).\n\n    Raises\n    ------\n    ValueError\n        If the sakoe_chiba_window_radius is not an integer.\n    \"\"\"\n    if window < 0 or window > 1:\n        raise ValueError(\"Window must between 0 and 1\")\n\n    x_size = x.shape[1]\n    y_size = y.shape[1]\n    bounding_matrix = np.full((x_size, y_size), np.inf)\n    sakoe_chiba_window_radius = ((x_size / 100) * window) * 100\n\n    x_upper_line_values = np.interp(\n        list(range(x_size)),\n        [0, x_size - 1],\n        [0 - sakoe_chiba_window_radius, y_size - sakoe_chiba_window_radius - 1],\n    )\n    x_lower_line_values = np.interp(\n        list(range(x_size)),\n        [0, x_size - 1],\n        [0 + sakoe_chiba_window_radius, y_size + sakoe_chiba_window_radius - 1],\n    )\n\n    bounding_matrix = create_shape_on_matrix(\n        bounding_matrix, x_upper_line_values, x_lower_line_values\n    )\n\n    return bounding_matrix\n\n\n@njit(cache=True)\ndef itakura_parallelogram(\n    x: np.ndarray, y: np.ndarray, itakura_max_slope: float\n) -> np.ndarray:\n    \"\"\"Create a itakura parallelogram bounding matrix.\n\n    Parameters\n    ----------\n    x: np.ndarray (2d array)\n        First time series.\n    y: np.ndarray (2d array)\n        Second time series.\n    itakura_max_slope: float or int\n        Gradient of the slope must be between 0 and 1.\n\n    Returns\n    -------\n    np.ndarray (2d of size mxn where m is len(x) and n is len(y)).\n        Sakoe Chiba bounding matrix where the values inside the bound are finite\n        values (0s) and outside the bounds are infinity (non finite).\n\n    Raises\n    ------\n    ValueError\n        If the itakura_max_slope is not a float or int.\n    \"\"\"\n    if itakura_max_slope < 0 or itakura_max_slope > 1:\n        raise ValueError(\"Window must between 0 and 1\")\n    x_size = x.shape[1]\n    y_size = y.shape[1]\n    bounding_matrix = np.full((y_size, x_size), np.inf)\n    itakura_max_slope = math.floor(((x_size / 100) * itakura_max_slope) * 100) / 2\n\n    middle_x_upper = math.ceil(x_size / 2)\n    middle_x_lower = math.floor(x_size / 2)\n    if middle_x_lower == middle_x_upper:\n        middle_x_lower = middle_x_lower - 1\n    middle_y = math.floor(y_size / 2)\n\n    difference_from_middle_y = abs((middle_x_lower * itakura_max_slope) - middle_y)\n    middle_y_lower = middle_y + difference_from_middle_y\n    middle_y_upper = middle_y - difference_from_middle_y\n\n    x_upper_line_values = np.interp(\n        list(range(x_size)),\n        [0, middle_x_lower, middle_x_upper, x_size - 1],\n        [0, middle_y_upper, middle_y_upper, y_size - 1],\n    )\n    x_lower_line_values = np.interp(\n        list(range(x_size)),\n        [0, middle_x_lower, middle_x_upper, x_size - 1],\n        [0, middle_y_lower, middle_y_lower, y_size - 1],\n    )\n\n    if np.array_equal(x_upper_line_values, x_lower_line_values):\n        x_upper_line_values = _check_line_steps(x_upper_line_values)\n\n    bounding_matrix = create_shape_on_matrix(\n        bounding_matrix, x_upper_line_values, x_lower_line_values\n    )\n\n    return bounding_matrix\n\n\n@njit(cache=True)\ndef numba_create_bounding_matrix(\n    x: np.ndarray,\n    y: np.ndarray,\n    window: float = -1.0,\n    itakura_max_slope: float = -1.0,\n) -> np.ndarray:\n    \"\"\"Numba compiled way of creating bounding matrix.\n\n    Parameters\n    ----------\n    x: np.ndarray (2d array)\n        First time series.\n    y: np.ndarray (2d array)\n        Second time series.\n    window: float, defaults = -1.\n        Float that is the % radius of the sakoe chiba window (if using Sakoe-Chiba\n        lower bounding). Must be between 0 and 1.\n    itakura_max_slope: float, defaults = -1.\n        Gradient of the slope for itakura parallelogram (if using Itakura\n        Parallelogram lower bounding). Must be between 0 and 1.\n    \"\"\"\n    if window != -1.0:\n        bounding_matrix = sakoe_chiba(x, y, window)\n    elif itakura_max_slope != -1.0:\n        bounding_matrix = itakura_parallelogram(x, y, itakura_max_slope)\n    else:\n        bounding_matrix = no_bounding(x, y)\n\n    return bounding_matrix\n", "path": "sktime/distances/_lower_bounding_numba.py"}]}