<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def uniform_partition(min_pt=None, max_pt=None, shape=None, cell_sides=None, nodes_on_bdry=False):
 """Return a partition with equally sized cells. Parameters min_pt, max_pt : float or sequence of float, optional Vectors defining the lower/upper limits of the intervals in an `IntervalProd` (a rectangular box). ``None`` entries mean "compute the value". shape : int or sequence of ints, optional Number of nodes per axis. ``None`` entries mean "compute the value". cell_sides : float or sequence of floats, optional Side length of the partition cells per axis. ``None`` entries mean "compute the value". nodes_on_bdry : bool or sequence, optional If a sequence is provided, it determines per axis whether to place the last grid point on the boundary (``True``) or shift it by half a cell size into the interior (``False``). In each axis, an entry may consist in a single bool or a 2-tuple of bool. In the latter case, the first tuple entry decides for the left, the second for the right boundary. The length of the sequence must be ``array.ndim``. A single boolean is interpreted as a global choice for all boundaries. Notes ----- In each axis, 3 of the 4 possible parameters ``min_pt``, ``max_pt``, ``shape`` and ``cell_sides`` must be given. If all four are provided, they are checked for consistency. See Also -------- uniform_partition_fromintv : partition an existing set uniform_partition_fromgrid : use an existing grid as basis Examples -------- Any combination of three of the four parameters can be used for creation of a partition: (array([ 0. , 0.5, 1. , 1.5, 2. ]),) (array([ 0. , 0.5, 1. , 1.5, 2. ]),) (array([ 0. , 0.5, 1. , 1.5, 2. ]),) (array([ 0. , 0.5, 1. , 1.5, 2. ]),) In higher dimensions, the parameters can be given differently in each axis. Where ``None`` is given, the value will be computed: (array([ 0. , 0.25, 0.5 , 0.75, 1. ]), array([ 0., 1., 2.])) (array([ 0. , 0.25, 0.5 , 0.75, 1. ]), array([ 0., 1., 2.])) (array([ 0. , 0.25, 0.5 , 0.75, 1. ]), array([ 0., 1., 2.])) By default, no grid points are placed on the boundary: False (array([ 0. , 0.25, 0.5 , 0.75, 1. ]),) (array([ 0.125, 0.375, 0.625, 0.875]),) This can be changed with the nodes_on_bdry parameter: True (array([ 0. , 0.25, 0.75, 1. ]),) (array([ 0. , 0.5, 1. ]),) We can specify this per axis, too. In this case we choose both in the first axis and only the rightmost in the second: array([ 0. , 0.25, 0.75, 1. ]) array([ 0. , 0.5, 1. ]) array([ 0. , 0.4, 0.8, 1. ]) array([ 0.2, 0.6, 1. ]) """

    # Normalize partition parameters

    # np.size(None) == 1, so that would screw it for sizes 0 of the rest
    sizes = [np.size(p) for p in (min_pt, max_pt, shape, cell_sides)
             if p is not None]
    ndim = int(np.max(sizes))

    min_pt = normalized_scalar_param_list(min_pt, ndim, param_conv=float,
                                          keep_none=True)
    max_pt = normalized_scalar_param_list(max_pt, ndim, param_conv=float,
                                          keep_none=True)
    shape = normalized_scalar_param_list(shape, ndim, param_conv=safe_int_conv,
                                         keep_none=True)
    cell_sides = normalized_scalar_param_list(cell_sides, ndim,
                                              param_conv=float, keep_none=True)

    nodes_on_bdry = normalized_nodes_on_bdry(nodes_on_bdry, ndim)

    # Calculate the missing parameters in min_pt, max_pt, shape
    for i, (xmin, xmax, n, dx, on_bdry) in enumerate(
            zip(min_pt, max_pt, shape, cell_sides, nodes_on_bdry)):
        num_params = sum(p is not None for p in (xmin, xmax, n, dx))
        if num_params < 3:
            raise ValueError('in axis {}: expected at least 3 of the '
                             'parameters `min_pt`, `max_pt`, `shape`, '
                             '`cell_sides`, got {}'
                             ''.format(i, num_params))

        # Unpack the tuple if possible, else use bool globally for this axis
        try:
            bdry_l, bdry_r = on_bdry
        except TypeError:
            bdry_l = bdry_r = on_bdry

        # For each node on the boundary, we subtract 1/2 from the number of
        # full cells between min_pt and max_pt.
        if xmin is None:
            min_pt[i] = xmax - (n - sum([bdry_l, bdry_r]) / 2.0) * dx
        elif xmax is None:
            max_pt[i] = xmin + (n - sum([bdry_l, bdry_r]) / 2.0) * dx
        elif n is None:
            # Here we add to n since (e-b)/s gives the reduced number of cells.
            n_calc = (xmax - xmin) / dx + sum([bdry_l, bdry_r]) / 2.0
            n_round = int(round(n_calc))
            if abs(n_calc - n_round) > 1e-5:
                raise ValueError('in axis {}: calculated number of nodes '
                                 '{} = ({} - {}) / {} too far from integer'
                                 ''.format(i, n_calc, xmax, xmin, dx))
            shape[i] = n_round
        elif dx is None:
            pass
        else:
            xmax_calc = xmin + (n - sum([bdry_l, bdry_r]) / 2.0) * dx
            if not np.isclose(xmax, xmax_calc):
                raise ValueError('in axis {}: calculated endpoint '
                                 '{} = {} + {} * {} too far from given '
                                 'endpoint {}.'
                                 ''.format(i, xmax_calc, xmin, n, dx, xmax))

    return uniform_partition_fromintv(
        IntervalProd(min_pt, max_pt), shape, nodes_on_bdry)