OneScience commited on
Commit
371b59f
·
verified ·
1 Parent(s): 81331f3

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -1,35 +1,50 @@
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
 
4
  *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
  *.ftz filter=lfs diff=lfs merge=lfs -text
7
  *.gz filter=lfs diff=lfs merge=lfs -text
8
  *.h5 filter=lfs diff=lfs merge=lfs -text
9
  *.joblib filter=lfs diff=lfs merge=lfs -text
10
  *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
  *.model filter=lfs diff=lfs merge=lfs -text
13
  *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
  *.onnx filter=lfs diff=lfs merge=lfs -text
17
  *.ot filter=lfs diff=lfs merge=lfs -text
18
  *.parquet filter=lfs diff=lfs merge=lfs -text
19
  *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
  *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
  *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
  *.tflite filter=lfs diff=lfs merge=lfs -text
30
  *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
  *.xz filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bin.* filter=lfs diff=lfs merge=lfs -text
5
  *.bz2 filter=lfs diff=lfs merge=lfs -text
 
6
  *.ftz filter=lfs diff=lfs merge=lfs -text
7
  *.gz filter=lfs diff=lfs merge=lfs -text
8
  *.h5 filter=lfs diff=lfs merge=lfs -text
9
  *.joblib filter=lfs diff=lfs merge=lfs -text
10
  *.lfs.* filter=lfs diff=lfs merge=lfs -text
 
11
  *.model filter=lfs diff=lfs merge=lfs -text
12
  *.msgpack filter=lfs diff=lfs merge=lfs -text
 
 
13
  *.onnx filter=lfs diff=lfs merge=lfs -text
14
  *.ot filter=lfs diff=lfs merge=lfs -text
15
  *.parquet filter=lfs diff=lfs merge=lfs -text
16
  *.pb filter=lfs diff=lfs merge=lfs -text
17
+
 
 
18
  *.pth filter=lfs diff=lfs merge=lfs -text
19
  *.rar filter=lfs diff=lfs merge=lfs -text
 
20
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
21
  *.tar.* filter=lfs diff=lfs merge=lfs -text
 
22
  *.tflite filter=lfs diff=lfs merge=lfs -text
23
  *.tgz filter=lfs diff=lfs merge=lfs -text
 
24
  *.xz filter=lfs diff=lfs merge=lfs -text
25
  *.zip filter=lfs diff=lfs merge=lfs -text
26
+ *.zstandard filter=lfs diff=lfs merge=lfs -text
27
+ *.tfevents* filter=lfs diff=lfs merge=lfs -text
28
+ *.db* filter=lfs diff=lfs merge=lfs -text
29
+ *.ark* filter=lfs diff=lfs merge=lfs -text
30
+ **/*ckpt*data* filter=lfs diff=lfs merge=lfs -text
31
+ **/*ckpt*.meta filter=lfs diff=lfs merge=lfs -text
32
+ **/*ckpt*.index filter=lfs diff=lfs merge=lfs -text
33
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
34
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
35
+ *.gguf* filter=lfs diff=lfs merge=lfs -text
36
+ *.ggml filter=lfs diff=lfs merge=lfs -text
37
+ *.llamafile* filter=lfs diff=lfs merge=lfs -text
38
+ *.pt2 filter=lfs diff=lfs merge=lfs -text
39
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
40
+ *.npy filter=lfs diff=lfs merge=lfs -text
41
+
42
+ *.pickle filter=lfs diff=lfs merge=lfs -text
43
+ *.pkl filter=lfs diff=lfs merge=lfs -text
44
+ *.tar filter=lfs diff=lfs merge=lfs -text
45
+ *.wasm filter=lfs diff=lfs merge=lfs -text
46
  *.zst filter=lfs diff=lfs merge=lfs -text
47
  *tfevents* filter=lfs diff=lfs merge=lfs -text
48
+
49
+ *.npz filter=lfs diff=lfs merge=lfs -text
50
+ *.pt filter=lfs diff=lfs merge=lfs -textdata/AC.mat filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ frameworks:
3
+ - pytorch
4
+ language:
5
+ - en
6
+ license: apache-2.0
7
+ tags:
8
+ - OneScience
9
+ - FPINNs
10
+ - fuzzy-physics-informed-neural-networks
11
+ - Allen-Cahn
12
+ - forward-problem
13
+ - inverse-problem
14
+ tasks:
15
+ - pde-solving
16
+ ---
17
+ <p align="center">
18
+ <strong>
19
+ <span style="font-size: 30px;">FPINNs</span>
20
+ </strong>
21
+ </p>
22
+
23
+ # Model Overview
24
+
25
+ In this model package, FPINNs refers to Fuzzy Physics-Informed Neural Networks, not fractional PINNs. The architecture augments a fully connected network with a Gaussian fuzzy-membership branch and fuses neural features with fuzzy-rule features to predict solutions to partial differential equations.
26
+
27
+ The current example solves the Allen–Cahn equation:
28
+
29
+ ```text
30
+ u_t - lambda_1 u_xx + lambda_2 (u^3 - u) = 0
31
+ ```
32
+
33
+ Paper: Deep fuzzy physics-informed neural networks for forward and inverse PDE problems
34
+ https://doi.org/10.1016/j.neunet.2024.106750
35
+
36
+ # Model Description
37
+
38
+ FPINNs are trained jointly on data loss and PDE residuals and support both forward and inverse Allen–Cahn problems. The forward task predicts the spatiotemporal solution for known parameters `lambda_1=0.0001` and `lambda_2=5.0`; the inverse task jointly learns the equation solution and both parameters from observations. The model is trained with Adam by default, with optional L-BFGS refinement.
39
+
40
+ # Use Cases
41
+
42
+ | Use Case | Description |
43
+ | :---: | :--- |
44
+ | Forward Allen–Cahn problem | Predict the complete spatiotemporal solution using known diffusion and reaction parameters |
45
+ | Inverse Allen–Cahn problem | Identify diffusion and reaction parameters from solution observations |
46
+ | Fuzzy-feature research | Evaluate the fusion of neural features and Gaussian fuzzy-rule features |
47
+ | Pipeline validation | Validate training and inference using the bundled data and a small-scale configuration |
48
+
49
+ # Usage
50
+
51
+ ## 1. OneCode
52
+
53
+ Use the online OneCode environment for an intelligent, one-click AI for Science (AI4S) programming experience:
54
+
55
+ [Launch OneCode for one-click AI4S programming](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
56
+
57
+ ## 2. Manual Setup
58
+
59
+ **Hardware Requirements**
60
+
61
+ - A GPU or DCU is recommended for training and full-grid inference.
62
+ - A CPU can be used for small-scale pipeline validation.
63
+ - DCU users must install DTK and a PyTorch environment compatible with the target cluster.
64
+
65
+ ### Download the Model Package
66
+
67
+ ```bash
68
+ modelscope download --model OneScience/FPINNs --local_dir ./FPINNs
69
+ cd FPINNs
70
+ ```
71
+
72
+ ### Set Up the Runtime Environment
73
+
74
+ **DCU Environment**
75
+
76
+ ```bash
77
+ # Activate DTK and Conda first
78
+ conda create -n onescience311 python=3.11 -y
79
+ conda activate onescience311
80
+ pip install onescience[cfd-dcu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
81
+ ```
82
+
83
+ **GPU Environment**
84
+
85
+ ```bash
86
+ # Activate Conda first
87
+ conda create -n onescience311 python=3.11 -y libstdcxx-ng=12 libgcc-ng=12 gcc_linux-64=12 gxx_linux-64=12
88
+ conda activate onescience311
89
+ pip install onescience[cfd-gpu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
90
+ ```
91
+
92
+ ### Training Data
93
+
94
+ The model package includes the Allen–Cahn data file `data/AC.mat`, which contains spatial coordinates, temporal coordinates, and the corresponding equation solution. The number of training samples and evaluation batch size can be adjusted in `conf/config.yaml`.
95
+
96
+ ### Training
97
+
98
+ The training task is controlled by `common.task` in `conf/config.yaml`: `forward` selects the forward problem, while `inverse` selects the inverse problem. After configuring the task, run:
99
+
100
+ ```bash
101
+ python scripts/train.py
102
+ ```
103
+
104
+ Training checkpoints and histories are saved to the `weight/` and `result/` directories by default.
105
+
106
+ ### Model Weights
107
+
108
+ This repository provides weights trained on the Allen–Cahn dataset in the `weight/` directory.
109
+
110
+ ### Inference, Evaluation, and Visualization
111
+
112
+ After training the selected task, run:
113
+
114
+ ```bash
115
+ python scripts/inference.py
116
+ ```
117
+
118
+ The inference task is also controlled by `common.task`. Results are saved as `result/fpinn_forward.*` or `result/fpinn_inverse.*` by default. The inverse task additionally reports the recovered `lambda_1` and `lambda_2` values. Model, training, loss, and inference parameters can all be modified in `conf/config.yaml`.
119
+
120
+ # Official OneScience Resources
121
+
122
+ | Platform | OneScience Repository | Skills Repository |
123
+ | --- | --- | --- |
124
+ | Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
125
+ | GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
126
+
127
+ # Citations and License
128
+
129
+ - Wu, W., Duan, S., Sun, Y., Yu, Y., Liu, D., and Peng, D. Deep fuzzy physics-informed neural networks for forward and inverse PDE problems. Neural Networks, 181, 106750, 2025.
130
+ - This model package is released under the Apache-2.0 license and retains attribution to the original paper and data sources.
conf/config.yaml ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ root:
2
+ common:
3
+ task: "forward"
4
+ device: "auto"
5
+ dtype: "float32"
6
+ seed: 1111
7
+ weight_dir: "weight"
8
+ result_dir: "result"
9
+
10
+ data:
11
+ mat_file: "data/AC.mat"
12
+ n_train: 8000
13
+ evaluation_batch_size: 8192
14
+
15
+ model:
16
+ hidden_layers: [200, 200, 200, 200]
17
+ linear_dim: 10
18
+ fuzzy_dim: 4
19
+ activation: "tanh"
20
+
21
+ tasks:
22
+ forward:
23
+ training:
24
+ epochs: 10000
25
+ lr: 0.001
26
+ lbfgs_iters: 0
27
+ log_interval: 500
28
+ loss:
29
+ data: 1.0
30
+ pde: 1.0
31
+ pde:
32
+ lambda_1: 0.0001
33
+ lambda_2: 5.0
34
+ output:
35
+ checkpoint_name: "fpinn_forward.pt"
36
+ history_name: "fpinn_forward_history.npz"
37
+ predictions_name: "fpinn_forward.npz"
38
+ figure_name: "fpinn_forward.png"
39
+
40
+ inverse:
41
+ training:
42
+ epochs: 10000
43
+ lr: 0.001
44
+ lbfgs_iters: 0
45
+ log_interval: 500
46
+ loss:
47
+ data: 1.0
48
+ pde: 1.0
49
+ pde:
50
+ initial_lambda_1: 1.0
51
+ initial_lambda_2: 0.0
52
+ true_lambda_1: 0.0001
53
+ true_lambda_2: 5.0
54
+ output:
55
+ checkpoint_name: "fpinn_inverse.pt"
56
+ history_name: "fpinn_inverse_history.npz"
57
+ predictions_name: "fpinn_inverse.npz"
58
+ figure_name: "fpinn_inverse.png"
configuration.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"framework":"Pytorch","task":"other"}
data/AC.mat ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:266e97eedd9ef543d7f6d370fa5f06388761aa7da2b97e476562dcf1d644a175
3
+ size 559196
model/__pycache__/fpinn.cpython-311.pyc ADDED
Binary file (12.3 kB). View file
 
model/fpinn.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping, Sequence
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+
8
+
9
+ class FCN(nn.Module):
10
+ """Fully connected trunk used by the fuzzy PINN."""
11
+
12
+ def __init__(
13
+ self,
14
+ layer_sizes: Sequence[int],
15
+ activation: str = "tanh",
16
+ dtype: torch.dtype = torch.float32,
17
+ ) -> None:
18
+ super().__init__()
19
+ if len(layer_sizes) < 2:
20
+ raise ValueError("layer_sizes must include input and output widths")
21
+ if activation not in {"tanh", "sin", "sine", "relu", "gelu"}:
22
+ raise ValueError(f"unsupported activation: {activation}")
23
+ self.activation = "sin" if activation == "sine" else activation
24
+ self.linears = nn.ModuleList(
25
+ nn.Linear(layer_sizes[index], layer_sizes[index + 1], dtype=dtype)
26
+ for index in range(len(layer_sizes) - 1)
27
+ )
28
+
29
+ def _activate(self, values: torch.Tensor) -> torch.Tensor:
30
+ if self.activation == "gelu":
31
+ return torch.nn.functional.gelu(values)
32
+ return getattr(torch, self.activation)(values)
33
+
34
+ def forward(self, inputs: torch.Tensor) -> torch.Tensor:
35
+ hidden = inputs
36
+ for linear in self.linears[:-1]:
37
+ hidden = self._activate(linear(hidden))
38
+ return self.linears[-1](hidden)
39
+
40
+
41
+ class FuzzyLayer(nn.Module):
42
+ """Gaussian fuzzy membership rules followed by product inference."""
43
+
44
+ def __init__(
45
+ self,
46
+ input_dim: int,
47
+ rule_count: int,
48
+ dtype: torch.dtype = torch.float32,
49
+ ) -> None:
50
+ super().__init__()
51
+ if min(input_dim, rule_count) <= 0:
52
+ raise ValueError("input_dim and rule_count must be positive")
53
+ self.input_dim = input_dim
54
+ self.rule_count = rule_count
55
+ self.centers = nn.Parameter(torch.empty(rule_count, input_dim, dtype=dtype))
56
+ self.sigma = nn.Parameter(torch.ones(rule_count, input_dim, dtype=dtype))
57
+ nn.init.xavier_uniform_(self.centers)
58
+
59
+ def forward(self, inputs: torch.Tensor) -> torch.Tensor:
60
+ differences = inputs[:, None, :] - self.centers[None, :, :]
61
+ variance = self.sigma.square().clamp_min(torch.finfo(inputs.dtype).eps)
62
+ memberships = torch.exp(-differences.square() / variance[None, :, :])
63
+ return memberships.prod(dim=-1)
64
+
65
+
66
+ class FPINNNet(nn.Module):
67
+ """Parallel neural and fuzzy feature branches with a fused linear head."""
68
+
69
+ def __init__(
70
+ self,
71
+ hidden_layers: Sequence[int],
72
+ linear_dim: int,
73
+ fuzzy_dim: int,
74
+ activation: str = "tanh",
75
+ dtype: torch.dtype = torch.float32,
76
+ ) -> None:
77
+ super().__init__()
78
+ self.fcn = FCN(
79
+ [2, *hidden_layers, linear_dim], activation=activation, dtype=dtype
80
+ )
81
+ self.fuzzy = FuzzyLayer(2, fuzzy_dim, dtype=dtype)
82
+ self.head = nn.Linear(linear_dim + fuzzy_dim, 1, dtype=dtype)
83
+
84
+ def forward(self, coordinates: torch.Tensor) -> torch.Tensor:
85
+ neural_features = torch.tanh(self.fcn(coordinates))
86
+ fuzzy_features = self.fuzzy(coordinates)
87
+ return self.head(torch.cat((neural_features, fuzzy_features), dim=1))
88
+
89
+
90
+ class FPINNForward(nn.Module):
91
+ """Forward Allen-Cahn solver with fixed PDE parameters."""
92
+
93
+ def __init__(
94
+ self, model_config: Mapping, dtype: torch.dtype = torch.float32
95
+ ) -> None:
96
+ super().__init__()
97
+ self.net = FPINNNet(
98
+ hidden_layers=model_config["hidden_layers"],
99
+ linear_dim=int(model_config["linear_dim"]),
100
+ fuzzy_dim=int(model_config["fuzzy_dim"]),
101
+ activation=str(model_config["activation"]),
102
+ dtype=dtype,
103
+ )
104
+
105
+ def forward(self, coordinates: torch.Tensor) -> torch.Tensor:
106
+ return self.net(coordinates)
107
+
108
+ def predict_u(self, coordinates: torch.Tensor) -> torch.Tensor:
109
+ return self.forward(coordinates)
110
+
111
+
112
+ class FPINNInverse(FPINNForward):
113
+ """Inverse Allen-Cahn solver with learnable diffusion and reaction values."""
114
+
115
+ def __init__(
116
+ self,
117
+ model_config: Mapping,
118
+ initial_lambda_1: float = 1.0,
119
+ initial_lambda_2: float = 0.0,
120
+ dtype: torch.dtype = torch.float32,
121
+ ) -> None:
122
+ super().__init__(model_config, dtype=dtype)
123
+ self.lambda_1 = nn.Parameter(torch.tensor(initial_lambda_1, dtype=dtype))
124
+ self.lambda_2 = nn.Parameter(torch.tensor(initial_lambda_2, dtype=dtype))
125
+
126
+
127
+ def build_model(
128
+ task: str,
129
+ model_config: Mapping,
130
+ pde_config: Mapping,
131
+ dtype: torch.dtype,
132
+ ) -> FPINNForward:
133
+ if task == "forward":
134
+ return FPINNForward(model_config, dtype=dtype)
135
+ if task == "inverse":
136
+ return FPINNInverse(
137
+ model_config,
138
+ initial_lambda_1=float(pde_config["initial_lambda_1"]),
139
+ initial_lambda_2=float(pde_config["initial_lambda_2"]),
140
+ dtype=dtype,
141
+ )
142
+ raise ValueError(f"unsupported task: {task}")
143
+
144
+
145
+ def allen_cahn_residual(
146
+ model: FPINNForward,
147
+ coordinates: torch.Tensor,
148
+ lambda_1: float | torch.Tensor,
149
+ lambda_2: float | torch.Tensor,
150
+ ) -> torch.Tensor:
151
+ inputs = coordinates.detach().requires_grad_(True)
152
+ prediction = model(inputs)
153
+ gradient = torch.autograd.grad(
154
+ prediction, inputs, torch.ones_like(prediction), create_graph=True
155
+ )[0]
156
+ prediction_x = gradient[:, 0:1]
157
+ prediction_t = gradient[:, 1:2]
158
+ prediction_xx = torch.autograd.grad(
159
+ prediction_x,
160
+ inputs,
161
+ torch.ones_like(prediction_x),
162
+ create_graph=True,
163
+ )[0][:, 0:1]
164
+ return (
165
+ prediction_t
166
+ - lambda_1 * prediction_xx
167
+ + lambda_2 * (prediction.pow(3) - prediction)
168
+ )
169
+
170
+
171
+ def loss_components(
172
+ model: FPINNForward,
173
+ x_data: torch.Tensor,
174
+ u_data: torch.Tensor,
175
+ x_pde: torch.Tensor,
176
+ lambda_1: float | torch.Tensor,
177
+ lambda_2: float | torch.Tensor,
178
+ ) -> dict[str, torch.Tensor]:
179
+ prediction = model(x_data)
180
+ residual = allen_cahn_residual(model, x_pde, lambda_1, lambda_2)
181
+ return {
182
+ "data": torch.mean((prediction - u_data).square()),
183
+ "pde": torch.mean(residual.square()),
184
+ }
185
+
186
+
187
+ def weighted_loss(components: Mapping[str, torch.Tensor], weights: Mapping) -> torch.Tensor:
188
+ return float(weights["data"]) * components["data"] + float(
189
+ weights["pde"]
190
+ ) * components["pde"]
result/fpinn_forward_history.npz ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:34c76c41ee404a6ef8baf3dbcb39585f5b2fc42f82fcdca5e9637f6297a86fb5
3
+ size 42078
scripts/__pycache__/common.cpython-311.pyc ADDED
Binary file (4.81 kB). View file
 
scripts/__pycache__/data_utils.cpython-311.pyc ADDED
Binary file (4.94 kB). View file
 
scripts/__pycache__/inference.cpython-311.pyc ADDED
Binary file (8.97 kB). View file
 
scripts/__pycache__/train.cpython-311.pyc ADDED
Binary file (13.9 kB). View file
 
scripts/common.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import random
4
+ from collections.abc import Mapping
5
+ from pathlib import Path
6
+
7
+ import numpy as np
8
+ import torch
9
+ import yaml
10
+
11
+
12
+ def load_config(path: Path) -> dict:
13
+ with path.open("r", encoding="utf-8") as stream:
14
+ config = yaml.safe_load(stream)
15
+ if not isinstance(config, dict) or "root" not in config:
16
+ raise ValueError(f"config must contain a 'root' mapping: {path}")
17
+ return config["root"]
18
+
19
+
20
+ def project_path(value: str | Path, project_root: Path) -> Path:
21
+ path = Path(value).expanduser()
22
+ return path if path.is_absolute() else project_root / path
23
+
24
+
25
+ def resolve_device(requested: str) -> torch.device:
26
+ if requested == "auto":
27
+ return torch.device("cuda" if torch.cuda.is_available() else "cpu")
28
+ device = torch.device(requested)
29
+ if device.type == "cuda" and not torch.cuda.is_available():
30
+ raise RuntimeError("CUDA/DCU was requested but torch.cuda.is_available() is false")
31
+ return device
32
+
33
+
34
+ def resolve_dtype(name: str) -> torch.dtype:
35
+ try:
36
+ return {"float32": torch.float32, "float64": torch.float64}[name]
37
+ except KeyError as error:
38
+ raise ValueError(f"unsupported dtype: {name}") from error
39
+
40
+
41
+ def seed_everything(seed: int) -> None:
42
+ random.seed(seed)
43
+ np.random.seed(seed)
44
+ torch.manual_seed(seed)
45
+ if torch.cuda.is_available():
46
+ torch.cuda.manual_seed_all(seed)
47
+
48
+
49
+ def relative_l2(prediction: np.ndarray, reference: np.ndarray) -> float:
50
+ return float(
51
+ np.linalg.norm(prediction.reshape(-1) - reference.reshape(-1))
52
+ / (np.linalg.norm(reference.reshape(-1)) + 1.0e-12)
53
+ )
54
+
55
+
56
+ def checkpoint_state(checkpoint: Mapping) -> tuple[Mapping[str, torch.Tensor], dict]:
57
+ if "model_state" in checkpoint:
58
+ return checkpoint["model_state"], dict(checkpoint)
59
+ if checkpoint and all(torch.is_tensor(value) for value in checkpoint.values()):
60
+ return checkpoint, {}
61
+ raise ValueError("checkpoint contains no valid FPINN model state")
scripts/data_utils.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from pathlib import Path
5
+
6
+ import numpy as np
7
+ import scipy.io
8
+ import torch
9
+
10
+
11
+ def load_allen_cahn(path: Path) -> dict[str, np.ndarray | tuple[int, int]]:
12
+ if not path.is_file():
13
+ raise FileNotFoundError(f"Allen-Cahn data not found: {path}")
14
+ data = scipy.io.loadmat(path)
15
+ missing = {"tt", "x", "uu"}.difference(data)
16
+ if missing:
17
+ raise ValueError(f"Allen-Cahn data is missing fields: {sorted(missing)}")
18
+ time_axis = np.asarray(data["tt"], dtype=np.float64).reshape(-1)
19
+ space_axis = np.asarray(data["x"], dtype=np.float64).reshape(-1)
20
+ raw_solution = np.real(np.asarray(data["uu"]))
21
+ expected_shape = (space_axis.size, time_axis.size)
22
+ if raw_solution.shape != expected_shape:
23
+ raise ValueError(
24
+ f"uu shape must be {expected_shape} in [x,t] order, got {raw_solution.shape}"
25
+ )
26
+ exact_grid = raw_solution.T
27
+ mesh_x, mesh_t = np.meshgrid(space_axis, time_axis, indexing="xy")
28
+ coordinates = np.column_stack((mesh_x.ravel(), mesh_t.ravel()))
29
+ exact = exact_grid.reshape(-1, 1)
30
+ return {
31
+ "time": time_axis,
32
+ "space": space_axis,
33
+ "coordinates": coordinates,
34
+ "exact": exact,
35
+ "grid_shape": exact_grid.shape,
36
+ }
37
+
38
+
39
+ def sample_training_data(
40
+ dataset: Mapping,
41
+ n_train: int,
42
+ seed: int,
43
+ device: torch.device,
44
+ dtype: torch.dtype,
45
+ ) -> tuple[torch.Tensor, torch.Tensor]:
46
+ coordinates = np.asarray(dataset["coordinates"])
47
+ exact = np.asarray(dataset["exact"])
48
+ if n_train <= 0 or n_train > coordinates.shape[0]:
49
+ raise ValueError(
50
+ f"n_train must be between 1 and {coordinates.shape[0]}, got {n_train}"
51
+ )
52
+ generator = np.random.default_rng(seed)
53
+ indices = generator.choice(coordinates.shape[0], n_train, replace=False)
54
+ return (
55
+ torch.as_tensor(coordinates[indices], dtype=dtype, device=device),
56
+ torch.as_tensor(exact[indices], dtype=dtype, device=device),
57
+ )
58
+
59
+
60
+ def batched_predict(
61
+ model: torch.nn.Module,
62
+ coordinates: np.ndarray,
63
+ batch_size: int,
64
+ device: torch.device,
65
+ dtype: torch.dtype,
66
+ ) -> np.ndarray:
67
+ if batch_size <= 0:
68
+ raise ValueError("evaluation batch size must be positive")
69
+ predictions = []
70
+ model.eval()
71
+ with torch.no_grad():
72
+ for start in range(0, coordinates.shape[0], batch_size):
73
+ batch = torch.as_tensor(
74
+ coordinates[start : start + batch_size], dtype=dtype, device=device
75
+ )
76
+ predictions.append(model.predict_u(batch).cpu().numpy())
77
+ return np.concatenate(predictions, axis=0)
scripts/inference.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from collections.abc import Mapping
5
+ from pathlib import Path
6
+
7
+ import matplotlib
8
+
9
+ matplotlib.use("Agg")
10
+ import matplotlib.pyplot as plt
11
+ import numpy as np
12
+ import torch
13
+
14
+
15
+ PROJECT_ROOT = Path(__file__).resolve().parents[1]
16
+ sys.path.insert(0, str(PROJECT_ROOT))
17
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
18
+
19
+ from common import ( # noqa: E402
20
+ checkpoint_state,
21
+ load_config,
22
+ project_path,
23
+ relative_l2,
24
+ resolve_device,
25
+ resolve_dtype,
26
+ )
27
+ from data_utils import batched_predict, load_allen_cahn # noqa: E402
28
+ from model.fpinn import build_model # noqa: E402
29
+
30
+
31
+ DEFAULT_CONFIG = PROJECT_ROOT / "conf" / "config.yaml"
32
+ TASKS = ("forward", "inverse")
33
+
34
+
35
+ def main() -> None:
36
+ config_path = DEFAULT_CONFIG.resolve()
37
+ config = load_config(config_path)
38
+ common = config["common"]
39
+ task = str(common["task"]).lower()
40
+ if task not in TASKS:
41
+ raise ValueError("common.task must be one of: forward, inverse")
42
+ task_config = config["tasks"][task]
43
+ device = resolve_device(str(common["device"]))
44
+ dtype = resolve_dtype(str(common["dtype"]))
45
+ weight_dir = project_path(common["weight_dir"], PROJECT_ROOT)
46
+ result_dir = project_path(common["result_dir"], PROJECT_ROOT)
47
+ checkpoint_path = weight_dir / task_config["output"]["checkpoint_name"]
48
+ if not checkpoint_path.is_file():
49
+ raise FileNotFoundError(
50
+ f"checkpoint not found: {checkpoint_path}. Run scripts/train.py first."
51
+ )
52
+ checkpoint = torch.load(checkpoint_path, map_location="cpu", weights_only=True)
53
+ if not isinstance(checkpoint, Mapping):
54
+ raise ValueError(f"invalid FPINN checkpoint: {checkpoint_path}")
55
+ state, metadata = checkpoint_state(checkpoint)
56
+ checkpoint_task = metadata.get("task", task)
57
+ if checkpoint_task != task:
58
+ raise ValueError(
59
+ f"checkpoint task is {checkpoint_task}, but common.task is {task}"
60
+ )
61
+ model_config = metadata.get("model_config", config["model"])
62
+ pde_config = metadata.get("pde_config", task_config["pde"])
63
+ data_config = dict(metadata.get("data_config", config["data"]))
64
+ data_path = project_path(data_config["mat_file"], PROJECT_ROOT)
65
+ dataset = load_allen_cahn(data_path)
66
+ model = build_model(task, model_config, pde_config, dtype).to(
67
+ device=device, dtype=dtype
68
+ )
69
+ model.load_state_dict(state, strict=True)
70
+ model.eval()
71
+ prediction = batched_predict(
72
+ model,
73
+ np.asarray(dataset["coordinates"]),
74
+ int(data_config["evaluation_batch_size"]),
75
+ device,
76
+ dtype,
77
+ )
78
+ exact = np.asarray(dataset["exact"])
79
+ error = relative_l2(prediction, exact)
80
+ rmse = float(np.sqrt(np.mean((prediction - exact) ** 2)))
81
+ max_error = float(np.max(np.abs(prediction - exact)))
82
+
83
+ result_dir.mkdir(parents=True, exist_ok=True)
84
+ predictions_path = result_dir / task_config["output"]["predictions_name"]
85
+ figure_path = result_dir / task_config["output"]["figure_name"]
86
+ payload = {
87
+ "coordinates": dataset["coordinates"],
88
+ "exact": exact,
89
+ "prediction": prediction,
90
+ "relative_l2": error,
91
+ "rmse": rmse,
92
+ "max_abs_error": max_error,
93
+ }
94
+ if task == "inverse":
95
+ payload["lambda_1"] = model.lambda_1.item()
96
+ payload["lambda_2"] = model.lambda_2.item()
97
+ np.savez_compressed(predictions_path, **payload)
98
+
99
+ shape = dataset["grid_shape"]
100
+ exact_grid = exact.reshape(shape)
101
+ prediction_grid = prediction.reshape(shape)
102
+ error_grid = np.abs(prediction_grid - exact_grid)
103
+ extent = (
104
+ float(np.min(dataset["space"])),
105
+ float(np.max(dataset["space"])),
106
+ float(np.min(dataset["time"])),
107
+ float(np.max(dataset["time"])),
108
+ )
109
+ figure, axes = plt.subplots(1, 3, figsize=(15, 4))
110
+ for axis, title, field in zip(
111
+ axes,
112
+ ("Exact", "FPINN", "Absolute error"),
113
+ (exact_grid, prediction_grid, error_grid),
114
+ strict=True,
115
+ ):
116
+ image = axis.imshow(
117
+ field,
118
+ extent=extent,
119
+ origin="lower",
120
+ aspect="auto",
121
+ cmap="viridis" if title != "Absolute error" else "hot",
122
+ )
123
+ axis.set_title(title)
124
+ axis.set_xlabel("x")
125
+ axis.set_ylabel("t")
126
+ figure.colorbar(image, ax=axis)
127
+ title = f"Allen-Cahn {task} | L2={error:.3e}"
128
+ if task == "inverse":
129
+ title += (
130
+ f" | lambda=({model.lambda_1.item():.3e}, "
131
+ f"{model.lambda_2.item():.3e})"
132
+ )
133
+ figure.suptitle(title)
134
+ figure.tight_layout()
135
+ figure.savefig(figure_path, dpi=150)
136
+ plt.close(figure)
137
+
138
+ print(f"Config: {config_path}")
139
+ print(f"Task: {task}")
140
+ print(f"Data: {data_path}")
141
+ print(f"Checkpoint: {checkpoint_path}")
142
+ print(f"Device: {device}")
143
+ print(f"Relative L2={error:.6e}, RMSE={rmse:.6e}, MaxAbs={max_error:.6e}")
144
+ if task == "inverse":
145
+ print(
146
+ f"Recovered lambda_1={model.lambda_1.item():.8e}, "
147
+ f"lambda_2={model.lambda_2.item():.8e}"
148
+ )
149
+ print(f"Predictions: {predictions_path}")
150
+ print(f"Plot: {figure_path}")
151
+
152
+
153
+ if __name__ == "__main__":
154
+ main()
scripts/train.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ import time
5
+ from pathlib import Path
6
+
7
+ import numpy as np
8
+ import torch
9
+
10
+
11
+ PROJECT_ROOT = Path(__file__).resolve().parents[1]
12
+ sys.path.insert(0, str(PROJECT_ROOT))
13
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
14
+
15
+ from common import ( # noqa: E402
16
+ load_config,
17
+ project_path,
18
+ relative_l2,
19
+ resolve_device,
20
+ resolve_dtype,
21
+ seed_everything,
22
+ )
23
+ from data_utils import batched_predict, load_allen_cahn, sample_training_data # noqa: E402
24
+ from model.fpinn import build_model, loss_components, weighted_loss # noqa: E402
25
+
26
+
27
+ DEFAULT_CONFIG = PROJECT_ROOT / "conf" / "config.yaml"
28
+ TASKS = ("forward", "inverse")
29
+
30
+
31
+ def main() -> None:
32
+ config_path = DEFAULT_CONFIG.resolve()
33
+ config = load_config(config_path)
34
+ common = config["common"]
35
+ task = str(common["task"]).lower()
36
+ if task not in TASKS:
37
+ raise ValueError("common.task must be one of: forward, inverse")
38
+ task_config = config["tasks"][task]
39
+ device = resolve_device(str(common["device"]))
40
+ dtype = resolve_dtype(str(common["dtype"]))
41
+ seed = int(common["seed"])
42
+ epochs = int(task_config["training"]["epochs"])
43
+ lbfgs_iters = int(task_config["training"]["lbfgs_iters"])
44
+ learning_rate = float(task_config["training"]["lr"])
45
+ if min(epochs, lbfgs_iters) < 0 or epochs + lbfgs_iters == 0:
46
+ raise ValueError("at least one optimizer iteration count must be positive")
47
+ if learning_rate <= 0:
48
+ raise ValueError("learning rate must be positive")
49
+
50
+ data_config = dict(config["data"])
51
+ data_path = project_path(data_config["mat_file"], PROJECT_ROOT)
52
+ weight_dir = project_path(common["weight_dir"], PROJECT_ROOT)
53
+ result_dir = project_path(common["result_dir"], PROJECT_ROOT)
54
+ checkpoint_path = weight_dir / task_config["output"]["checkpoint_name"]
55
+ history_path = result_dir / task_config["output"]["history_name"]
56
+ seed_everything(seed)
57
+
58
+ dataset = load_allen_cahn(data_path)
59
+ x_train, u_train = sample_training_data(
60
+ dataset,
61
+ int(data_config["n_train"]),
62
+ seed,
63
+ device,
64
+ dtype,
65
+ )
66
+ model = build_model(task, config["model"], task_config["pde"], dtype).to(
67
+ device=device, dtype=dtype
68
+ )
69
+ parameter_count = sum(parameter.numel() for parameter in model.parameters())
70
+ if task == "forward":
71
+ lambda_1 = float(task_config["pde"]["lambda_1"])
72
+ lambda_2 = float(task_config["pde"]["lambda_2"])
73
+ else:
74
+ lambda_1 = model.lambda_1
75
+ lambda_2 = model.lambda_2
76
+
77
+ print(f"Config: {config_path}")
78
+ print(f"Task: {task}")
79
+ print(f"Data: {data_path}")
80
+ print(f"Device: {device}")
81
+ print(f"Training points: {x_train.shape[0]}")
82
+ print(f"Parameters: {parameter_count:,}")
83
+
84
+ with torch.enable_grad():
85
+ sample = x_train[: min(10, x_train.shape[0])].detach().requires_grad_(True)
86
+ test_function = sample[:, 0:1].square() + sample[:, 1:2]
87
+ gradient = torch.autograd.grad(
88
+ test_function,
89
+ sample,
90
+ torch.ones_like(test_function),
91
+ create_graph=True,
92
+ )[0]
93
+ second = torch.autograd.grad(
94
+ gradient[:, 0:1],
95
+ sample,
96
+ torch.ones_like(gradient[:, 0:1]),
97
+ create_graph=True,
98
+ )[0][:, 0:1]
99
+ if not torch.allclose(gradient[:, 1:2], torch.ones_like(second), atol=1.0e-5):
100
+ raise RuntimeError("u_t autograd validation failed")
101
+ if not torch.allclose(second, torch.full_like(second, 2.0), atol=1.0e-5):
102
+ raise RuntimeError("u_xx autograd validation failed")
103
+ print("Autograd validation: passed")
104
+
105
+ def current_loss() -> tuple[torch.Tensor, dict[str, torch.Tensor]]:
106
+ components = loss_components(
107
+ model, x_train, u_train, x_train, lambda_1, lambda_2
108
+ )
109
+ return weighted_loss(components, task_config["loss"]), components
110
+
111
+ def evaluate() -> float:
112
+ prediction = batched_predict(
113
+ model,
114
+ np.asarray(dataset["coordinates"]),
115
+ int(data_config["evaluation_batch_size"]),
116
+ device,
117
+ dtype,
118
+ )
119
+ return relative_l2(prediction, np.asarray(dataset["exact"]))
120
+
121
+ optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
122
+ log_interval = int(task_config["training"]["log_interval"])
123
+ loss_history = []
124
+ l2_history = []
125
+ lambda_history = []
126
+ best_l2 = evaluate()
127
+ started = time.time()
128
+ for epoch in range(1, epochs + 1):
129
+ loss, components = current_loss()
130
+ if not torch.isfinite(loss):
131
+ raise FloatingPointError(f"FPINN loss became non-finite at epoch {epoch}")
132
+ optimizer.zero_grad(set_to_none=True)
133
+ loss.backward()
134
+ optimizer.step()
135
+ loss_history.append(loss.item())
136
+ if task == "inverse":
137
+ lambda_history.append((model.lambda_1.item(), model.lambda_2.item()))
138
+
139
+ if epoch == 1 or epoch % log_interval == 0 or epoch == epochs:
140
+ error = evaluate()
141
+ l2_history.append((epoch, error))
142
+ best_l2 = min(best_l2, error)
143
+ lambda_text = (
144
+ ""
145
+ if task == "forward"
146
+ else f" lambda=({model.lambda_1.item():.6e}, {model.lambda_2.item():.6e})"
147
+ )
148
+ print(
149
+ f"epoch={epoch:6d} loss={loss.item():.3e} "
150
+ f"data={components['data'].item():.3e} "
151
+ f"pde={components['pde'].item():.3e} l2={error:.3e}{lambda_text}"
152
+ )
153
+
154
+ if lbfgs_iters > 0:
155
+ lbfgs = torch.optim.LBFGS(
156
+ model.parameters(),
157
+ lr=1.0,
158
+ max_iter=lbfgs_iters,
159
+ max_eval=max(1, 2 * lbfgs_iters),
160
+ history_size=50,
161
+ tolerance_grad=1.0e-5,
162
+ tolerance_change=float(np.finfo(float).eps),
163
+ line_search_fn="strong_wolfe",
164
+ )
165
+
166
+ def closure() -> torch.Tensor:
167
+ lbfgs.zero_grad(set_to_none=True)
168
+ closure_loss, _ = current_loss()
169
+ if not torch.isfinite(closure_loss):
170
+ raise FloatingPointError("FPINN L-BFGS loss became non-finite")
171
+ closure_loss.backward()
172
+ return closure_loss
173
+
174
+ lbfgs.step(closure)
175
+ error = evaluate()
176
+ l2_history.append((epochs + lbfgs_iters, error))
177
+ best_l2 = min(best_l2, error)
178
+ print(f"L-BFGS relative L2={error:.6e}")
179
+
180
+ final_l2 = evaluate()
181
+ elapsed = time.time() - started
182
+ weight_dir.mkdir(parents=True, exist_ok=True)
183
+ result_dir.mkdir(parents=True, exist_ok=True)
184
+ checkpoint = {
185
+ "task": task,
186
+ "case": "allen_cahn",
187
+ "architecture": "fuzzy_pinn",
188
+ "model_state": model.state_dict(),
189
+ "model_config": config["model"],
190
+ "pde_config": task_config["pde"],
191
+ "data_config": data_config,
192
+ "seed": seed,
193
+ "epochs": epochs,
194
+ "lbfgs_iters": lbfgs_iters,
195
+ "final_l2": final_l2,
196
+ }
197
+ torch.save(checkpoint, checkpoint_path)
198
+ l2_array = np.asarray(l2_history, dtype=np.float64).reshape(-1, 2)
199
+ lambda_array = np.asarray(lambda_history, dtype=np.float64).reshape(-1, 2)
200
+ np.savez_compressed(
201
+ history_path,
202
+ loss=np.asarray(loss_history, dtype=np.float64),
203
+ l2_steps=l2_array[:, 0],
204
+ l2_values=l2_array[:, 1],
205
+ lambda_1=lambda_array[:, 0] if lambda_array.size else np.array([]),
206
+ lambda_2=lambda_array[:, 1] if lambda_array.size else np.array([]),
207
+ final_l2=final_l2,
208
+ best_l2=best_l2,
209
+ elapsed_seconds=elapsed,
210
+ parameter_count=parameter_count,
211
+ )
212
+ print(f"Final relative L2={final_l2:.6e}, elapsed={elapsed:.1f}s")
213
+ if task == "inverse":
214
+ print(
215
+ f"Recovered lambda_1={model.lambda_1.item():.8e}, "
216
+ f"lambda_2={model.lambda_2.item():.8e}"
217
+ )
218
+ print(f"Saved checkpoint: {checkpoint_path}")
219
+ print(f"Saved history: {history_path}")
220
+
221
+
222
+ if __name__ == "__main__":
223
+ main()
weight/.gitkeep ADDED
File without changes
weight/fpinn_forward.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:657474331eae29e21f6bfb1bd4ce9bea8d73c39e50e32190a5007826688d311d
3
+ size 498456