fix doc domain

This commit is contained in:
giovanni
2025-03-12 15:27:06 +01:00
committed by FilippoOlivo
parent a76c1cda96
commit 06e6c4763d
10 changed files with 329 additions and 325 deletions

View File

@@ -1,6 +1,4 @@
""" """Modules to create and handle domains."""
This module contains the domain classes.
"""
__all__ = [ __all__ = [
"DomainInterface", "DomainInterface",

View File

@@ -1,4 +1,4 @@
"""Module for CartesianDomain class.""" """Module for the Cartesian Domain."""
import torch import torch
@@ -14,7 +14,7 @@ class CartesianDomain(DomainInterface):
def __init__(self, cartesian_dict): def __init__(self, cartesian_dict):
""" """
Initialize the :class:`~pina.domain.CartesianDomain` class. Initialization of the :class:`CartesianDomain` class.
:param dict cartesian_dict: A dictionary where the keys are the :param dict cartesian_dict: A dictionary where the keys are the
variable names and the values are the domain extrema. The domain variable names and the values are the domain extrema. The domain
@@ -58,11 +58,10 @@ class CartesianDomain(DomainInterface):
def update(self, new_domain): def update(self, new_domain):
""" """
Add new dimensions to an existing :class:`~pina.domain.CartesianDomain` Add new dimensions to an existing :class:`CartesianDomain` object.
object.
:param :class:`~pina.domain.CartesianDomain` new_domain: New domain to :param CartesianDomain new_domain: New domain to be added to an existing
be added to an existing :class:`~pina.domain.CartesianDomain` object. :class:`CartesianDomain` object.
:Example: :Example:
>>> spatial_domain = CartesianDomain({'x': [0, 1], 'y': [0, 1]}) >>> spatial_domain = CartesianDomain({'x': [0, 1], 'y': [0, 1]})
@@ -114,8 +113,7 @@ class CartesianDomain(DomainInterface):
Available modes: random sampling, ``random``; Available modes: random sampling, ``random``;
latin hypercube sampling, ``latin`` or ``lh``; latin hypercube sampling, ``latin`` or ``lh``;
chebyshev sampling, ``chebyshev``; grid sampling ``grid``. chebyshev sampling, ``chebyshev``; grid sampling ``grid``.
:param variables: variables to be sampled. Default is ``all``. :param list[str] variables: variables to be sampled. Default is ``all``.
:type variables: str | list[str]
:return: Sampled points. :return: Sampled points.
:rtype: LabelTensor :rtype: LabelTensor
@@ -165,8 +163,7 @@ class CartesianDomain(DomainInterface):
:param int n: Number of points to sample. :param int n: Number of points to sample.
:param str mode: Sampling method. :param str mode: Sampling method.
:param variables: variables to be sampled. :param list[str] variables: variables to be sampled.
:type variables: str | list[str]
:return: Sampled points. :return: Sampled points.
:rtype: list[LabelTensor] :rtype: list[LabelTensor]
""" """
@@ -203,8 +200,7 @@ class CartesianDomain(DomainInterface):
:param int n: Number of points to sample. :param int n: Number of points to sample.
:param str mode: Sampling method. :param str mode: Sampling method.
:param variables: variables to be sampled. :param list[str] variables: variables to be sampled.
:type variables: str | list[str]
:return: Sampled points. :return: Sampled points.
:rtype: list[LabelTensor] :rtype: list[LabelTensor]
""" """
@@ -232,8 +228,7 @@ class CartesianDomain(DomainInterface):
Sample a single point in one dimension. Sample a single point in one dimension.
:param int n: Number of points to sample. :param int n: Number of points to sample.
:param variables: variables to be sampled. :param list[str] variables: variables to be sampled.
:type variables: str | list[str]
:return: Sampled points. :return: Sampled points.
:rtype: list[torch.Tensor] :rtype: list[torch.Tensor]
""" """
@@ -273,8 +268,8 @@ class CartesianDomain(DomainInterface):
Check if a point is inside the hypercube. Check if a point is inside the hypercube.
:param LabelTensor point: Point to be checked. :param LabelTensor point: Point to be checked.
:param bool check_border: Determines whether to check if the point lies :param bool check_border: If ``True``, the border is considered inside
on the boundary of the hypercube. Default is ``False``. the hypercube. Default is ``False``.
:return: ``True`` if the point is inside the domain, ``False`` otherwise. :return: ``True`` if the point is inside the domain, ``False`` otherwise.
:rtype: bool :rtype: bool
""" """

View File

@@ -1,4 +1,4 @@
"""Module for Difference class.""" """Module for the Difference Operation."""
import torch import torch
from .operation_interface import OperationInterface from .operation_interface import OperationInterface
@@ -6,45 +6,46 @@ from ..label_tensor import LabelTensor
class Difference(OperationInterface): class Difference(OperationInterface):
""" r"""
PINA implementation of Difference of Domains. Implementation of the difference operation between of a list of domains.
Given two sets :math:`A` and :math:`B`, define the difference of the two
sets as:
.. math::
A - B = \{x \mid x \in A \land x \not\in B\},
where :math:`x` is a point in :math:`\mathbb{R}^N`.
""" """
def __init__(self, geometries): def __init__(self, geometries):
r""" """
Given two sets :math:`A` and :math:`B` then the Initialization of the :class:`Difference` class.
domain difference is defined as:
.. math:: :param list[DomainInterface] geometries: A list of instances of the
A - B = \{x \mid x \in A \land x \not\in B\}, :class:`~pina.domain.DomainInterface` class on which the difference
operation is performed. The first domain in the list serves as the
with :math:`x` a point in :math:`\mathbb{R}^N` and :math:`N` base from which points are sampled, while the remaining domains
the dimension of the geometry space. define the regions to be excluded from the base domain to compute
the difference.
: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: :Example:
>>> # Create two ellipsoid domains >>> # Create two ellipsoid domains
>>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]}) >>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]})
>>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]}) >>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]})
>>> # Create a Difference of the ellipsoid domains >>> # Define the difference between the domains
>>> difference = Difference([ellipsoid1, ellipsoid2]) >>> difference = Difference([ellipsoid1, ellipsoid2])
""" """
super().__init__(geometries) super().__init__(geometries)
def is_inside(self, point, check_border=False): def is_inside(self, point, check_border=False):
""" """
Check if a point is inside the ``Difference`` domain. Check if a point is inside the resulting domain.
:param point: Point to be checked. :param LabelTensor point: Point to be checked.
:type point: torch.Tensor :param bool check_border: If ``True``, the border is considered inside
:param bool check_border: If ``True``, the border is considered inside. the domain. Default is ``False``.
:return: ``True`` if the point is inside the Exclusion domain, :return: ``True`` if the point is inside the domain, ``False`` otherwise.
``False`` otherwise.
:rtype: bool :rtype: bool
""" """
for geometry in self.geometries[1:]: for geometry in self.geometries[1:]:
@@ -54,21 +55,21 @@ class Difference(OperationInterface):
def sample(self, n, mode="random", variables="all"): def sample(self, n, mode="random", variables="all"):
""" """
Sample routine for ``Difference`` domain. Sampling routine.
:param int n: Number of points to sample in the shape. :param int n: Number of points to sample.
:param str mode: Mode for sampling, defaults to ``random``. Available :param str mode: Sampling method. Default is ``random``.
modes include: ``random``. Available modes: random sampling, ``random``;
:param variables: Variables to be sampled, defaults to ``all``. :param list[str] variables: variables to be sampled. Default is ``all``.
:type variables: str | list[str] :raises NotImplementedError: If the sampling method is not implemented.
:return: Returns ``LabelTensor`` of n sampled points. :return: Sampled points.
:rtype: LabelTensor :rtype: LabelTensor
:Example: :Example:
>>> # Create two Cartesian domains >>> # Create two Cartesian domains
>>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]}) >>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]})
>>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]}) >>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]})
>>> # Create a Difference of the ellipsoid domains >>> # Define the difference between the domains
>>> difference = Difference([cartesian1, cartesian2]) >>> difference = Difference([cartesian1, cartesian2])
>>> # Sampling >>> # Sampling
>>> difference.sample(n=5) >>> difference.sample(n=5)

View File

@@ -1,12 +1,12 @@
"""Module for the DomainInterface class.""" """Module for the Domain Interface."""
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
class DomainInterface(metaclass=ABCMeta): class DomainInterface(metaclass=ABCMeta):
""" """
Abstract Location class. Abstract base class for geometric domains. All specific domain types should
Any geometry entity should inherit from this class. inherit from this class.
""" """
available_sampling_modes = ["random", "grid", "lh", "chebyshev", "latin"] available_sampling_modes = ["random", "grid", "lh", "chebyshev", "latin"]
@@ -15,20 +15,24 @@ class DomainInterface(metaclass=ABCMeta):
@abstractmethod @abstractmethod
def sample_modes(self): def sample_modes(self):
""" """
Abstract method returing available samples modes for the Domain. Abstract method defining sampling methods.
""" """
@property @property
@abstractmethod @abstractmethod
def variables(self): def variables(self):
""" """
Abstract method returing Domain variables. Abstract method returning the domain variables.
""" """
@sample_modes.setter @sample_modes.setter
def sample_modes(self, values): def sample_modes(self, values):
""" """
TODO Setter for the sample_modes property.
:param values: Sampling modes to be set.
:type values: str | list[str]
:raises TypeError: Invalid sampling mode.
""" """
if not isinstance(values, (list, tuple)): if not isinstance(values, (list, tuple)):
values = [values] values = [values]
@@ -43,18 +47,15 @@ class DomainInterface(metaclass=ABCMeta):
@abstractmethod @abstractmethod
def sample(self): def sample(self):
""" """
Abstract method for sampling a point from the location. To be Abstract method for the sampling routine.
implemented in the child class.
""" """
@abstractmethod @abstractmethod
def is_inside(self, point, check_border=False): def is_inside(self, point, check_border=False):
""" """
Abstract method for checking if a point is inside the location. To be Abstract method for checking if a point is inside the domain.
implemented in the child class.
:param torch.Tensor point: A tensor point to be checked. :param LabelTensor point: Point to be checked.
:param bool check_border: A boolean that determines whether the border :param bool check_border: If ``True``, the border is considered inside
of the location is considered checked to be considered inside or the domain. Default is ``False``.
not. Defaults to ``False``.
""" """

View File

@@ -1,6 +1,4 @@
""" """Module for the Ellipsoid Domain."""
Module for the Ellipsoid domain.
"""
import torch import torch
from .domain_interface import DomainInterface from .domain_interface import DomainInterface
@@ -9,34 +7,35 @@ from ..utils import check_consistency
class EllipsoidDomain(DomainInterface): class EllipsoidDomain(DomainInterface):
"""PINA implementation of Ellipsoid domain.""" """
Implementation of the ellipsoid domain.
"""
def __init__(self, ellipsoid_dict, sample_surface=False): def __init__(self, ellipsoid_dict, sample_surface=False):
"""PINA implementation of Ellipsoid domain. """
Initialization of the :class:`EllipsoidDomain` class.
:param ellipsoid_dict: A dictionary with dict-key a string representing :param dict ellipsoid_dict: A dictionary where the keys are the variable
the input variables for the pinn, and dict-value a list with names and the values are the domain extrema.
the domain extrema. :param bool sample_surface: A flag to choose the sampling strategy.
:type ellipsoid_dict: dict If ``True``, samples are taken only from the surface of the ellipsoid.
:param sample_surface: A variable for choosing sample strategies. If If ``False``, samples are taken from the interior of the ellipsoid.
``sample_surface=True`` only samples on the ellipsoid surface Default is ``False``.
frontier are taken. If ``sample_surface=False`` only samples on :raises TypeError: If the input dictionary is not correctly formatted.
the ellipsoid interior are taken, defaults to ``False``.
:type sample_surface: bool
.. warning:: .. warning::
Sampling for dimensions greater or equal to 10 could result Sampling for dimensions greater or equal to 10 could result in a
in a shrinking of the ellipsoid, which degrades the quality shrinkage of the ellipsoid, which degrades the quality of the
of the samples. For dimensions higher than 10, other algorithms samples. For dimensions higher than 10, use other sampling algorithms.
for sampling should be used, such as: Dezert, Jean, and Christian .. seealso::
Musso. "An efficient method for generating points uniformly **Original reference**: Dezert, Jean, and Musso, Christian.
distributed in hyperellipsoids." Proceedings of the Workshop on *An efficient method for generating points uniformly distributed
Estimation, Tracking and Fusion: A Tribute to Yaakov Bar-Shalom. in hyperellipsoids.*
Vol. 7. No. 8. 2001. Proceedings of the Workshop on Estimation, Tracking and Fusion:
A Tribute to Yaakov Bar-Shalom. 2001.
:Example: :Example:
>>> spatial_domain = Ellipsoid({'x':[-1, 1], 'y':[-1,1]}) >>> spatial_domain = Ellipsoid({'x':[-1, 1], 'y':[-1,1]})
""" """
self.fixed_ = {} self.fixed_ = {}
self.range_ = {} self.range_ = {}
@@ -75,34 +74,40 @@ class EllipsoidDomain(DomainInterface):
@property @property
def sample_modes(self): def sample_modes(self):
"""
List of available sampling modes.
:return: List of available sampling modes.
:rtype: list[str]
"""
return ["random"] return ["random"]
@property @property
def variables(self): def variables(self):
"""Spatial variables. """
List of variables of the domain.
:return: Spatial variables defined in '__init__()' :return: List of variables of the domain.
:rtype: list[str] :rtype: list[str]
""" """
return sorted(list(self.fixed_.keys()) + list(self.range_.keys())) return sorted(list(self.fixed_.keys()) + list(self.range_.keys()))
def is_inside(self, point, check_border=False): def is_inside(self, point, check_border=False):
"""Check if a point is inside the ellipsoid domain. """
Check if a point is inside the ellipsoid.
:param LabelTensor point: Point to be checked.
:param bool check_border: If ``True``, the border is considered inside
the ellipsoid. Default is ``False``.
:raises ValueError: If the labels of the point are different from those
passed in the ``__init__`` method.
:return: ``True`` if the point is inside the domain, ``False`` otherwise.
:rtype: bool
.. note:: .. note::
When ``sample_surface`` in the ``__init()__`` When ``sample_surface=True`` in the ``__init__`` method, this method
is set to ``True``, then the method only checks checks only those points lying on the surface of the ellipsoid.
points on the surface, and not inside the domain.
:param point: Point to be checked.
:type point: LabelTensor
:param check_border: Check if the point is also on the frontier
of the ellipsoid, default ``False``.
:type check_border: bool
:return: Returning True if the point is inside, ``False`` otherwise.
:rtype: bool
""" """
# small check that point is labeltensor # small check that point is labeltensor
check_consistency(point, LabelTensor) check_consistency(point, LabelTensor)
@@ -142,15 +147,13 @@ class EllipsoidDomain(DomainInterface):
return bool(eqn < 0) return bool(eqn < 0)
def _sample_range(self, n, mode, variables): def _sample_range(self, n, mode, variables):
"""Rescale the samples to the correct bounds. """"""
"""
Rescale the samples to fit within the specified bounds.
:param n: Number of points to sample in the ellipsoid. :param int n: Number of points to sample.
:type n: int :param str mode: Sampling method. Default is ``random``.
:param mode: Mode for sampling, defaults to ``random``. :param list[str] variables: variables whose samples must be rescaled.
Available modes include: random sampling, ``random``.
:type mode: str, optional
:param variables: Variables to be rescaled in the samples.
:type variables: torch.Tensor
:return: Rescaled sample points. :return: Rescaled sample points.
:rtype: torch.Tensor :rtype: torch.Tensor
""" """
@@ -202,19 +205,20 @@ class EllipsoidDomain(DomainInterface):
return pts return pts
def sample(self, n, mode="random", variables="all"): def sample(self, n, mode="random", variables="all"):
"""Sample routine. """
Sampling routine.
:param int n: Number of points to sample in the shape. :param int n: Number of points to sample.
:param str mode: Mode for sampling, defaults to ``random``. :param str mode: Sampling method. Default is ``random``.
Available modes include: ``random``. Available modes: random sampling, ``random``.
:param variables: Variables to be sampled, defaults to ``all``. :param list[str] variables: variables to be sampled. Default is ``all``.
:type variables: str | list[str] :raises NotImplementedError: If the sampling mode is not implemented.
:return: Returns ``LabelTensor`` of n sampled points. :return: Sampled points.
:rtype: LabelTensor :rtype: LabelTensor
:Example: :Example:
>>> elips = Ellipsoid({'x':[1, 0], 'y':1}) >>> ellipsoid = Ellipsoid({'x':[1, 0], 'y':1})
>>> elips.sample(n=6) >>> ellipsoid.sample(n=6)
tensor([[0.4872, 1.0000], tensor([[0.4872, 1.0000],
[0.2977, 1.0000], [0.2977, 1.0000],
[0.0422, 1.0000], [0.0422, 1.0000],
@@ -224,19 +228,14 @@ class EllipsoidDomain(DomainInterface):
""" """
def _Nd_sampler(n, mode, variables): def _Nd_sampler(n, mode, variables):
"""Sample all the variables together """
Sample all variables together.
:param n: Number of points to sample. :param int n: Number of points to sample.
:type n: int :param str mode: Sampling method.
:param mode: Mode for sampling, defaults to ``random``. :param list[str] variables: variables to be sampled.
Available modes include: random sampling, ``random``; :return: Sampled points.
latin hypercube sampling, 'latin' or 'lh'; :rtype: list[LabelTensor]
chebyshev sampling, 'chebyshev'; grid sampling 'grid'.
:type mode: str, optional.
:param variables: pinn variable to be sampled, defaults to ``all``.
:type variables: str or list[str], optional.
:return: Sample points.
:rtype: list[torch.Tensor]
""" """
pairs = [(k, v) for k, v in self.range_.items() if k in variables] pairs = [(k, v) for k, v in self.range_.items() if k in variables]
keys, _ = map(list, zip(*pairs)) keys, _ = map(list, zip(*pairs))
@@ -258,13 +257,12 @@ class EllipsoidDomain(DomainInterface):
return result return result
def _single_points_sample(n, variables): def _single_points_sample(n, variables):
"""Sample a single point in one dimension. """
Sample a single point in one dimension.
:param n: Number of points to sample. :param int n: Number of points to sample.
:type n: int :param list[str] variables: variables to be sampled.
:param variables: Variables to sample from. :return: Sampled points.
:type variables: list[str]
:return: Sample points.
:rtype: list[torch.Tensor] :rtype: list[torch.Tensor]
""" """
tmp = [] tmp = []

View File

@@ -1,4 +1,4 @@
"""Module for Exclusion class.""" """Module for the Exclusion Operation."""
import random import random
import torch import torch
@@ -7,43 +7,44 @@ from .operation_interface import OperationInterface
class Exclusion(OperationInterface): class Exclusion(OperationInterface):
""" r"""
PINA implementation of Exclusion of Domains. Implementation of the exclusion operation between of a list of domains.
Given two sets :math:`A` and :math:`B`, define the exclusion of the two
sets as:
.. math::
A \setminus B = \{x \mid x \in A \land x \in B \land
x \not\in(A \lor B)\},
where :math:`x` is a point in :math:`\mathbb{R}^N`.
""" """
def __init__(self, geometries): def __init__(self, geometries):
r""" """
Given two sets :math:`A` and :math:`B` then the Initialization of the :class:`Exclusion` class.
domain difference is defined as:
.. math:: :param list[DomainInterface] geometries: A list of instances of the
A \setminus B = \{x \mid x \in A \land x \in B \land :class:`~pina.domain.DomainInterface` class on which the exclusion
x \not\in(A \lor B)\}, operation is performed.
with :math:`x` a point in :math:`\mathbb{R}^N` and :math:`N`
the dimension of the geometry space.
:param list geometries: A list of geometries from ``pina.geometry``
such as ``EllipsoidDomain`` or ``CartesianDomain``.
:Example: :Example:
>>> # Create two ellipsoid domains >>> # Create two ellipsoid domains
>>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]}) >>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]})
>>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]}) >>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]})
>>> # Create a Exclusion of the ellipsoid domains >>> # Define the exclusion between the domains
>>> exclusion = Exclusion([ellipsoid1, ellipsoid2]) >>> exclusion = Exclusion([ellipsoid1, ellipsoid2])
""" """
super().__init__(geometries) super().__init__(geometries)
def is_inside(self, point, check_border=False): def is_inside(self, point, check_border=False):
""" """
Check if a point is inside the ``Exclusion`` domain. Check if a point is inside the resulting domain.
:param point: Point to be checked. :param LabelTensor point: Point to be checked.
:type point: torch.Tensor :param bool check_border: If ``True``, the border is considered inside
:param bool check_border: If ``True``, the border is considered inside. the domain. Default is ``False``.
:return: ``True`` if the point is inside the Exclusion domain, :return: ``True`` if the point is inside the domain, ``False`` otherwise.
``False`` otherwise.
:rtype: bool :rtype: bool
""" """
flag = 0 flag = 0
@@ -54,21 +55,21 @@ class Exclusion(OperationInterface):
def sample(self, n, mode="random", variables="all"): def sample(self, n, mode="random", variables="all"):
""" """
Sample routine for ``Exclusion`` domain. Sampling routine.
:param int n: Number of points to sample in the shape. :param int n: Number of points to sample.
:param str mode: Mode for sampling, defaults to ``random``. Available :param str mode: Sampling method. Default is ``random``.
modes include: ``random``. Available modes: random sampling, ``random``;
:param variables: Variables to be sampled, defaults to ``all``. :param list[str] variables: variables to be sampled. Default is ``all``.
:type variables: str | list[str] :raises NotImplementedError: If the sampling method is not implemented.
:return: Returns ``LabelTensor`` of n sampled points. :return: Sampled points.
:rtype: LabelTensor :rtype: LabelTensor
:Example: :Example:
>>> # Create two Cartesian domains >>> # Create two Cartesian domains
>>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]}) >>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]})
>>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]}) >>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]})
>>> # Create a Exclusion of the ellipsoid domains >>> # Define the exclusion between the domains
>>> Exclusion = Exclusion([cartesian1, cartesian2]) >>> Exclusion = Exclusion([cartesian1, cartesian2])
>>> # Sample >>> # Sample
>>> Exclusion.sample(n=5) >>> Exclusion.sample(n=5)
@@ -79,7 +80,6 @@ class Exclusion(OperationInterface):
[0.1978, 0.3526]]) [0.1978, 0.3526]])
>>> len(Exclusion.sample(n=5) >>> len(Exclusion.sample(n=5)
5 5
""" """
if mode not in self.sample_modes: if mode not in self.sample_modes:
raise NotImplementedError( raise NotImplementedError(

View File

@@ -1,4 +1,4 @@
"""Module for Intersection class.""" """Module for the Intersection Operation."""
import random import random
import torch import torch
@@ -7,44 +7,43 @@ from .operation_interface import OperationInterface
class Intersection(OperationInterface): class Intersection(OperationInterface):
""" r"""
PINA implementation of Intersection of Domains. Implementation of the intersection operation between of a list of domains.
Given two sets :math:`A` and :math:`B`, define the intersection of the two
sets as:
.. math::
A \cap B = \{x \mid x \in A \land x \in B\},
where :math:`x` is a point in :math:`\mathbb{R}^N`.
""" """
def __init__(self, geometries): def __init__(self, geometries):
r""" """
Given two sets :math:`A` and :math:`B` then the Initialization of the :class:`Intersection` class.
domain difference is defined as:
.. math:: :param list[DomainInterface] geometries: A list of instances of the
A \cap B = \{x \mid x \in A \land x \in B\}, :class:`~pina.domain.DomainInterface` class on which the intersection
operation is performed.
with :math:`x` a point in :math:`\mathbb{R}^N` and :math:`N`
the dimension of the geometry space.
: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: :Example:
>>> # Create two ellipsoid domains >>> # Create two ellipsoid domains
>>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]}) >>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]})
>>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]}) >>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]})
>>> # Create a Intersection of the ellipsoid domains >>> # Define the intersection of the domains
>>> intersection = Intersection([ellipsoid1, ellipsoid2]) >>> intersection = Intersection([ellipsoid1, ellipsoid2])
""" """
super().__init__(geometries) super().__init__(geometries)
def is_inside(self, point, check_border=False): def is_inside(self, point, check_border=False):
""" """
Check if a point is inside the ``Intersection`` domain. Check if a point is inside the resulting domain.
:param point: Point to be checked. :param LabelTensor point: Point to be checked.
:type point: torch.Tensor :param bool check_border: If ``True``, the border is considered inside
:param bool check_border: If ``True``, the border is considered inside. the domain. Default is ``False``.
:return: ``True`` if the point is inside the Intersection domain, :return: ``True`` if the point is inside the domain, ``False`` otherwise.
``False`` otherwise.
:rtype: bool :rtype: bool
""" """
flag = 0 flag = 0
@@ -55,21 +54,21 @@ class Intersection(OperationInterface):
def sample(self, n, mode="random", variables="all"): def sample(self, n, mode="random", variables="all"):
""" """
Sample routine for ``Intersection`` domain. Sampling routine.
:param int n: Number of points to sample in the shape. :param int n: Number of points to sample.
:param str mode: Mode for sampling, defaults to ``random``. Available :param str mode: Sampling method. Default is ``random``.
modes include: ``random``. Available modes: random sampling, ``random``;
:param variables: Variables to be sampled, defaults to ``all``. :param list[str] variables: variables to be sampled. Default is ``all``.
:type variables: str | list[str] :raises NotImplementedError: If the sampling method is not implemented.
:return: Returns ``LabelTensor`` of n sampled points. :return: Sampled points.
:rtype: LabelTensor :rtype: LabelTensor
:Example: :Example:
>>> # Create two Cartesian domains >>> # Create two Cartesian domains
>>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]}) >>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]})
>>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]}) >>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]})
>>> # Create a Intersection of the ellipsoid domains >>> # Define the intersection of the domains
>>> intersection = Intersection([cartesian1, cartesian2]) >>> intersection = Intersection([cartesian1, cartesian2])
>>> # Sample >>> # Sample
>>> intersection.sample(n=5) >>> intersection.sample(n=5)
@@ -80,7 +79,6 @@ class Intersection(OperationInterface):
[1.9902, 1.4458]]) [1.9902, 1.4458]])
>>> len(intersection.sample(n=5) >>> len(intersection.sample(n=5)
5 5
""" """
if mode not in self.sample_modes: if mode not in self.sample_modes:
raise NotImplementedError( raise NotImplementedError(

View File

@@ -1,4 +1,4 @@
"""Module for OperationInterface class.""" """Module for the Operation Interface."""
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from .domain_interface import DomainInterface from .domain_interface import DomainInterface
@@ -7,15 +7,16 @@ from ..utils import check_consistency
class OperationInterface(DomainInterface, metaclass=ABCMeta): class OperationInterface(DomainInterface, metaclass=ABCMeta):
""" """
Abstract class for set domains operations. Abstract class for set operations defined on geometric domains.
""" """
def __init__(self, geometries): def __init__(self, geometries):
""" """
Any geometry operation entity must inherit from this class. Initialization of the :class:`OperationInterface` class.
:param list geometries: A list of geometries from ``pina.geometry`` :param list[DomainInterface] geometries: A list of instances of the
such as ``EllipsoidDomain`` or ``CartesianDomain``. :class:`~pina.domain.DomainInterface` class on which the set
operation is performed.
""" """
# check consistency geometries # check consistency geometries
check_consistency(geometries, DomainInterface) check_consistency(geometries, DomainInterface)
@@ -29,21 +30,30 @@ class OperationInterface(DomainInterface, metaclass=ABCMeta):
@property @property
def sample_modes(self): def sample_modes(self):
"""
List of available sampling modes.
:return: List of available sampling modes.
:rtype: list[str]
"""
return ["random"] return ["random"]
@property @property
def geometries(self): def geometries(self):
""" """
The geometries to perform set operation. The domains on which to perform the set operation.
:return: The domains on which to perform the set operation.
:rtype: list[DomainInterface]
""" """
return self._geometries return self._geometries
@property @property
def variables(self): def variables(self):
""" """
Spatial variables of the domain. List of variables of the domain.
:return: All the variables defined in ``__init__`` in order. :return: List of variables of the domain.
:rtype: list[str] :rtype: list[str]
""" """
variables = [] variables = []
@@ -54,22 +64,23 @@ class OperationInterface(DomainInterface, metaclass=ABCMeta):
@abstractmethod @abstractmethod
def is_inside(self, point, check_border=False): def is_inside(self, point, check_border=False):
""" """
Check if a point is inside the resulting domain after Abstract method to check if a point lies inside the resulting domain
a set operation is applied. after performing the set operation.
:param point: Point to be checked. :param LabelTensor point: Point to be checked.
:type point: torch.Tensor :param bool check_border: If ``True``, the border is considered inside
:param bool check_border: If ``True``, the border is considered inside. the resulting domain. Default is ``False``.
:return: ``True`` if the point is inside the Intersection domain, :return: ``True`` if the point is inside the domain, ``False`` otherwise.
``False`` otherwise.
:rtype: bool :rtype: bool
""" """
def _check_dimensions(self, geometries): def _check_dimensions(self, geometries):
"""Check if the dimensions of the geometries are consistent. """
Check if the dimensions of the geometries are consistent.
:param geometries: Geometries to be checked. :param list[DomainInterface] geometries: Domains to be checked.
:type geometries: list[Location] :raises NotImplementedError: If the dimensions of the geometries are not
consistent.
""" """
for geometry in geometries: for geometry in geometries:
if geometry.variables != geometries[0].variables: if geometry.variables != geometries[0].variables:

View File

@@ -1,6 +1,4 @@
""" """Module for the Simplex Domain."""
Module for Simplex Domain.
"""
import torch import torch
from .domain_interface import DomainInterface from .domain_interface import DomainInterface
@@ -10,27 +8,28 @@ from ..utils import check_consistency
class SimplexDomain(DomainInterface): class SimplexDomain(DomainInterface):
"""PINA implementation of a Simplex.""" """
Implementation of the simplex domain.
"""
def __init__(self, simplex_matrix, sample_surface=False): def __init__(self, simplex_matrix, sample_surface=False):
""" """
:param simplex_matrix: A matrix of LabelTensor objects representing Initialization of the :class:`SimplexDomain` class.
a vertex of the simplex (a tensor), and the coordinates of the
point (a list of labels).
:type simplex_matrix: list[LabelTensor] :param list[LabelTensor] simplex_matrix: A matrix representing the
:param sample_surface: A variable for choosing sample strategies. If vertices of the simplex.
``sample_surface=True`` only samples on the Simplex surface :param bool sample_surface: A flag to choose the sampling strategy.
frontier are taken. If ``sample_surface=False``, no such criteria If ``True``, samples are taken only from the surface of the simplex.
is followed. If ``False``, samples are taken from the interior of the simplex.
Default is ``False``.
:type sample_surface: bool :raises ValueError: If the labels of the vertices don't match.
:raises ValueError: If the number of vertices is not equal to the
dimension of the simplex plus one.
.. warning:: .. warning::
Sampling for dimensions greater or equal to 10 could result Sampling for dimensions greater or equal to 10 could result in a
in a shrinking of the simplex, which degrades the quality shrinkage of the simplex, which degrades the quality of the samples.
of the samples. For dimensions higher than 10, other algorithms For dimensions higher than 10, use other sampling algorithms.
for sampling should be used.
:Example: :Example:
>>> spatial_domain = SimplexDomain( >>> spatial_domain = SimplexDomain(
@@ -77,18 +76,30 @@ class SimplexDomain(DomainInterface):
@property @property
def sample_modes(self): def sample_modes(self):
"""
List of available sampling modes.
:return: List of available sampling modes.
:rtype: list[str]
"""
return ["random"] return ["random"]
@property @property
def variables(self): def variables(self):
"""
List of variables of the domain.
:return: List of variables of the domain.
:rtype: list[str]
"""
return sorted(self._vertices_matrix.labels) return sorted(self._vertices_matrix.labels)
def _build_cartesian(self, vertices): def _build_cartesian(self, vertices):
""" """
Build Cartesian border for Simplex domain to be used in sampling. Build the cartesian border for a simplex domain to be used in sampling.
:param vertex_matrix: matrix of vertices
:type vertices: list[list] :param list[LabelTensor] vertices: Matrix of vertices defining the domain.
:return: Cartesian border for triangular domain :return: The cartesian border for the simplex domain.
:rtype: CartesianDomain :rtype: CartesianDomain
""" """
@@ -105,22 +116,16 @@ class SimplexDomain(DomainInterface):
def is_inside(self, point, check_border=False): def is_inside(self, point, check_border=False):
""" """
Check if a point is inside the simplex. Check if a point is inside the simplex. It uses an algorithm involving
Uses the algorithm described involving barycentric coordinates: barycentric coordinates.
https://en.wikipedia.org/wiki/Barycentric_coordinate_system.
:param point: Point to be checked. :param LabelTensor point: Point to be checked.
:type point: LabelTensor :param check_border: If ``True``, the border is considered inside
:param check_border: Check if the point is also on the frontier the simplex. Default is ``False``.
of the simplex, default ``False``. :raises ValueError: If the labels of the point are different from those
:type check_border: bool passed in the ``__init__`` method.
:return: Returning ``True`` if the point is inside, ``False`` otherwise. :return: ``True`` if the point is inside the domain, ``False`` otherwise.
:rtype: bool :rtype: bool
.. note::
When ``sample_surface`` in the ``__init()__``
is set to ``True``, then the method only checks
points on the surface, and not inside the domain.
""" """
if not all(label in self.variables for label in point.labels): if not all(label in self.variables for label in point.labels):
@@ -134,7 +139,6 @@ class SimplexDomain(DomainInterface):
point_shift = point_shift.tensor.reshape(-1, 1) point_shift = point_shift.tensor.reshape(-1, 1)
# compute barycentric coordinates # compute barycentric coordinates
lambda_ = torch.linalg.solve( lambda_ = torch.linalg.solve(
self._vectors_shifted * 1.0, point_shift * 1.0 self._vectors_shifted * 1.0, point_shift * 1.0
) )
@@ -151,13 +155,13 @@ class SimplexDomain(DomainInterface):
def _sample_interior_randomly(self, n, variables): def _sample_interior_randomly(self, n, variables):
""" """
Randomly sample points inside a simplex of arbitrary Sample at random points from the interior of the simplex. Boundaries are
dimension, without the boundary. excluded from this sampling routine.
:param int n: Number of points to sample in the shape.
:param variables: pinn variable to be sampled, defaults to ``all``. :param int n: Number of points to sample.
:type variables: str or list[str], optional :param list[str] variables: variables to be sampled.
:return: Returns tensor of n sampled points. :return: Sampled points.
:rtype: torch.Tensor :rtype: list[torch.Tensor]
""" """
# =============== For Developers ================ # # =============== For Developers ================ #
@@ -182,10 +186,10 @@ class SimplexDomain(DomainInterface):
def _sample_boundary_randomly(self, n): def _sample_boundary_randomly(self, n):
""" """
Randomly sample points on the boundary of a simplex Sample at random points from the boundary of the simplex.
of arbitrary dimensions.
:param int n: Number of points to sample in the shape. :param int n: Number of points to sample.
:return: Returns tensor of n sampled points :return: Sampled points.
:rtype: torch.Tensor :rtype: torch.Tensor
""" """
@@ -221,20 +225,19 @@ class SimplexDomain(DomainInterface):
def sample(self, n, mode="random", variables="all"): def sample(self, n, mode="random", variables="all"):
""" """
Sample n points from Simplex domain. Sampling routine.
:param int n: Number of points to sample in the shape. :param int n: Number of points to sample.
:param str mode: Mode for sampling, defaults to ``random``. Available :param str mode: Sampling method. Default is ``random``.
modes include: ``random``. Available modes: random sampling, ``random``.
:param variables: Variables to be sampled, defaults to ``all``. :param list[str] variables: variables to be sampled. Default is ``all``.
:type variables: str | list[str] :raises NotImplementedError: If the sampling method is not implemented.
:return: Returns ``LabelTensor`` of n sampled points. :return: Sampled points.
:rtype: LabelTensor :rtype: LabelTensor
.. warning:: .. warning::
When ``sample_surface = True`` in the initialization, all When ``sample_surface=True``, all variables are sampled,
the variables are sampled, despite passing different once ignoring the ``variables`` parameter.
in ``variables``.
""" """
if variables == "all": if variables == "all":

View File

@@ -1,4 +1,4 @@
"""Module for Union class.""" """Module for the Union Operation."""
import random import random
import torch import torch
@@ -7,51 +7,51 @@ from ..label_tensor import LabelTensor
class Union(OperationInterface): class Union(OperationInterface):
""" r"""
Union of Domains. Implementation of the union operation between of a list of domains.
Given two sets :math:`A` and :math:`B`, define the union of the two sets as:
.. math::
A \cup B = \{x \mid x \in A \lor x \in B\},
where :math:`x` is a point in :math:`\mathbb{R}^N`.
""" """
def __init__(self, geometries): def __init__(self, geometries):
r""" """
PINA implementation of Unions of Domains. Initialization of the :class:`Union` class.
Given two sets :math:`A` and :math:`B` then the
domain difference is defined as:
.. math:: :param list[DomainInterface] geometries: A list of instances of the
A \cup B = \{x \mid x \in A \lor x \in B\}, :class:`~pina.domain.DomainInterface` class on which the union
operation is performed.
with :math:`x` a point in :math:`\mathbb{R}^N` and :math:`N`
the dimension of the geometry space.
:param list geometries: A list of geometries from ``pina.geometry``
such as ``EllipsoidDomain`` or ``CartesianDomain``.
:Example: :Example:
>>> # Create two ellipsoid domains >>> # Create two ellipsoid domains
>>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]}) >>> ellipsoid1 = EllipsoidDomain({'x': [-1, 1], 'y': [-1, 1]})
>>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]}) >>> ellipsoid2 = EllipsoidDomain({'x': [0, 2], 'y': [0, 2]})
>>> # Create a union of the ellipsoid domains >>> # Define the union of the domains
>>> union = GeometryUnion([ellipsoid1, ellipsoid2]) >>> union = Union([ellipsoid1, ellipsoid2])
""" """
super().__init__(geometries) super().__init__(geometries)
@property @property
def sample_modes(self): def sample_modes(self):
"""
List of available sampling modes.
"""
self.sample_modes = list( self.sample_modes = list(
set(geom.sample_modes for geom in self.geometries) set(geom.sample_modes for geom in self.geometries)
) )
def is_inside(self, point, check_border=False): def is_inside(self, point, check_border=False):
""" """
Check if a point is inside the ``Union`` domain. Check if a point is inside the resulting domain.
:param point: Point to be checked. :param LabelTensor point: Point to be checked.
:type point: LabelTensor :param bool check_border: If ``True``, the border is considered inside
:param check_border: Check if the point is also on the frontier the domain. Default is ``False``.
of the ellipsoid, default ``False``. :return: ``True`` if the point is inside the domain, ``False`` otherwise.
:type check_border: bool
:return: Returning ``True`` if the point is inside, ``False`` otherwise.
:rtype: bool :rtype: bool
""" """
for geometry in self.geometries: for geometry in self.geometries:
@@ -61,21 +61,20 @@ class Union(OperationInterface):
def sample(self, n, mode="random", variables="all"): def sample(self, n, mode="random", variables="all"):
""" """
Sample routine for ``Union`` domain. Sampling routine.
:param int n: Number of points to sample in the shape. :param int n: Number of points to sample.
:param str mode: Mode for sampling, defaults to ``random``. Available :param str mode: Sampling method. Default is ``random``.
modes include: ``random``. Available modes: random sampling, ``random``;
:param variables: Variables to be sampled, defaults to ``all``. :param list[str] variables: variables to be sampled. Default is ``all``.
:type variables: str | list[str] :return: Sampled points.
:return: Returns ``LabelTensor`` of n sampled points.
:rtype: LabelTensor :rtype: LabelTensor
:Example: :Example:
>>> # Create two ellipsoid domains >>> # Create two cartesian domains
>>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]}) >>> cartesian1 = CartesianDomain({'x': [0, 2], 'y': [0, 2]})
>>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]}) >>> cartesian2 = CartesianDomain({'x': [1, 3], 'y': [1, 3]})
>>> # Create a union of the ellipsoid domains >>> # Define the union of the domains
>>> union = Union([cartesian1, cartesian2]) >>> union = Union([cartesian1, cartesian2])
>>> # Sample >>> # Sample
>>> union.sample(n=5) >>> union.sample(n=5)