MCPcopy Index your code
hub / github.com/apple/ml-pointersect / fit_hyperplane

Function fit_hyperplane

plib/utils.py:1822–1892  ·  view source on GitHub ↗

Fit a hyperplane for the n points such that the plane minimizes the point-to-plane distances. .. math:: \min_{n,c} \sum_i w_i (n^T * (p_i - c))^2, where :math:`n` is the normal of the hyperplane, :math:`c` is the anchor of the plane. Args: points: (*

(
        points: torch.Tensor,  # (*, n, d)
        centers: torch.Tensor = None,  # (*, d)
        weights: torch.Tensor = None,  # (*, n)
        th_eig_val: float = 1.e-3,
)

Source from the content-addressed store, hash-verified

1820
1821
1822def fit_hyperplane(
1823 points: torch.Tensor, # (*, n, d)
1824 centers: torch.Tensor = None, # (*, d)
1825 weights: torch.Tensor = None, # (*, n)
1826 th_eig_val: float = 1.e-3,
1827) -> T.Dict[str, T.Any]:
1828 """
1829 Fit a hyperplane for the n points such that the plane minimizes the point-to-plane distances.
1830
1831 .. math::
1832
1833 \min_{n,c} \sum_i w_i (n^T * (p_i - c))^2,
1834
1835 where :math:`n` is the normal of the hyperplane, :math:`c` is the anchor of the plane.
1836
1837 Args:
1838 points:
1839 (*, n, d) the points in d-dimensinoal space
1840 centers:
1841 (*, d) the given anchors of the hyperplanes
1842 weights:
1843 (*, n) the weight to each points. If None, all points have unit weights.
1844 # ray_origins:
1845 # (*, d) the ray origins. If given, we will also compute the intersection of the ray on the plane.
1846 # ray_directions:
1847 # (*, d) the ray directions. If given, we will also compute the intersection of the ray on the plane.
1848
1849 Returns:
1850 plane_normals:
1851 (*, d) Note that the plane normal can points to one of the two directions
1852 centers:
1853 (*, d)
1854 ts:
1855 (*,) or None. ts on the ray to the plane.
1856
1857 """
1858 *p_shape, d = points.shape
1859 if weights is None:
1860 weights = torch.ones(*p_shape, 1, dtype=points.dtype, device=points.device) # (*, n, 1)
1861 else:
1862 weights = weights.unsqueeze(-1) # (*, n, 1)
1863
1864 if centers is None:
1865 centers = (points * weights).sum(dim=-2) / weights.sum(dim=-2) # (*, d)
1866
1867 points_centered = points - centers.unsqueeze(-2) # (*, n, d)
1868 PTP = (weights * points_centered).transpose(-1, -2) @ points_centered # (*, d, d)
1869
1870 # we will make sure PTP is at least rank 2 (at least two points not on the same line)
1871 with torch.no_grad():
1872 eig_vals, eig_vecs = torch.linalg.eigh(PTP) # eig_vecs: (*, d, d), eig_vals: (*, d) small to large
1873 valid_mask = (eig_vals[..., -2] > th_eig_val) # (*,)
1874 valid_mask = torch.logical_and(
1875 valid_mask,
1876 (eig_vals[..., 1] - eig_vals[..., 0]) > th_eig_val,
1877 ) # (*,)
1878
1879 # fill PTP with full rank matrix if invalid

Callers

nothing calls this directly

Calls 1

masked_fillMethod · 0.80

Tested by

no test coverage detected