diff --git a/pina/geometry/__init__.py b/pina/geometry/__init__.py index bbd1afc..61253db 100644 --- a/pina/geometry/__init__.py +++ b/pina/geometry/__init__.py @@ -2,11 +2,18 @@ __all__ = [ 'Location', 'CartesianDomain', 'EllipsoidDomain', - 'Union' + 'Union', + 'Intersection', + 'Exclusion', + 'Difference', + 'OperationInterface' ] from .location import Location from .cartesian import CartesianDomain from .ellipsoid import EllipsoidDomain -from .difference_domain import Difference +from .exclusion_domain import Exclusion +from .intersection_domain import Intersection from .union_domain import Union +from .difference_domain import Difference +from .operation_interface import OperationInterface diff --git a/pina/geometry/difference_domain.py b/pina/geometry/difference_domain.py index 52eda1b..4ede524 100644 --- a/pina/geometry/difference_domain.py +++ b/pina/geometry/difference_domain.py @@ -1,28 +1,88 @@ """Module for Location class.""" - -from .location import Location +import torch +from .exclusion_domain import Exclusion +from .operation_interface import OperationInterface from ..label_tensor import LabelTensor -class Difference(Location): - """ - """ +class Difference(OperationInterface): + """ PINA implementation of Difference of Domains.""" - def __init__(self, first, second): + def __init__(self, geometries): + """ PINA implementation of Difference of Domains. - self.first = first - self.second = second + :param list geometries: A list of geometries from 'pina.geometry' + such as 'EllipsoidDomain' or 'CartesianDomain'. The first + geometry in the list is the geometry from which points are + sampled. The rest of the geometries are the geometries that + are excluded from the first geometry to find the difference. + :Example: + # Create two ellipsoid domains + >>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]}) + >>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]}) + + # Create a Difference of the ellipsoid domains + >>> difference = Difference([ellipsoid1, ellipsoid2]) + """ + super().__init__(geometries) + + def is_inside(self, point, check_border=False): + for geometry in self.geometries[1:]: + if geometry.is_inside(point): + return False + return self.geometries[0].is_inside(point, check_border) + def sample(self, n, mode='random', variables='all'): - """ - """ - assert mode == 'random', 'Only random mode is implemented' + """Sample routine for difference domain. - samples = [] - while len(samples) < n: - sample = self.first.sample(1, 'random') - if not self.second.is_inside(sample): - samples.append(sample) + :param n: Number of points to sample in the shape. + :type n: int + :param mode: Mode for sampling, defaults to 'random'. + Available modes include: random sampling, 'random'. + :type mode: str, optional + :param variables: pinn variable to be sampled, defaults to 'all'. + :type variables: str or list[str], optional - import torch - return LabelTensor(torch.cat(samples), labels=['x', 'y']) + :Example: + # Create two Cartesian domains + >>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]}) + >>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]}) + + # Create a Difference of the ellipsoid domains + >>> difference = Difference([cartesian1, cartesian2]) + + >>> difference.sample(n=5) + LabelTensor([[0.8400, 0.9179], + [0.9154, 0.5769], + [1.7403, 0.4835], + [0.9545, 1.2851], + [1.3726, 0.9831]]) + + >>> len(difference.sample(n=5) + 5 + + """ + if mode != 'random': + raise NotImplementedError( + f'{mode} is not a valid mode for sampling.') + + sampled = [] + + # sample the points + while len(sampled) < n: + # get sample point from first geometry + point = self.geometries[0].sample(1, mode, variables) + is_inside = False + + # check if point is inside any other geometry + for geometry in self.geometries[1:]: + # if point is inside any other geometry, break + if geometry.is_inside(point): + is_inside = True + break + # if point is not inside any other geometry, add to sampled + if not is_inside: + sampled.append(point) + + return LabelTensor(torch.cat(sampled), labels=self.variables) diff --git a/pina/geometry/exclusion_domain.py b/pina/geometry/exclusion_domain.py new file mode 100644 index 0000000..2931b3f --- /dev/null +++ b/pina/geometry/exclusion_domain.py @@ -0,0 +1,102 @@ +"""Module for Location class.""" +import torch +from .location import Location +from ..utils import check_consistency +from ..label_tensor import LabelTensor +import random +from .operation_interface import OperationInterface + + +class Exclusion(OperationInterface): + """ PINA implementation of Exclusion of Domains.""" + + def __init__(self, geometries): + """ PINA implementation of Exclusion of Domains. + + :param list geometries: A list of geometries from 'pina.geometry' + such as 'EllipsoidDomain' or 'CartesianDomain'. + + :Example: + # Create two ellipsoid domains + >>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]}) + >>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]}) + + # Create a Exclusion of the ellipsoid domains + >>> exclusion = Exclusion([ellipsoid1, ellipsoid2]) + """ + super().__init__(geometries) + + def is_inside(self, point, check_border=False): + """Check if a point is inside the Exclusion domain. + + :param point: Point to be checked. + :type point: torch.Tensor + :param bool check_border: If True, the border is considered inside. + :return: True if the point is inside the Exclusion domain, False otherwise. + :rtype: bool + """ + flag = 0 + for geometry in self.geometries: + if geometry.is_inside(point, check_border): + flag += 1 + return flag == 1 + + def sample(self, n, mode='random', variables='all'): + """Sample routine for exclusion domain. + + :param n: Number of points to sample in the shape. + :type n: int + :param mode: Mode for sampling, defaults to 'random'. + Available modes include: random sampling, 'random'. + :type mode: str, optional + :param variables: pinn variable to be sampled, defaults to 'all'. + :type variables: str or list[str], optional + + :Example: + # Create two Cartesian domains + >>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]}) + >>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]}) + + # Create a Exclusion of the ellipsoid domains + >>> Exclusion = Exclusion([cartesian1, cartesian2]) + + >>> Exclusion.sample(n=5) + LabelTensor([[2.4187, 1.5792], + [2.7456, 2.3868], + [2.3830, 1.7037], + [0.8636, 1.8453], + [0.1978, 0.3526]]) + + >>> len(Exclusion.sample(n=5) + 5 + + """ + if mode != 'random': + raise NotImplementedError( + f'{mode} is not a valid mode for sampling.') + + sampled = [] + + # calculate the number of points to sample for each geometry and the remainder. + remainder = n % len(self.geometries) + num_points = n // len(self.geometries) + + # sample the points + # NB. geometries as shuffled since if we sample + # multiple times just one point, we would end + # up sampling only from the first geometry. + iter_ = random.sample(self.geometries, len(self.geometries)) + for i, geometry in enumerate(iter_): + sampled_points = [] + # int(i < remainder) is one only if we have a remainder + # different than zero. Notice that len(geometries) is + # always smaller than remaider. + # makes sure point is uniquely inside 1 shape. + while len(sampled_points) < (num_points + int(i < remainder)): + sample = geometry.sample(1, mode, variables) + # if not self.is_inside(sample) --> will be the intersection + if self.is_inside(sample): + sampled_points.append(sample) + sampled += sampled_points + + return LabelTensor(torch.cat(sampled), labels=self.variables) \ No newline at end of file diff --git a/pina/geometry/intersection_domain.py b/pina/geometry/intersection_domain.py new file mode 100644 index 0000000..ce53e8e --- /dev/null +++ b/pina/geometry/intersection_domain.py @@ -0,0 +1,102 @@ +"""Module for Location class.""" +import torch +from .exclusion_domain import Exclusion +from ..label_tensor import LabelTensor +from .operation_interface import OperationInterface +import random + + +class Intersection(OperationInterface): + """ PINA implementation of Intersection of Domains.""" + + def __init__(self, geometries): + """ PINA implementation of Intersection of Domains. + + :param list geometries: A list of geometries from 'pina.geometry' + such as 'EllipsoidDomain' or 'CartesianDomain'. The intersection + will be taken between all the geometries in the list. The resulting + geometry will be the intersection of all the geometries in the list. + + :Example: + # Create two ellipsoid domains + >>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]}) + >>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]}) + + # Create a Intersection of the ellipsoid domains + >>> intersection = Intersection([ellipsoid1, ellipsoid2]) + """ + super().__init__(geometries) + + def is_inside(self, point, check_border=False): + """Check if a point is inside the Exclusion domain. + + :param point: Point to be checked. + :type point: torch.Tensor + :param bool check_border: If True, the border is considered inside. + :return: True if the point is inside the Exclusion domain, False otherwise. + :rtype: bool + """ + flag = 0 + for geometry in self.geometries: + if geometry.is_inside(point, check_border): + flag += 1 + return flag == len(self.geometries) + + def sample(self, n, mode='random', variables='all'): + """Sample routine for intersection domain. + + :param n: Number of points to sample in the shape. + :type n: int + :param mode: Mode for sampling, defaults to 'random'. + Available modes include: random sampling, 'random'. + :type mode: str, optional + :param variables: pinn variable to be sampled, defaults to 'all'. + :type variables: str or list[str], optional + + :Example: + # Create two Cartesian domains + >>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]}) + >>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]}) + + # Create a Intersection of the ellipsoid domains + >>> intersection = Intersection([cartesian1, cartesian2]) + + >>> intersection.sample(n=5) + LabelTensor([[1.7697, 1.8654], + [1.2841, 1.1208], + [1.7289, 1.9843], + [1.3332, 1.2448], + [1.9902, 1.4458]]) + + >>> len(intersection.sample(n=5) + 5 + + """ + if mode != 'random': + raise NotImplementedError( + f'{mode} is not a valid mode for sampling.') + + sampled = [] + + # calculate the number of points to sample for each geometry and the remainder. + remainder = n % len(self.geometries) + num_points = n // len(self.geometries) + + # sample the points + # NB. geometries as shuffled since if we sample + # multiple times just one point, we would end + # up sampling only from the first geometry. + iter_ = random.sample(self.geometries, len(self.geometries)) + for i, geometry in enumerate(iter_): + sampled_points = [] + # int(i < remainder) is one only if we have a remainder + # different than zero. Notice that len(geometries) is + # always smaller than remaider. + # makes sure point is uniquely inside 1 shape. + while len(sampled_points) < (num_points + int(i < remainder)): + sample = geometry.sample(1, mode, variables) + if self.is_inside(sample): + sampled_points.append(sample) + sampled += sampled_points + + return LabelTensor(torch.cat(sampled), labels=self.variables) diff --git a/pina/geometry/location.py b/pina/geometry/location.py index 1b674d0..c91b89a 100644 --- a/pina/geometry/location.py +++ b/pina/geometry/location.py @@ -14,4 +14,17 @@ class Location(metaclass=ABCMeta): Abstract method for sampling a point from the location. To be implemented in the child class. """ - pass \ No newline at end of file + pass + + @abstractmethod + def is_inside(self, point, check_border=False): + """ + Abstract method for checking if a point is inside the location. To be + implemented in the child class. + + :param tensor point: A tensor point to be checked. + :param bool check_border: a boolean that determines whether the border + of the location is considered checked to be considered inside or + not. Defaults to False. + """ + pass diff --git a/pina/geometry/operation_interface.py b/pina/geometry/operation_interface.py new file mode 100644 index 0000000..53cc885 --- /dev/null +++ b/pina/geometry/operation_interface.py @@ -0,0 +1,53 @@ +import torch +from .location import Location +from ..utils import check_consistency +from ..label_tensor import LabelTensor +from abc import ABCMeta, abstractmethod +import random + + +class OperationInterface(Location, metaclass=ABCMeta): + def __init__(self, geometries): + """ + Abstract Operation class. + Any geometry operation entity must inherit from this class. + + :param list geometries: A list of geometries from 'pina.geometry' + such as 'EllipsoidDomain' or 'CartesianDomain'. + """ + # check consistency geometries + check_consistency(geometries, Location) + + # check we are passing always different + # geometries with the same labels. + self._check_dimensions(geometries) + + # assign geometries + self._geometries = geometries + + @property + def geometries(self): + """ + The geometries.""" + return self._geometries + + @property + def variables(self): + """ + Spatial variables. + + :return: All the variables defined in ``__init__`` in order. + :rtype: list[str] + """ + return self.geometries[0].variables + + def _check_dimensions(self, geometries): + """Check if the dimensions of the geometries are consistent. + + :param geometries: Geometries to be checked. + :type geometries: list[Location] + """ + for geometry in geometries: + if geometry.variables != geometries[0].variables: + raise NotImplementedError( + f'The geometries need to have same dimensions and labels.') diff --git a/pina/geometry/union_domain.py b/pina/geometry/union_domain.py index c77ea49..415b8b6 100644 --- a/pina/geometry/union_domain.py +++ b/pina/geometry/union_domain.py @@ -1,11 +1,12 @@ import torch from .location import Location +from .operation_interface import OperationInterface from ..utils import check_consistency from ..label_tensor import LabelTensor import random -class Union(Location): +class Union(OperationInterface): """ PINA implementation of Unions of Domains.""" def __init__(self, geometries): @@ -23,37 +24,7 @@ class Union(Location): >>> union = GeometryUnion([ellipsoid1, ellipsoid2]) """ - super().__init__() - - # union checks - check_consistency(geometries, Location) - self._check_union_dimensions(geometries) - - # assign geometries - self._geometries = geometries - - @property - def geometries(self): - """ - The geometries.""" - return self._geometries - - @property - def variables(self): - """ - Spatial variables. - - :return: All the spatial variables defined in '__init__()' in order. - :rtype: list[str] - """ - all_variables = [] - seen_variables = set() - for geometry in self.geometries: - for variable in geometry.variables: - if variable not in seen_variables: - all_variables.append(variable) - seen_variables.add(variable) - return all_variables + super().__init__(geometries) def is_inside(self, point, check_border=False): """Check if a point is inside the union domain. @@ -72,7 +43,7 @@ class Union(Location): return False def sample(self, n, mode='random', variables='all'): - """Sample routine. + """Sample routine for union domain. :param n: Number of points to sample in the shape. :type n: int @@ -84,23 +55,21 @@ class Union(Location): :Example: # Create two ellipsoid domains - >>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]}) - >>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]}) + >>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]}) + >>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]}) # Create a union of the ellipsoid domains - >>> union = Union([ellipsoid1, ellipsoid2]) + >>> union = Union([cartesian1, cartesian2]) - >>> union.sample(n=1000) - LabelTensor([[-0.2025, 0.0072], - [ 0.0358, 0.5748], - [ 0.5083, 0.0482], - ..., - [ 0.5857, 0.9279], - [ 1.1496, 1.7339], - [ 0.7650, 1.0469]]) + >>> union.sample(n=5) + LabelTensor([[1.2128, 2.1991], + [1.3530, 2.4317], + [2.2562, 1.6605], + [0.8451, 1.9878], + [1.8623, 0.7102]]) - >>> len(union.sample(n=1000) - 1000 + >>> len(union.sample(n=5) + 5 """ sampled_points = [] @@ -122,15 +91,4 @@ class Union(Location): if len(sampled_points) >= n: break - return LabelTensor(torch.cat(sampled_points), labels=[f'{i}' for i in self.variables]) - - def _check_union_dimensions(self, geometries): - """Check if the dimensions of the geometries are consistent. - - :param geometries: Geometries to be checked. - :type geometries: list[Location] - """ - for geometry in geometries: - if geometry.variables != geometries[0].variables: - raise NotImplementedError( - f'The geometries need to be the same dimensions. {geometry.variables} is not equal to {geometries[0].variables}') + return LabelTensor(torch.cat(sampled_points), labels=self.variables) diff --git a/tests/test_cartesian.py b/tests/test_geometry/test_cartesian.py similarity index 100% rename from tests/test_cartesian.py rename to tests/test_geometry/test_cartesian.py diff --git a/tests/test_geometry/test_difference.py b/tests/test_geometry/test_difference.py new file mode 100644 index 0000000..e174521 --- /dev/null +++ b/tests/test_geometry/test_difference.py @@ -0,0 +1,51 @@ +import torch + +from pina import LabelTensor +from pina.geometry import Difference, EllipsoidDomain, CartesianDomain + + +def test_constructor_two_CartesianDomains(): + Difference([CartesianDomain({'x': [0, 2], 'y': [0, 2]}), + CartesianDomain({'x': [1, 3], 'y': [1, 3]})]) + + +def test_constructor_two_3DCartesianDomain(): + Difference([CartesianDomain({'x': [0, 2], 'y': [0, 2], 'z': [0, 2]}), + CartesianDomain({'x': [1, 3], 'y': [1, 3], 'z': [1, 3]})]) + + +def test_constructor_three_CartesianDomains(): + Difference([CartesianDomain({'x': [0, 2], 'y': [0, 2]}), CartesianDomain( + {'x': [1, 3], 'y': [1, 3]}), CartesianDomain({'x': [2, 4], 'y': [2, 4]})]) + + +def test_is_inside_two_CartesianDomains(): + pt_1 = LabelTensor(torch.tensor([[0.5, 0.5]]), ['x', 'y']) + pt_2 = LabelTensor(torch.tensor([[-1, -0.5]]), ['x', 'y']) + domain = Difference([CartesianDomain({'x': [0, 2], 'y': [0, 2]}), + CartesianDomain({'x': [1, 3], 'y': [1, 3]})]) + assert domain.is_inside(pt_1) == True + assert domain.is_inside(pt_2) == False + + +def test_is_inside_two_3DCartesianDomain(): + pt_1 = LabelTensor(torch.tensor([[0.5, 0.5, 0.5]]), ['x', 'y', 'z']) + pt_2 = LabelTensor(torch.tensor([[-1, -0.5, -0.5]]), ['x', 'y', 'z']) + domain = Difference([CartesianDomain({'x': [0, 2], 'y': [0, 2], 'z': [ + 0, 2]}), CartesianDomain({'x': [1, 3], 'y': [1, 3], 'z': [1, 3]})]) + assert domain.is_inside(pt_1) == True + assert domain.is_inside(pt_2) == False + + +def test_sample(): + n = 100 + domain = Difference([EllipsoidDomain( + {'x': [-1, 1], 'y': [-1, 1]}), CartesianDomain({'x': [-0.5, 0.5], 'y': [-0.5, 0.5]})]) + pts = domain.sample(n) + assert isinstance(pts, LabelTensor) + assert pts.shape[0] == n + + n = 105 + pts = domain.sample(n) + assert isinstance(pts, LabelTensor) + assert pts.shape[0] == n diff --git a/tests/test_ellipsoid.py b/tests/test_geometry/test_ellipsoid.py similarity index 100% rename from tests/test_ellipsoid.py rename to tests/test_geometry/test_ellipsoid.py diff --git a/tests/test_geometry/test_exclusion.py b/tests/test_geometry/test_exclusion.py new file mode 100644 index 0000000..2ae53f3 --- /dev/null +++ b/tests/test_geometry/test_exclusion.py @@ -0,0 +1,51 @@ +import torch + +from pina import LabelTensor +from pina.geometry import Exclusion, EllipsoidDomain, CartesianDomain + + +def test_constructor_two_CartesianDomains(): + Exclusion([CartesianDomain({'x': [0, 2], 'y': [0, 2]}), + CartesianDomain({'x': [1, 3], 'y': [1, 3]})]) + + +def test_constructor_two_3DCartesianDomain(): + Exclusion([CartesianDomain({'x': [0, 2], 'y': [0, 2], 'z': [0, 2]}), + CartesianDomain({'x': [1, 3], 'y': [1, 3], 'z': [1, 3]})]) + + +def test_constructor_three_CartesianDomains(): + Exclusion([CartesianDomain({'x': [0, 2], 'y': [0, 2]}), CartesianDomain( + {'x': [1, 3], 'y': [1, 3]}), CartesianDomain({'x': [2, 4], 'y': [2, 4]})]) + + +def test_is_inside_two_CartesianDomains(): + pt_1 = LabelTensor(torch.tensor([[0.5, 0.5]]), ['x', 'y']) + pt_2 = LabelTensor(torch.tensor([[-1, -0.5]]), ['x', 'y']) + domain = Exclusion([CartesianDomain({'x': [0, 2], 'y': [0, 2]}), + CartesianDomain({'x': [1, 3], 'y': [1, 3]})]) + assert domain.is_inside(pt_1) == True + assert domain.is_inside(pt_2) == False + + +def test_is_inside_two_3DCartesianDomain(): + pt_1 = LabelTensor(torch.tensor([[0.5, 0.5, 0.5]]), ['x', 'y', 'z']) + pt_2 = LabelTensor(torch.tensor([[-1, -0.5, -0.5]]), ['x', 'y', 'z']) + domain = Exclusion([CartesianDomain({'x': [0, 2], 'y': [0, 2], 'z': [ + 0, 2]}), CartesianDomain({'x': [1, 3], 'y': [1, 3], 'z': [1, 3]})]) + assert domain.is_inside(pt_1) == True + assert domain.is_inside(pt_2) == False + + +def test_sample(): + n = 100 + domain = Exclusion([EllipsoidDomain( + {'x': [-1, 1], 'y': [-1, 1]}), CartesianDomain({'x': [0.3, 1.5], 'y': [0.3, 1.5]})]) + pts = domain.sample(n) + assert isinstance(pts, LabelTensor) + assert pts.shape[0] == n + + n = 105 + pts = domain.sample(n) + assert isinstance(pts, LabelTensor) + assert pts.shape[0] == n diff --git a/tests/test_geometry/test_intersection.py b/tests/test_geometry/test_intersection.py new file mode 100644 index 0000000..1c5cd81 --- /dev/null +++ b/tests/test_geometry/test_intersection.py @@ -0,0 +1,56 @@ +import torch + +from pina import LabelTensor +from pina.geometry import Intersection, EllipsoidDomain, CartesianDomain + + +def test_constructor_two_CartesianDomains(): + Intersection([CartesianDomain({'x': [0, 2], 'y': [0, 2]}), + CartesianDomain({'x': [1, 3], 'y': [1, 3]})]) + + +def test_constructor_two_3DCartesianDomain(): + Intersection([CartesianDomain({'x': [0, 2], 'y': [0, 2], 'z': [0, 2]}), + CartesianDomain({'x': [1, 3], 'y': [1, 3], 'z': [1, 3]})]) + + +def test_constructor_three_CartesianDomains(): + Intersection([CartesianDomain({'x': [0, 2], 'y': [0, 2]}), CartesianDomain( + {'x': [1, 3], 'y': [1, 3]}), CartesianDomain({'x': [2, 4], 'y': [2, 4]})]) + + +def test_is_inside_two_CartesianDomains(): + pt_1 = LabelTensor(torch.tensor([[0.5, 0.5]]), ['x', 'y']) + pt_2 = LabelTensor(torch.tensor([[-1, -0.5]]), ['x', 'y']) + pt_3 = LabelTensor(torch.tensor([[1.5, 1.5]]), ['x', 'y']) + + domain = Intersection([CartesianDomain({'x': [0, 2], 'y': [0, 2]}), + CartesianDomain({'x': [1, 3], 'y': [1, 3]})]) + assert domain.is_inside(pt_1) == False + assert domain.is_inside(pt_2) == False + assert domain.is_inside(pt_3) == True + + +def test_is_inside_two_3DCartesianDomain(): + pt_1 = LabelTensor(torch.tensor([[0.5, 0.5, 0.5]]), ['x', 'y', 'z']) + pt_2 = LabelTensor(torch.tensor([[-1, -0.5, -0.5]]), ['x', 'y', 'z']) + pt_3 = LabelTensor(torch.tensor([[1.5, 1.5, 1.5]]), ['x', 'y', 'z']) + domain = Intersection([CartesianDomain({'x': [0, 2], 'y': [0, 2], 'z': [ + 0, 2]}), CartesianDomain({'x': [1, 3], 'y': [1, 3], 'z': [1, 3]})]) + assert domain.is_inside(pt_1) == False + assert domain.is_inside(pt_2) == False + assert domain.is_inside(pt_3) == True + + +def test_sample(): + n = 100 + domain = Intersection([EllipsoidDomain( + {'x': [-1, 1], 'y': [-1, 1]}), CartesianDomain({'x': [-0.5, 0.5], 'y': [-0.5, 0.5]})]) + pts = domain.sample(n) + assert isinstance(pts, LabelTensor) + assert pts.shape[0] == n + + n = 105 + pts = domain.sample(n) + assert isinstance(pts, LabelTensor) + assert pts.shape[0] == n diff --git a/tests/test_union.py b/tests/test_geometry/test_union.py similarity index 98% rename from tests/test_union.py rename to tests/test_geometry/test_union.py index 65e3087..07787a3 100644 --- a/tests/test_union.py +++ b/tests/test_geometry/test_union.py @@ -45,6 +45,7 @@ def test_is_inside_EllipsoidDomain_CartesianDomain(): assert domain.is_inside(pt_1) == True assert domain.is_inside(pt_2) == False + def test_sample(): n = 100 domain = Union([EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]}), @@ -56,4 +57,4 @@ def test_sample(): n = 105 pts = domain.sample(n) assert isinstance(pts, LabelTensor) - assert pts.shape[0] == n \ No newline at end of file + assert pts.shape[0] == n