# Path: rbf/utils.py
# def assert_shape(arr, shape, label='array'):
#     '''
#     Raises a ValueError if `arr` does not have the specified shape
# 
#     Parameters
#     ----------
#     arr : array-like
# 
#     shape : tuple
#         The shape requirement for `arr`. This can have `None` to indicate that
#         an axis can have any length. This can also have an Ellipsis to only
#         enforce the shape for the first and/or last dimensions of `arr`
# 
#     label : str
#         What to call `arr` in the error
# 
#     '''
#     if not _SHAPE_ASSERTIONS:
#         return
# 
#     if hasattr(arr, 'shape'):
#         # if `arr` has a shape attribute then use it rather than finding the
#         # shape with np.shape, which converts `arr` to a numpy array
#         arr_shape = arr.shape
#     else:
#         arr_shape = np.shape(arr)
# 
#     arr_ndim = len(arr_shape)
#     if Ellipsis in shape:
#         # If `shape` has an ellipsis then only the first and/or last dimensions
#         # will be checked
#         start_shape = shape[:shape.index(Ellipsis)]
#         end_shape = shape[shape.index(Ellipsis) + 1:]
#         start_ndim = len(start_shape)
#         end_ndim = len(end_shape)
#         if arr_ndim < (start_ndim + end_ndim):
#             raise ValueError(
#                 '%s is %d dimensional but it should have at least %d '
#                 'dimensions' % (label, arr_ndim, start_ndim + end_ndim))
# 
#         arr_start_shape = arr_shape[:start_ndim]
#         arr_end_shape = arr_shape[arr_ndim - end_ndim:]
#         for axis, (i, j) in enumerate(zip(arr_start_shape, start_shape)):
#             if j is None:
#                 continue
# 
#             if i != j:
#                 raise ValueError(
#                     'axis %d of %s has length %d but it should have length %d'
#                     % (axis, label, i, j))
# 
#         for axis, (i, j) in enumerate(zip(arr_end_shape, end_shape)):
#             if j is None:
#                 continue
# 
#             if i != j:
#                 raise ValueError(
#                     'axis %d of %s has length %d but it should have length %d'
#                     % (arr_ndim - end_ndim + axis, label, i, j))
# 
#     else:
#         ndim = len(shape)
#         if arr_ndim != ndim:
#             raise ValueError(
#                 '%s is %d dimensional but it should have %d dimensions'
#                 % (label, arr_ndim, ndim))
# 
#         for axis, (i, j) in enumerate(zip(arr_shape, shape)):
#             if j is None:
#                 continue
# 
#             if i != j:
#                 raise ValueError(
#                     'axis %d of %s has length %d but it should have length %d'
#                     % (axis, label, i, j))
# 
#     return
# 
# class KDTree(cKDTree):
#     '''
#     Same as `scipy.spatial.cKDTree`, except when calling `query` with `k=1`,
#     the output does not get squeezed to 1D. Also, an error will be raised if
#     `query` is called with `k` larger than the number of points in the tree.
#     '''
#     def query(self, x, k=1, **kwargs):
#         '''query the KD-tree for nearest neighbors'''
#         if k > self.n:
#             raise ValueError(
#                 'Cannot find the %s nearest points among a set of %s points'
#                 % (k, self.n))
# 
#         dist, indices = cKDTree.query(self, x, k=k, **kwargs)
#         if k == 1:
#             dist = dist[..., None]
#             indices = indices[..., None]
# 
#         return dist, indices
# 
# Path: rbf/pde/domain.py
# def as_domain(obj):
#     '''
#     Coerces the input to a `Domain` instance. The input can be a tuple
#     of vertices and simplices, or it can already be a `Domain`
#     instance. In the latter case, the object will be returned
#     unaltered.
#     '''
#     if issubclass(type(obj), Domain):
#         return obj
#     else:
#         return Domain(*obj)


import logging
import numpy as np
from scipy.sparse import csc_matrix
from scipy.sparse.csgraph import reverse_cuthill_mckee
from rbf.utils import assert_shape, KDTree
from rbf.pde.domain import as_domain
from rbf.pde.sampling import rejection_sampling, poisson_discs

    if m is None:
        # this should be roughly equal to the stencil size for the RBF-FD
        # problem
        m = 5**nodes.shape[1]

    m = min(m, nodes.shape[0])
    # find the indices of the nearest m nodes for each node
    _, idx = KDTree(nodes).query(nodes, m)
    # efficiently form adjacency matrix
    col = idx.ravel()
    row = np.repeat(np.arange(nodes.shape[0]), m)
    data = np.ones(nodes.shape[0]*m, dtype=bool)
    mat = csc_matrix((data, (row, col)), dtype=bool)
    permutation = reverse_cuthill_mckee(mat)
    return permutation


def _check_spacing(nodes, rho=None):
    '''
    Check if any nodes are unusually close to eachother. If so, a warning will
    be printed.
    '''
    n, dim = nodes.shape

    if rho is None:
        def rho(x):
            return np.ones(x.shape[0])

    # distance to nearest neighbor

# Path: rbf/utils.py
# def assert_shape(arr, shape, label='array'):
#     '''
#     Raises a ValueError if `arr` does not have the specified shape
# 
#     Parameters
#     ----------
#     arr : array-like
# 
#     shape : tuple
#         The shape requirement for `arr`. This can have `None` to indicate that
#         an axis can have any length. This can also have an Ellipsis to only
#         enforce the shape for the first and/or last dimensions of `arr`
# 
#     label : str
#         What to call `arr` in the error
# 
#     '''
#     if not _SHAPE_ASSERTIONS:
#         return
# 
#     if hasattr(arr, 'shape'):
#         # if `arr` has a shape attribute then use it rather than finding the
#         # shape with np.shape, which converts `arr` to a numpy array
#         arr_shape = arr.shape
#     else:
#         arr_shape = np.shape(arr)
# 
#     arr_ndim = len(arr_shape)
#     if Ellipsis in shape:
#         # If `shape` has an ellipsis then only the first and/or last dimensions
#         # will be checked
#         start_shape = shape[:shape.index(Ellipsis)]
#         end_shape = shape[shape.index(Ellipsis) + 1:]
#         start_ndim = len(start_shape)
#         end_ndim = len(end_shape)
#         if arr_ndim < (start_ndim + end_ndim):
#             raise ValueError(
#                 '%s is %d dimensional but it should have at least %d '
#                 'dimensions' % (label, arr_ndim, start_ndim + end_ndim))
# 
#         arr_start_shape = arr_shape[:start_ndim]
#         arr_end_shape = arr_shape[arr_ndim - end_ndim:]
#         for axis, (i, j) in enumerate(zip(arr_start_shape, start_shape)):
#             if j is None:
#                 continue
# 
#             if i != j:
#                 raise ValueError(
#                     'axis %d of %s has length %d but it should have length %d'
#                     % (axis, label, i, j))
# 
#         for axis, (i, j) in enumerate(zip(arr_end_shape, end_shape)):
#             if j is None:
#                 continue
# 
#             if i != j:
#                 raise ValueError(
#                     'axis %d of %s has length %d but it should have length %d'
#                     % (arr_ndim - end_ndim + axis, label, i, j))
# 
#     else:
#         ndim = len(shape)
#         if arr_ndim != ndim:
#             raise ValueError(
#                 '%s is %d dimensional but it should have %d dimensions'
#                 % (label, arr_ndim, ndim))
# 
#         for axis, (i, j) in enumerate(zip(arr_shape, shape)):
#             if j is None:
#                 continue
# 
#             if i != j:
#                 raise ValueError(
#                     'axis %d of %s has length %d but it should have length %d'
#                     % (axis, label, i, j))
# 
#     return
# 
# class KDTree(cKDTree):
#     '''
#     Same as `scipy.spatial.cKDTree`, except when calling `query` with `k=1`,
#     the output does not get squeezed to 1D. Also, an error will be raised if
#     `query` is called with `k` larger than the number of points in the tree.
#     '''
#     def query(self, x, k=1, **kwargs):
#         '''query the KD-tree for nearest neighbors'''
#         if k > self.n:
#             raise ValueError(
#                 'Cannot find the %s nearest points among a set of %s points'
#                 % (k, self.n))
# 
#         dist, indices = cKDTree.query(self, x, k=k, **kwargs)
#         if k == 1:
#             dist = dist[..., None]
#             indices = indices[..., None]
# 
#         return dist, indices
# 
# Path: rbf/pde/domain.py
# def as_domain(obj):
#     '''
#     Coerces the input to a `Domain` instance. The input can be a tuple
#     of vertices and simplices, or it can already be a `Domain`
#     instance. In the latter case, the object will be returned
#     unaltered.
#     '''
#     if issubclass(type(obj), Domain):
#         return obj
#     else:
#         return Domain(*obj)


# Path: rbf/pde/nodes.py
import logging
import numpy as np
from scipy.sparse import csc_matrix
from scipy.sparse.csgraph import reverse_cuthill_mckee
from rbf.utils import assert_shape, KDTree
from rbf.pde.domain import as_domain
from rbf.pde.sampling import rejection_sampling, poisson_discs


    if m is None:
        # this should be roughly equal to the stencil size for the RBF-FD
        # problem
        m = 5**nodes.shape[1]

    m = min(m, nodes.shape[0])
    # find the indices of the nearest m nodes for each node
    _, idx = KDTree(nodes).query(nodes, m)
    # efficiently form adjacency matrix
    col = idx.ravel()
    row = np.repeat(np.arange(nodes.shape[0]), m)
    data = np.ones(nodes.shape[0]*m, dtype=bool)
    mat = csc_matrix((data, (row, col)), dtype=bool)
    permutation = reverse_cuthill_mckee(mat)
    return permutation


def _check_spacing(nodes, rho=None):
    '''
    Check if any nodes are unusually close to eachother. If so, a warning will
    be printed.
    '''
    n, dim = nodes.shape

    if rho is None:
        def rho(x):
            return np.ones(x.shape[0])

    # distance to nearest neighbor

    dist = KDTree(nodes).query(nodes, 2)[0][:, 1]