<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_grid_fromintv(intv_prod, shape, nodes_on_bdry=True):
 """Return a grid from sampling an interval product uniformly. The resulting grid will by default include ``intv_prod.min_pt`` and ``intv_prod.max_pt`` as grid points. If you want a subdivision into equally sized cells with grid points in the middle, use `uniform_partition` instead. Parameters intv_prod : `IntervalProd` Set to be sampled. shape : int or sequence of ints Number of nodes per axis. Entries corresponding to degenerate axes must be equal to 1. 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. Returns ------- sampling : `RectGrid` Uniform sampling grid for the interval product. Examples -------- (array([-1.5, -1. , -0.5]), array([ 2. , 2.5, 3. ])) To have the nodes in the "middle", use ``nodes_on_bdry=False``: (array([-1.25, -0.75]), array([ 2.25, 2.75])) See Also -------- uniform_grid : Create a uniform grid directly. odl.discr.partition.uniform_partition_fromintv : divide interval product into equally sized subsets """

    if not isinstance(intv_prod, IntervalProd):
        raise TypeError('{!r} is not an `IntervalProd` instance'
                        ''.format(intv_prod))

    if (np.any(np.isinf(intv_prod.min_pt)) or
            np.any(np.isinf(intv_prod.max_pt))):
        raise ValueError('`intv_prod` must be finite, got {!r}'
                         ''.format('intv_prod'))

    shape = normalized_scalar_param_list(shape, intv_prod.ndim, safe_int_conv)

    if np.shape(nodes_on_bdry) == ():
        nodes_on_bdry = ([(bool(nodes_on_bdry), bool(nodes_on_bdry))] *
                         intv_prod.ndim)
    elif intv_prod.ndim == 1 and len(nodes_on_bdry) == 2:
        nodes_on_bdry = [nodes_on_bdry]
    elif len(nodes_on_bdry) != intv_prod.ndim:
        raise ValueError('`nodes_on_bdry` has length {}, expected {}'
                         ''.format(len(nodes_on_bdry), intv_prod.ndim))
    else:
        shape = tuple(int(n) for n in shape)

    # We need to determine the placement of the grid minimum and maximum
    # points based on the choices in nodes_on_bdry. If in a given axis,
    # and for a given side (left or right), the entry is True, the node lies
    # on the boundary, so this coordinate can simply be taken as-is.
    #
    # Otherwise, the following conditions must be met:
    #
    # 1. The node should be half a stride s away from the boundary
    # 2. Adding or subtracting (n-1)*s should give the other extremal node.
    #
    # If both nodes are to be shifted half a stride inside,
    # the second condition yields
    # a + s/2 + (n-1)*s = b - s/2 => s = (b - a) / n,
    # hence the extremal grid points are
    # gmin = a + s/2 = a + (b - a) / (2 * n),
    # gmax = b - s/2 = b - (b - a) / (2 * n).
    #
    # In the case where one node, say the rightmost, lies on the boundary,
    # the condition 2. reads as
    # a + s/2 + (n-1)*s = b => s = (b - a) / (n - 1/2),
    # thus
    # gmin = a + (b - a) / (2 * n - 1).

    gmin, gmax = [], []
    for n, xmin, xmax, on_bdry in zip(shape, intv_prod.min_pt,
                                      intv_prod.max_pt, nodes_on_bdry):

        # 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

        if bdry_l and bdry_r:
            gmin.append(xmin)
            gmax.append(xmax)
        elif bdry_l and not bdry_r:
            gmin.append(xmin)
            gmax.append(xmax - (xmax - xmin) / (2 * n - 1))
        elif not bdry_l and bdry_r:
            gmin.append(xmin + (xmax - xmin) / (2 * n - 1))
            gmax.append(xmax)
        else:
            gmin.append(xmin + (xmax - xmin) / (2 * n))
            gmax.append(xmax - (xmax - xmin) / (2 * n))

    # Create the grid
    coord_vecs = [np.linspace(mi, ma, num, endpoint=True, dtype=np.float64)
                  for mi, ma, num in zip(gmin, gmax, shape)]
    return RectGrid(*coord_vecs)