def sample(self, num, d=1, rng=np.random, ntm=Sobol()): if d == 1: return super(ScatteredHypersphere, self).sample(num, d, rng) if self.surface: cube = ntm.sample(num, d-1) radius = 1.0 else: dcube = ntm.sample(num, d) cube, radius = dcube[:, :-1], dcube[:, -1:] ** (1.0 / d) # inverse transform method (section 1.5.2) for j in range(d-1): cube[:, j] = SphericalCoords(d-1-j).ppf(cube[:, j]) # spherical coordinate transform mapped = np.ones((num, d)) i = np.ones(d-1) i[-1] = 2.0 s = np.sin(i[None, :] * np.pi * cube) c = np.cos(i[None, :] * np.pi * cube) mapped[:, 1:] = np.cumprod(s, axis=1) mapped[:, :-1] *= c # radius adjustment for ball versus sphere, and rotate rotation = random_orthogonal(d, rng=rng) return np.dot(mapped * radius, rotation)