1212
1313import matplotlib .pyplot as plt
1414import numpy as np
15+ from scipy .stats import qmc
1516
1617import optiland .backend as be
1718
@@ -375,6 +376,42 @@ def generate_points(self, num_points: int):
375376 self .y = be .sin (theta )
376377
377378
379+ class SobolDistribution (BaseDistribution ):
380+ """A class representing a Sobol distribution.
381+
382+ Generates `num_points` points using a Sobol low-discrepancy sequence
383+ within the unit disk.
384+
385+ Attributes:
386+ seed (int | None): Seed for the Sobol sequence generator.
387+ x: The x-coordinates of the generated points.
388+ y: The y-coordinates of the generated points.
389+ """
390+
391+ def __init__ (self , seed : int | None = None ):
392+ super ().__init__ ()
393+ self .seed = seed
394+
395+ def generate_points (self , num_points : int ):
396+ """Generates Sobol points.
397+
398+ Args:
399+ num_points (int): The number of points to generate.
400+
401+ """
402+ sampler = qmc .Sobol (d = 2 , scramble = True , seed = self .seed )
403+ sample = sampler .random (num_points )
404+
405+ u1 = be .array (sample [:, 0 ])
406+ u2 = be .array (sample [:, 1 ])
407+
408+ r = be .sqrt (u1 )
409+ theta = 2 * be .pi * u2
410+
411+ self .x = r * be .cos (theta )
412+ self .y = r * be .sin (theta )
413+
414+
378415def create_distribution (distribution_type : DistributionType ) -> BaseDistribution :
379416 """Create a distribution based on the given distribution type.
380417
@@ -400,6 +437,7 @@ def create_distribution(distribution_type: DistributionType) -> BaseDistribution
400437 "hexapolar" : HexagonalDistribution ,
401438 "cross" : CrossDistribution ,
402439 "ring" : RingDistribution ,
440+ "sobol" : SobolDistribution ,
403441 }
404442
405443 if distribution_type not in distribution_classes :
0 commit comments