-
Notifications
You must be signed in to change notification settings - Fork 25
Adding supporting code for blog post #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mmarkows17
wants to merge
10
commits into
main
Choose a base branch
from
PhiML_blog_supporting_code
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
d35594a
Adding PhiML blog post supporting code
mmarkows17 2183e8e
addressing review comments for HNN example
mmarkows17 c54037b
edits suggested by reviewers
mmarkows17 eaa1994
editing readme
mmarkows17 0156c93
getting rid of project by adding data generation scripts and includin…
mmarkows17 50c13ce
updating the startup instructions in the readme to remove the prj and…
mmarkows17 287ed60
adding PINN m and readme files
mmarkows17 10ef65e
removing extraneous rng calls and modified initial condition in FNO e…
mmarkows17 a22a59d
changing sqrt g to omega0
mmarkows17 ce78b86
review edits plus minor typos in data generation files
mmarkows17 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
|
||
# Fourier Neural Operator for Nonlinear Pendulum | ||
|
||
This example demonstrates how to use a Fourier Neural Operator (FNO) to model the dynamics of a nonlinear pendulum with a time\-dependent forcing term. The governing equation is given by the following second\-order ordinary differential equation (ODE): | ||
|
||
|
||
$\ddot{\theta} (t)=-\omega_0^2 \sin (\theta (t))+f(t)$, | ||
|
||
|
||
where $\theta$ is the angular position, $\omega_0$ is the natural frequency, and $f$ is the an external forcing function. | ||
|
||
|
||
Traditionally, solving this system for a new forcing function $f$ requires numerically solving the ODE from scratch. In constrast, an FNO learns a mapping from the input forcing function $f$ to the corresponding solution $\theta$, enabling rapid inference without re\-solving the equation. | ||
|
||
|
||
The FNO is trained on a dataset of input\-output pairs $(f_i (t),\theta_i (t))$, and once trained, it can predict $\theta$ for unseen forcing functions. While this approach may be excessive for simple ODEs like the pendulum, it can be advantageous for complex simulations involving partial differential equations (PDEs), where traditional solvers (e.g. finite element method) are computationally expensive. | ||
|
||
# Generate or Import Data | ||
```matlab | ||
% Get the path to the main directory | ||
mainDir = findProjectRoot('generatePendulumDataFNO.m'); | ||
% If first time generating data, set generateData to true. Else, set to false. | ||
res = 512; | ||
generateData = 1; | ||
if generateData | ||
g = 9.81; r = 1; | ||
omega0 = sqrt(g/r); | ||
x0 = [0.5;0.5*omega0]; | ||
numSamples = 2000; | ||
doPlot = 0; | ||
generatePendulumDataFNO(omega0,x0,numSamples,res,doPlot); | ||
end | ||
% Construct full path to the data file | ||
dataFile = fullfile(mainDir, 'pendulumData', sprintf('fno_data_%d.mat',res)); | ||
% Read the data | ||
load(dataFile,'data'); | ||
res = 512; | ||
|
||
f = data.fSamples; | ||
theta = data.thetaSamples; | ||
t = data.tGrid; | ||
|
||
% normalize and center the data | ||
fMean = mean(f, 'all'); | ||
fStd = std(f, 0, 'all'); | ||
thetaMean = mean(theta, 'all'); | ||
thetaStd = std(theta, 0, 'all'); | ||
f = (f - fMean) / fStd; | ||
theta = (theta - thetaMean) / thetaStd; | ||
|
||
% visualize some of the training data | ||
numPlots = 3; | ||
figure | ||
tiledlayout(numPlots,2) | ||
for i = 1:numPlots | ||
nexttile | ||
plot(t,f(i,:)); | ||
title("Observation " + i + newline + "Forcing Function") | ||
xlabel("$t$",Interpreter='latex'); | ||
ylabel("$f(t)$",Interpreter='latex'); | ||
|
||
nexttile | ||
plot(t,theta(i,:)); | ||
title("ODE Solution") | ||
xlabel("$t$",Interpreter='latex') | ||
ylabel("$\theta(t)$",Interpreter='latex') | ||
end | ||
``` | ||
|
||
 | ||
# Prepare Training Data | ||
```matlab | ||
numObservations = size(f,1); | ||
[idxTrain,idxValidation,idxTest] = trainingPartitions(numObservations,[0.8 0.1 0.1]); | ||
fTrain = f(idxTrain,:); | ||
thetaTrain = theta(idxTrain,:); | ||
fValidation = f(idxValidation,:); | ||
thetaValidation = theta(idxValidation,:); | ||
fTest = f(idxTest,:); | ||
thetaTest = theta(idxTest,:); | ||
``` | ||
|
||
FNO requires input data which contains spatio\-temporal information. Concatenate the grid with the input data. | ||
|
||
```matlab | ||
tGridTrain = repmat(t, [numel(idxTrain) 1]); | ||
tGridValidation = repmat(t, [numel(idxValidation) 1]); | ||
fTrain = cat(3,fTrain,tGridTrain); | ||
fValidation = cat(3, fValidation, tGridValidation); | ||
|
||
size(fTrain) | ||
``` | ||
|
||
```matlabTextOutput | ||
ans = 1x3 | ||
1600 512 2 | ||
|
||
``` | ||
|
||
```matlab | ||
size(fValidation) | ||
``` | ||
|
||
```matlabTextOutput | ||
ans = 1x3 | ||
200 512 2 | ||
|
||
``` | ||
|
||
# Define Neural Network Architecture | ||
|
||
Network consists of multiple Fourier\-GeLU blocks connected in series. | ||
|
||
```matlab | ||
numModes = 16; | ||
tWidth = 128; | ||
numBlocks = 8; | ||
|
||
fourierBlock = [ | ||
fourierLayer(numModes,tWidth) | ||
geluLayer]; | ||
|
||
layers = [ | ||
inputLayer([NaN tWidth 2],"BSC"); | ||
convolution1dLayer(1,tWidth,Name="fc0") | ||
repmat(fourierBlock,[numBlocks 1]) | ||
convolution1dLayer(1,128) | ||
geluLayer | ||
convolution1dLayer(1,1)]; | ||
``` | ||
# Specify Training Options | ||
```matlab | ||
schedule = piecewiseLearnRate(DropFactor=0.5); | ||
|
||
options = trainingOptions("adam", ... | ||
InitialLearnRate=1e-3, ... | ||
LearnRateSchedule=schedule, ... | ||
MaxEpochs=10, ... | ||
MiniBatchSize=64, ... | ||
Shuffle="every-epoch", ... | ||
InputDataFormats="BSC", ... | ||
Plots="training-progress", ... | ||
ValidationData={fValidation,thetaValidation}, ... | ||
Verbose=false); | ||
``` | ||
# Train the Network | ||
```matlab | ||
net = trainnet(fTrain,thetaTrain,layers,"mse",options); | ||
``` | ||
|
||
 | ||
# Test the Model and Visualize Results | ||
```matlab | ||
tGridTest = repmat(t, [numel(idxTest) 1]); | ||
|
||
fTest = cat(3,fTest,tGridTest); | ||
mseTest = testnet(net,fTest,thetaTest,"mse") | ||
``` | ||
|
||
```matlabTextOutput | ||
mseTest = 6.0968e-04 | ||
``` | ||
|
||
|
||
Visualize the predictions on the test set | ||
|
||
```matlab | ||
Y = minibatchpredict(net, fTest); | ||
numTestPlots = 3; | ||
for i = 1:numTestPlots | ||
figure(); | ||
subplot(2,1,1); | ||
plot(t,fTest(i,:,1),LineWidth=2.5) | ||
title("Forcing Function") | ||
xlabel("$t$",Interpreter="latex") | ||
ylabel("$f(t)$",Interpreter="latex") | ||
set(gca,FontSize=14,LineWidth=2.5) | ||
subplot(2,1,2) | ||
plot(t,Y(i,:),'b-',LineWidth=2.5,DisplayName='FNO'); hold on | ||
plot(t,thetaTest(i,:),'k--',LineWidth=2.5,DisplayName='True Solution'); hold off | ||
title("Angular Position") | ||
xlabel("$t$",Interpreter="latex") | ||
ylabel("$\theta(t)$",Interpreter="latex") | ||
legend(Location='best'); | ||
set(gca,FontSize=14,LineWidth=2.5) | ||
end | ||
``` | ||
|
||
 | ||
|
||
 | ||
|
||
 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could these plots be made such that the y axis labels don't overlap, and the legend isn't blocking the plot? |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
153 changes: 153 additions & 0 deletions
153
phiml-blog-supporting-code/FNO/FNO_nonlinear_pendulum_code.m
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
%% Fourier Neural Operator for Nonlinear Pendulum | ||
% This example demonstrates how to use a Fourier Neural Operator (FNO) to model | ||
% the dynamics of a nonlinear pendulum with a time-dependent forcing term. The | ||
% governing equation is given by the following second-order ordinary differential | ||
% equation (ODE): | ||
% | ||
% $\ddot{\theta}(t) = -\omega_0^2\sin(\theta(t)) + f(t)$, | ||
% | ||
% where $\theta$ is the angular position, $\omega_0$ is the natural frequency, | ||
% and $f$ is the an external forcing function. | ||
% | ||
% Traditionally, solving this system for a new forcing function $f$ requires | ||
% numerically solving the ODE from scratch. In constrast, an FNO learns a mapping | ||
% from the input forcing function $f$ to the corresponding solution $\theta$, | ||
% enabling rapid inference without re-solving the equation. | ||
% | ||
% The FNO is trained on a dataset of input-output pairs $(f_i(t), \theta_i(t))$, | ||
% and once trained, it can predict $\theta$ for unseen forcing functions. While | ||
% this approach may be excessive for simple ODEs like the pendulum, it can be | ||
% advantageous for complex simulations involving partial differential equations | ||
% (PDEs), where traditional solvers (e.g. finite element method) are computationally | ||
% expensive. | ||
|
||
%% Generate or Import Data | ||
|
||
% Get the path to the main directory | ||
mainDir = findProjectRoot('generatePendulumDataFNO.m'); | ||
% If first time generating data, set generateData to true. Else, set to false. | ||
res = 512; | ||
generateData = 1; | ||
if generateData | ||
g = 9.81; r = 1; | ||
omega0 = sqrt(g/r); | ||
x0 = [0.5;0.5*omega0]; | ||
numSamples = 2000; | ||
doPlot = 0; | ||
generatePendulumDataFNO(omega0,x0,numSamples,res,doPlot); | ||
end | ||
% Construct full path to the data file | ||
dataFile = fullfile(mainDir, 'pendulumData', sprintf('fno_data_%d.mat',res)); | ||
% Read the data | ||
load(dataFile,'data'); | ||
res = 512; | ||
|
||
f = data.fSamples; | ||
theta = data.thetaSamples; | ||
t = data.tGrid; | ||
|
||
% normalize and center the data | ||
fMean = mean(f, 'all'); | ||
fStd = std(f, 0, 'all'); | ||
thetaMean = mean(theta, 'all'); | ||
thetaStd = std(theta, 0, 'all'); | ||
f = (f - fMean) / fStd; | ||
theta = (theta - thetaMean) / thetaStd; | ||
|
||
% visualize some of the training data | ||
numPlots = 3; | ||
figure | ||
tiledlayout(numPlots,2) | ||
for i = 1:numPlots | ||
nexttile | ||
plot(t,f(i,:)); | ||
title("Observation " + i + newline + "Forcing Function") | ||
xlabel("$t$",Interpreter='latex'); | ||
ylabel("$f(t)$",Interpreter='latex'); | ||
nexttile | ||
plot(t,theta(i,:)); | ||
title("ODE Solution") | ||
xlabel("$t$",Interpreter='latex') | ||
ylabel("$\theta(t)$",Interpreter='latex') | ||
end | ||
%% Prepare Training Data | ||
|
||
numObservations = size(f,1); | ||
[idxTrain,idxValidation,idxTest] = trainingPartitions(numObservations,[0.8 0.1 0.1]); | ||
fTrain = f(idxTrain,:); | ||
thetaTrain = theta(idxTrain,:); | ||
fValidation = f(idxValidation,:); | ||
thetaValidation = theta(idxValidation,:); | ||
fTest = f(idxTest,:); | ||
thetaTest = theta(idxTest,:); | ||
%% | ||
% FNO requires input data which contains spatio-temporal information. Concatenate | ||
% the grid with the input data. | ||
|
||
tGridTrain = repmat(t, [numel(idxTrain) 1]); | ||
tGridValidation = repmat(t, [numel(idxValidation) 1]); | ||
fTrain = cat(3,fTrain,tGridTrain); | ||
fValidation = cat(3, fValidation, tGridValidation); | ||
%% Define Neural Network Architecture | ||
% Network consists of multiple Fourier-GeLU blocks connected in series. | ||
|
||
numModes = 16; | ||
tWidth = 128; | ||
numBlocks = 8; | ||
|
||
fourierBlock = [ | ||
fourierLayer(numModes,tWidth) | ||
geluLayer]; | ||
|
||
layers = [ | ||
inputLayer([NaN tWidth 2],"BSC"); | ||
convolution1dLayer(1,tWidth,Name="fc0") | ||
repmat(fourierBlock,[numBlocks 1]) | ||
convolution1dLayer(1,128) | ||
geluLayer | ||
convolution1dLayer(1,1)]; | ||
%% Specify Training Options | ||
|
||
schedule = piecewiseLearnRate(DropFactor=0.5); | ||
|
||
options = trainingOptions("adam", ... | ||
InitialLearnRate=1e-3, ... | ||
LearnRateSchedule=schedule, ... | ||
MaxEpochs=10, ... | ||
MiniBatchSize=64, ... | ||
Shuffle="every-epoch", ... | ||
InputDataFormats="BSC", ... | ||
Plots="training-progress", ... | ||
ValidationData={fValidation,thetaValidation}, ... | ||
Verbose=false); | ||
%% Train the Network | ||
|
||
net = trainnet(fTrain,thetaTrain,layers,"mse",options); | ||
%% Test the Model and Visualize Results | ||
|
||
tGridTest = repmat(t, [numel(idxTest) 1]); | ||
|
||
fTest = cat(3,fTest,tGridTest); | ||
mseTest = testnet(net,fTest,thetaTest,"mse"); | ||
%% | ||
% Visualize the predictions on the test set | ||
|
||
Y = minibatchpredict(net, fTest); | ||
numTestPlots = 3; | ||
for i = 1:numTestPlots | ||
figure(); | ||
subplot(2,1,1); | ||
plot(t,fTest(i,:,1),LineWidth=2.5) | ||
title("Forcing Function") | ||
xlabel("$t$",Interpreter="latex") | ||
ylabel("$f(t)$",Interpreter="latex") | ||
set(gca,FontSize=14,LineWidth=2.5) | ||
subplot(2,1,2) | ||
plot(t,Y(i,:),'b-',LineWidth=2.5,DisplayName='FNO'); hold on | ||
plot(t,thetaTest(i,:),'k--',LineWidth=2.5,DisplayName='True Solution'); hold off | ||
title("Angular Position") | ||
xlabel("$t$",Interpreter="latex") | ||
ylabel("$\theta(t)$",Interpreter="latex") | ||
legend(Location='best'); | ||
set(gca,FontSize=14,LineWidth=2.5) | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's nice if the alt text here is descriptive, like "Solution Plots" or something. Not sure if we have any control over that for MLX to MD export.