<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def parallel_beam_geometry(space, num_angles=None, det_shape=None):
 r"""Create default parallel beam geometry from ``space``. This is intended for simple test cases where users do not need the full flexibility of the geometries, but simply want a geometry that works. This default geometry gives a fully sampled sinogram according to the Nyquist criterion, which in general results in a very large number of samples. In particular, a ``space`` that is not centered at the origin can result in very large detectors. Parameters space : `DiscreteLp` Reconstruction space, the space of the volumetric data to be projected. Needs to be 2d or 3d. num_angles : int, optional Number of angles. Default: Enough to fully sample the data, see Notes. det_shape : int or sequence of int, optional Number of detector pixels. Default: Enough to fully sample the data, see Notes. Returns ------- geometry : `ParallelBeamGeometry` If ``space`` is 2d, return a `Parallel2dGeometry`. If ``space`` is 3d, return a `Parallel3dAxisGeometry`. Examples -------- Create a parallel beam geometry from a 2d space: 45 31 Notes ----- According to [NW2001]_, pages 72--74, a function :math:`f : \mathbb{R}^2 \to \mathbb{R}` that has compact support .. math:: \| x \| > \rho \implies f(x) = 0, and is essentially bandlimited .. math:: \| \xi \| > \Omega \implies \hat{f}(\xi) \approx 0, can be fully reconstructed from a parallel beam ray transform if (1) the projection angles are sampled with a spacing of :math:`\Delta \psi` such that .. math:: \Delta \psi \leq \frac{\pi}{\rho \Omega}, and (2) the detector is sampled with an interval :math:`\Delta s` that satisfies .. math:: \Delta s \leq \frac{\pi}{\Omega}. The geometry returned by this function satisfies these conditions exactly. If the domain is 3-dimensional, the geometry is "separable", in that each slice along the z-dimension of the data is treated as independed 2d data. References .. [NW2001] Natterer, F and Wuebbeling, F. *Mathematical Methods in Image Reconstruction*. SIAM, 2001. https://dx.doi.org/10.1137/1.9780898718324 """

    # Find maximum distance from rotation axis
    corners = space.domain.corners()[:, :2]
    rho = np.max(np.linalg.norm(corners, axis=1))

    # Find default values according to Nyquist criterion.

    # We assume that the function is bandlimited by a wave along the x or y
    # axis. The highest frequency we can measure is then a standing wave with
    # period of twice the inter-node distance.
    min_side = min(space.partition.cell_sides[:2])
    omega = np.pi / min_side
    num_px_horiz = 2 * int(np.ceil(rho * omega / np.pi)) + 1

    if space.ndim == 2:
        det_min_pt = -rho
        det_max_pt = rho
        if det_shape is None:
            det_shape = num_px_horiz
    elif space.ndim == 3:
        num_px_vert = space.shape[2]
        min_h = space.domain.min_pt[2]
        max_h = space.domain.max_pt[2]
        det_min_pt = [-rho, min_h]
        det_max_pt = [rho, max_h]
        if det_shape is None:
            det_shape = [num_px_horiz, num_px_vert]

    if num_angles is None:
        num_angles = int(np.ceil(omega * rho))

    angle_partition = uniform_partition(0, np.pi, num_angles)
    det_partition = uniform_partition(det_min_pt, det_max_pt, det_shape)

    if space.ndim == 2:
        return Parallel2dGeometry(angle_partition, det_partition)
    elif space.ndim == 3:
        return Parallel3dAxisGeometry(angle_partition, det_partition)
    else:
        raise ValueError('``space.ndim`` must be 2 or 3.')