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,
)
| 1820 | |
| 1821 | |
| 1822 | def 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 |
nothing calls this directly
no test coverage detected