From babe7ce8f5242b511ee5c3c32ec802e03f39ddca Mon Sep 17 00:00:00 2001 From: Filippo Olivo Date: Wed, 11 Sep 2024 12:14:03 +0200 Subject: [PATCH 1/5] Fix some bugs --- examples/problems/stokes.py | 18 ++++++++++---- examples/run_stokes.py | 4 ++-- pina/condition/condition.py | 6 +++-- pina/condition/condition_interface.py | 5 +++- pina/condition/domain_equation_condition.py | 12 +++++++--- pina/condition/domain_output_condition.py | 1 + pina/problem/abstract_problem.py | 26 ++++++++++----------- 7 files changed, 46 insertions(+), 26 deletions(-) diff --git a/examples/problems/stokes.py b/examples/problems/stokes.py index c7b13873c..5f14f4e8f 100644 --- a/examples/problems/stokes.py +++ b/examples/problems/stokes.py @@ -49,11 +49,19 @@ def wall(input_, output_): value = 0.0 return output_.extract(['ux', 'uy']) - value + domains = { + 'gamma_top': CartesianDomain({'x': [-2, 2], 'y': 1}), + 'gamma_bot': CartesianDomain({'x': [-2, 2], 'y': -1}), + 'gamma_out': CartesianDomain({'x': 2, 'y': [-1, 1]}), + 'gamma_in': CartesianDomain({'x': -2, 'y': [-1, 1]}), + 'D': CartesianDomain({'x': [-2, 2], 'y': [-1, 1]}) + } + # problem condition statement conditions = { - 'gamma_top': Condition(location=CartesianDomain({'x': [-2, 2], 'y': 1}), equation=Equation(wall)), - 'gamma_bot': Condition(location=CartesianDomain({'x': [-2, 2], 'y': -1}), equation=Equation(wall)), - 'gamma_out': Condition(location=CartesianDomain({'x': 2, 'y': [-1, 1]}), equation=Equation(outlet)), - 'gamma_in': Condition(location=CartesianDomain({'x': -2, 'y': [-1, 1]}), equation=Equation(inlet)), - 'D': Condition(location=CartesianDomain({'x': [-2, 2], 'y': [-1, 1]}), equation=SystemEquation([momentum, continuity])) + 'gamma_top': Condition(domain='gamma_top', equation=Equation(wall)), + 'gamma_bot': Condition(domain='gamma_bot', equation=Equation(wall)), + 'gamma_out': Condition(domain='gamma_out', equation=Equation(outlet)), + 'gamma_in': Condition(domain='gamma_in', equation=Equation(inlet)), + 'D': Condition(domain='D', equation=SystemEquation([momentum, continuity])) } diff --git a/examples/run_stokes.py b/examples/run_stokes.py index 54b2aecc3..04f652bd3 100644 --- a/examples/run_stokes.py +++ b/examples/run_stokes.py @@ -17,8 +17,8 @@ # create problem and discretise domain stokes_problem = Stokes() - stokes_problem.discretise_domain(n=1000, locations=['gamma_top', 'gamma_bot', 'gamma_in', 'gamma_out']) - stokes_problem.discretise_domain(n=2000, locations=['D']) + stokes_problem.discretise_domain(n=1000, domains=['gamma_top', 'gamma_bot', 'gamma_in', 'gamma_out']) + stokes_problem.discretise_domain(n=2000, domains=['D']) # make the model model = FeedForward( diff --git a/pina/condition/condition.py b/pina/condition/condition.py index eec523c92..d815838bb 100644 --- a/pina/condition/condition.py +++ b/pina/condition/condition.py @@ -84,14 +84,15 @@ def __new__(cls, *args, **kwargs): return DomainEquationCondition(**kwargs) else: raise ValueError(f"Invalid keyword arguments {kwargs.keys()}.") - + # TODO: remove, not used anymore + ''' if ( sorted(kwargs.keys()) != sorted(["input_points", "output_points"]) and sorted(kwargs.keys()) != sorted(["location", "equation"]) and sorted(kwargs.keys()) != sorted(["input_points", "equation"]) ): raise ValueError(f"Invalid keyword arguments {kwargs.keys()}.") - + # TODO: remove, not used anymore if not self._dictvalue_isinstance(kwargs, "input_points", LabelTensor): raise TypeError("`input_points` must be a torch.Tensor.") if not self._dictvalue_isinstance(kwargs, "output_points", LabelTensor): @@ -103,3 +104,4 @@ def __new__(cls, *args, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) + ''' \ No newline at end of file diff --git a/pina/condition/condition_interface.py b/pina/condition/condition_interface.py index f6b51bf96..0626a6d83 100644 --- a/pina/condition/condition_interface.py +++ b/pina/condition/condition_interface.py @@ -15,4 +15,7 @@ def residual(self, model): :param model: The model to evaluate the condition. :return: The residual of the condition. """ - pass \ No newline at end of file + pass + + def set_problem(self, problem): + self._problem = problem diff --git a/pina/condition/domain_equation_condition.py b/pina/condition/domain_equation_condition.py index 8f45f8f4a..15df3f85f 100644 --- a/pina/condition/domain_equation_condition.py +++ b/pina/condition/domain_equation_condition.py @@ -15,6 +15,12 @@ def __init__(self, domain, equation): self.domain = domain self.equation = equation + def residual(self, model): + """ + Compute the residual of the condition. + """ + self.batch_residual(model, self.domain, self.equation) + @staticmethod def batch_residual(model, input_pts, equation): """ @@ -22,7 +28,7 @@ def batch_residual(model, input_pts, equation): output points are provided as arguments. :param torch.nn.Module model: The model to evaluate the condition. - :param torch.Tensor input_points: The input points. - :param torch.Tensor output_points: The output points. + :param torch.Tensor input_pts: The input points. + :param torch.Tensor equation: The output points. """ - return equation.residual(model(input_pts)) \ No newline at end of file + return equation.residual(input_pts, model(input_pts)) \ No newline at end of file diff --git a/pina/condition/domain_output_condition.py b/pina/condition/domain_output_condition.py index 49a0cb6ed..f847720b5 100644 --- a/pina/condition/domain_output_condition.py +++ b/pina/condition/domain_output_condition.py @@ -40,4 +40,5 @@ def batch_residual(model, input_points, output_points): :param torch.Tensor input_points: The input points. :param torch.Tensor output_points: The output points. """ + return output_points - model(input_points) \ No newline at end of file diff --git a/pina/problem/abstract_problem.py b/pina/problem/abstract_problem.py index 05b335670..f433fbc15 100644 --- a/pina/problem/abstract_problem.py +++ b/pina/problem/abstract_problem.py @@ -20,7 +20,6 @@ class AbstractProblem(metaclass=ABCMeta): def __init__(self): - self._discretized_domains = {} for name, domain in self.domains.items(): @@ -28,7 +27,8 @@ def __init__(self): self._discretized_domains[name] = domain for condition_name in self.conditions: - self.conditions[condition_name]._problem = self + self.conditions[condition_name].set_problem(self) + # # variable storing all points # self.input_pts = {} @@ -141,7 +141,7 @@ def _span_condition_points(self): ) def discretise_domain( - self, n, mode="random", variables="all", locations="all" + self, n, mode="random", variables="all", domains="all" ): """ Generate a set of points to span the `Location` of all the conditions of @@ -193,24 +193,24 @@ def discretise_domain( ) # check consistency location - if locations == "all": - locations = [condition for condition in self.conditions] + if domains == "all": + domains = [condition for condition in self.conditions] else: - check_consistency(locations, str) - - if sorted(locations) != sorted(self.conditions): + check_consistency(domains, str) + print(domains) + if sorted(domains) != sorted(self.conditions): TypeError( f"Wrong locations for sampling. Location ", f"should be in {self.conditions}.", ) # sampling - for location in locations: - condition = self.conditions[location] + for domain in domains: + condition = self.conditions[domain] # we try to check if we have already sampled try: - already_sampled = [self.input_pts[location]] + already_sampled = [self.input_pts[domain]] # if we have not sampled, a key error is thrown except KeyError: already_sampled = [] @@ -219,9 +219,9 @@ def discretise_domain( # but we want to sample again we set already_sampled # to an empty list since we need to sample again, and # self._have_sampled_points to False. - if self._have_sampled_points[location]: + if self._have_sampled_points[domain]: already_sampled = [] - self._have_sampled_points[location] = False + self._have_sampled_points[domain] = False # build samples samples = [ From aa9121faa16f9b74b07b56a961a7435127951ed2 Mon Sep 17 00:00:00 2001 From: Filippo Olivo Date: Thu, 12 Sep 2024 10:36:34 +0200 Subject: [PATCH 2/5] Remove print --- pina/solvers/supervised.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pina/solvers/supervised.py b/pina/solvers/supervised.py index 4f3d497fe..dd7cab761 100644 --- a/pina/solvers/supervised.py +++ b/pina/solvers/supervised.py @@ -134,8 +134,6 @@ def training_step(self, batch, batch_idx): condition = self.problem.conditions[condition_name] pts = batch.input out = batch.output - print(out) - print(pts) if condition_name not in self.problem.conditions: raise RuntimeError("Something wrong happened.") From c38a011c1847b31c933f9bac6bef97cf701f8dd2 Mon Sep 17 00:00:00 2001 From: Filippo Olivo Date: Thu, 12 Sep 2024 12:18:16 +0200 Subject: [PATCH 3/5] Minor changes --- pina/domain/cartesian.py | 1 + pina/label_tensor.py | 37 +++++++++++++++++++++++++++++--- pina/problem/abstract_problem.py | 31 +++++++++++++------------- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/pina/domain/cartesian.py b/pina/domain/cartesian.py index 04f3abefe..51270549a 100644 --- a/pina/domain/cartesian.py +++ b/pina/domain/cartesian.py @@ -1,4 +1,5 @@ import torch +import torch from .domain_interface import DomainInterface from ..label_tensor import LabelTensor diff --git a/pina/label_tensor.py b/pina/label_tensor.py index 0a5515365..3167c0e61 100644 --- a/pina/label_tensor.py +++ b/pina/label_tensor.py @@ -5,7 +5,6 @@ from torch import Tensor - # class LabelTensor(torch.Tensor): # """Torch tensor with a label for any column.""" @@ -307,13 +306,13 @@ # s = "no labels\n" # s += super().__str__() # return s - def issubset(a, b): """ Check if a is a subset of b. """ return set(a).issubset(set(b)) + class LabelTensor(torch.Tensor): """Torch tensor with a label for any column.""" @@ -403,6 +402,10 @@ def extract(self, label_to_extract): return LabelTensor(new_tensor, label_to_extract) def __str__(self): + """ + returns a string with the representation of the class + """ + s = '' for key, value in self.labels.items(): s += f"{key}: {value}\n" @@ -431,4 +434,32 @@ def requires_grad_(self, mode=True): @property def dtype(self): - return super().dtype \ No newline at end of file + return super().dtype + + + def to(self, *args, **kwargs): + """ + Performs Tensor dtype and/or device conversion. For more details, see + :meth:`torch.Tensor.to`. + """ + tmp = super().to(*args, **kwargs) + new = self.__class__.clone(self) + new.data = tmp.data + return new + + + def clone(self, *args, **kwargs): + """ + Clone the LabelTensor. For more details, see + :meth:`torch.Tensor.clone`. + + :return: A copy of the tensor. + :rtype: LabelTensor + """ + # # used before merging + # try: + # out = LabelTensor(super().clone(*args, **kwargs), self.labels) + # except: + # out = super().clone(*args, **kwargs) + out = LabelTensor(super().clone(*args, **kwargs), self.labels) + return out \ No newline at end of file diff --git a/pina/problem/abstract_problem.py b/pina/problem/abstract_problem.py index f433fbc15..bf0a2dccd 100644 --- a/pina/problem/abstract_problem.py +++ b/pina/problem/abstract_problem.py @@ -30,16 +30,16 @@ def __init__(self): self.conditions[condition_name].set_problem(self) # # variable storing all points - # self.input_pts = {} + self.input_pts = {} # # varible to check if sampling is done. If no location # # element is presented in Condition this variable is set to true # self._have_sampled_points = {} - # for condition_name in self.conditions: - # self._have_sampled_points[condition_name] = False + for condition_name in self.conditions: + self._discretized_domains[condition_name] = False # # put in self.input_pts all the points that we don't need to sample - # self._span_condition_points() + self._span_condition_points() def __deepcopy__(self, memo): """ @@ -125,7 +125,7 @@ def _span_condition_points(self): if hasattr(condition, "input_points"): samples = condition.input_points self.input_pts[condition_name] = samples - self._have_sampled_points[condition_name] = True + self._discretized_domains[condition_name] = True if hasattr(self, "unknown_parameter_domain"): # initialize the unknown parameters of the inverse problem given # the domain the user gives @@ -205,12 +205,12 @@ def discretise_domain( ) # sampling - for domain in domains: - condition = self.conditions[domain] + for d in domains: + condition = self.conditions[d] # we try to check if we have already sampled try: - already_sampled = [self.input_pts[domain]] + already_sampled = [self.input_pts[d]] # if we have not sampled, a key error is thrown except KeyError: already_sampled = [] @@ -219,22 +219,23 @@ def discretise_domain( # but we want to sample again we set already_sampled # to an empty list since we need to sample again, and # self._have_sampled_points to False. - if self._have_sampled_points[domain]: + if self._discretized_domains[d]: already_sampled = [] - self._have_sampled_points[domain] = False - + self._discretized_domains[d] = False + print(condition.domain) + print(d) # build samples samples = [ - condition.location.sample(n=n, mode=mode, variables=variables) + self.domains[d].sample(n=n, mode=mode, variables=variables) ] + already_sampled pts = merge_tensors(samples) - self.input_pts[location] = pts + self.input_pts[d] = pts # the condition is sampled if input_pts contains all labels - if sorted(self.input_pts[location].labels) == sorted( + if sorted(self.input_pts[d].labels) == sorted( self.input_variables ): - self._have_sampled_points[location] = True + self._have_sampled_points[d] = True def add_points(self, new_points): """ From 55d428459b8fb93cc71db2327fcb475e8aa9c33b Mon Sep 17 00:00:00 2001 From: Filippo Olivo Date: Thu, 12 Sep 2024 14:10:50 +0200 Subject: [PATCH 4/5] Minor changes on test --- tests/test_solvers/test_supervised_solver.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_solvers/test_supervised_solver.py b/tests/test_solvers/test_supervised_solver.py index 1bb812027..d70f0273d 100644 --- a/tests/test_solvers/test_supervised_solver.py +++ b/tests/test_solvers/test_supervised_solver.py @@ -5,7 +5,7 @@ from pina.solvers import SupervisedSolver from pina.trainer import Trainer from pina.model import FeedForward -from pina.loss.loss_interface import LpLoss +from pina.loss import LpLoss class NeuralOperatorProblem(AbstractProblem): @@ -94,11 +94,9 @@ def forward(self, data, edge_index): return x def test_graph(): - solver = AutoSolver(problem = problem, model=GraphModel(2, 1), loss=LpLoss()) trainer = Trainer(solver=solver, max_epochs=30, accelerator='cpu', batch_size=20) trainer.train() - assert False def test_train_cpu(): @@ -106,6 +104,10 @@ def test_train_cpu(): trainer = Trainer(solver=solver, max_epochs=300, accelerator='cpu', batch_size=20) trainer.train() +test_train_cpu() +test_graph() +test_constructor() + # def test_train_restore(): # tmpdir = "tests/tmp_restore" From 386afae44c2c5427a4f4943bc52547308bb06285 Mon Sep 17 00:00:00 2001 From: Nicola Demo Date: Thu, 12 Sep 2024 18:11:32 +0200 Subject: [PATCH 5/5] Update test_supervised_solver.py --- tests/test_solvers/test_supervised_solver.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_solvers/test_supervised_solver.py b/tests/test_solvers/test_supervised_solver.py index d70f0273d..cc8c5631b 100644 --- a/tests/test_solvers/test_supervised_solver.py +++ b/tests/test_solvers/test_supervised_solver.py @@ -104,9 +104,6 @@ def test_train_cpu(): trainer = Trainer(solver=solver, max_epochs=300, accelerator='cpu', batch_size=20) trainer.train() -test_train_cpu() -test_graph() -test_constructor() # def test_train_restore(): @@ -155,4 +152,4 @@ def test_train_cpu(): # model=model_extra_feats, # extra_features=extra_feats) # trainer = Trainer(solver=pinn, max_epochs=5, accelerator='cpu') -# trainer.train() \ No newline at end of file +# trainer.train()