pinnrl 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pinnrl/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Top-level package for PINNs-RL-PDE framework."""
@@ -0,0 +1,134 @@
1
+ import logging
2
+
3
+ import torch
4
+
5
+
6
+ class AdaptiveLossWeights:
7
+ """Handles adaptive weighting of different loss components in PINNs."""
8
+
9
+ def __init__(self, strategy="rbw", alpha=0.9, eps=1e-5, initial_weights=None):
10
+ """
11
+ Initialize adaptive loss weights handler.
12
+
13
+ Args:
14
+ strategy (str): Weight adaptation strategy ('lrw' or 'rbw')
15
+ alpha (float): Moving average factor for weight updates
16
+ eps (float): Small constant for numerical stability
17
+ initial_weights (list): Initial weights for [pde, boundary, initial] components
18
+ """
19
+ self.strategy = strategy.lower()
20
+ self.alpha = alpha
21
+ # Ensure eps is a float, not a string
22
+ self.eps = float(eps) if isinstance(eps, (str, int)) else eps
23
+ self.initial_weights = (
24
+ torch.tensor(initial_weights) if initial_weights is not None else None
25
+ )
26
+ self.weights = None
27
+ self.running_losses = None
28
+ self.running_grads = None
29
+ self.logger = logging.getLogger(__name__)
30
+ self.logger.info(
31
+ f"AdaptiveLossWeights initialized with strategy={strategy}, alpha={alpha}, eps={self.eps}, initial_weights={initial_weights}"
32
+ )
33
+ self.prev_weights = None
34
+
35
+ def update_weights_lrw(self, gradients):
36
+ """
37
+ Update weights using Learning Rate based Weighting (LRW).
38
+
39
+ Args:
40
+ gradients (list): List of gradient norms for each loss component
41
+ """
42
+ if self.running_grads is None:
43
+ self.running_grads = gradients
44
+ self.weights = (
45
+ self.initial_weights.to(gradients.device)
46
+ if self.initial_weights is not None
47
+ else torch.ones_like(gradients)
48
+ )
49
+ self.logger.info(f"LRW - Initialized weights: {self.weights}")
50
+ return self.weights
51
+
52
+ # Update running average of gradients
53
+ self.running_grads = self.alpha * self.running_grads + (1 - self.alpha) * gradients
54
+
55
+ # Ensure eps is a tensor on the same device as the gradients
56
+ eps_tensor = torch.tensor(self.eps, device=self.running_grads.device)
57
+
58
+ # Compute weights inversely proportional to gradient magnitudes
59
+ inv_grads = 1.0 / (self.running_grads + eps_tensor)
60
+ self.weights = inv_grads / torch.sum(inv_grads)
61
+
62
+ self.logger.info(
63
+ f"LRW - Updated weights: gradients={gradients.detach().cpu().numpy()}, running_grads={self.running_grads.detach().cpu().numpy()}, weights={self.weights.detach().cpu().numpy()}"
64
+ )
65
+
66
+ return self.weights
67
+
68
+ def update_weights_rbw(self, losses):
69
+ """
70
+ Update weights using Relative Error based Weighting (RBW).
71
+ Gives more weight to terms with bigger losses to balance them.
72
+
73
+ Args:
74
+ losses (list): List of individual loss components
75
+ """
76
+ if self.running_losses is None:
77
+ self.running_losses = losses
78
+ self.weights = (
79
+ self.initial_weights.to(losses.device)
80
+ if self.initial_weights is not None
81
+ else torch.ones_like(losses)
82
+ )
83
+ self.logger.info(f"RBW - Initialized weights: {self.weights}")
84
+ return self.weights
85
+
86
+ # Update running average of losses
87
+ self.running_losses = self.alpha * self.running_losses + (1 - self.alpha) * losses
88
+
89
+ # Ensure eps is a tensor on the same device as the losses
90
+ eps_tensor = torch.tensor(self.eps, device=self.running_losses.device)
91
+
92
+ # Normalize the running losses to get weights
93
+ # Give more weight to higher losses to focus optimization on problematic terms
94
+ normalized_losses = self.running_losses / (self.running_losses.sum() + eps_tensor)
95
+ self.weights = normalized_losses # Higher loss -> Higher weight
96
+
97
+ # Update weights with exponential moving average
98
+ if self.prev_weights is not None:
99
+ self.weights = self.alpha * self.prev_weights + (1 - self.alpha) * self.weights
100
+
101
+ self.prev_weights = self.weights.clone()
102
+
103
+ self.logger.info(
104
+ f"RBW - Updated weights: losses={losses.detach().cpu().numpy()}, running_losses={self.running_losses.detach().cpu().numpy()}, weights={self.weights.detach().cpu().numpy()}"
105
+ )
106
+
107
+ return self.weights
108
+
109
+ def update(self, losses=None, gradients=None):
110
+ """
111
+ Update weights based on chosen strategy.
112
+
113
+ Args:
114
+ losses (list): List of individual loss components
115
+ gradients (list): List of gradient norms for each loss component
116
+
117
+ Returns:
118
+ torch.Tensor: Updated loss weights
119
+ """
120
+ if self.strategy == "lrw" and gradients is not None:
121
+ return self.update_weights_lrw(gradients)
122
+ elif self.strategy == "rbw" and losses is not None:
123
+ return self.update_weights_rbw(losses)
124
+ else:
125
+ raise ValueError(f"Invalid combination of strategy ({self.strategy}) and inputs")
126
+
127
+ def get_weights(self):
128
+ """Return current weights."""
129
+ if self.weights is not None:
130
+ return self.weights
131
+ elif self.initial_weights is not None:
132
+ return self.initial_weights
133
+ else:
134
+ return torch.ones(3) / 3.0 # Default to equal weights for 3 components