Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
P
PDE-VKOGA
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
pub
ians-anm
PDE-VKOGA
Commits
2ed0814e
Commit
2ed0814e
authored
3 years ago
by
Wenzel, Tizian
Browse files
Options
Downloads
Patches
Plain Diff
Initial commit.
parent
4e906c04
No related branches found
No related tags found
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
.gitignore
+156
-0
156 additions, 0 deletions
.gitignore
example.py
+73
-0
73 additions, 0 deletions
example.py
kernels_PDE.py
+237
-0
237 additions, 0 deletions
kernels_PDE.py
vkoga_PDE.py
+594
-0
594 additions, 0 deletions
vkoga_PDE.py
with
1060 additions
and
0 deletions
.gitignore
0 → 100644
+
156
−
0
View file @
2ed0814e
# Added Tiz
tb_logs/
*.npy
# Created by .ignore support plugin (hsz.mobi)
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# IPython Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# dotenv
.env
# virtualenv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject
### VirtualEnv template
# Virtualenv
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
.Python
[Bb]in
[Ii]nclude
[Ll]ib
[Ll]ib64
[Ll]ocal
[Ss]cripts
pyvenv.cfg
.venv
pip-selfcheck.json
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/workspace.xml
.idea/tasks.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
# Sensitive or high-churn files:
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# Gradle:
.idea/gradle.xml
.idea/libraries
# Mongo Explorer plugin:
.idea/mongoSettings.xml
.idea/
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
This diff is collapsed.
Click to expand it.
example.py
0 → 100644
+
73
−
0
View file @
2ed0814e
# Example file to demonstrate the use of the VKOGA-PDE code:
# We use \Omega = [0, 1]^2, L = -LaPlace and the Gaussian kernel.
# Some imports
import
numpy
as
np
from
matplotlib
import
pyplot
as
plt
from
vkoga_pde.kernels_PDE
import
Gaussian_laplace
from
vkoga_pde.vkoga_PDE
import
VKOGA_PDE
np
.
random
.
seed
(
1
)
# Create data set: Square domain
dim
=
2
scale_domain
=
1
X1
=
scale_domain
*
np
.
random
.
rand
(
int
(
1e4
),
dim
)
X2
=
scale_domain
*
np
.
random
.
rand
(
400
,
dim
)
X2
[:
100
,
0
]
=
0
X2
[
100
:
200
,
0
]
=
scale_domain
X2
[
200
:
300
,
1
]
=
0
X2
[
300
:
400
,
1
]
=
scale_domain
# Define some functions
u
=
lambda
x
:
x
[:,
[
1
]]
*
x
[:,
[
0
]]
**
3
f
=
lambda
x
:
-
6
*
x
[:,
[
0
]]
*
x
[:,
[
1
]]
# Define right hand side values
y1
=
f
(
X1
)
y2
=
u
(
X2
)
# Initialize and run models
kernel
=
Gaussian_laplace
(
dim
=
2
)
maxIter
=
200
model_f
=
VKOGA_PDE
(
kernel
=
kernel
,
greedy_type
=
'
f_greedy
'
)
model_p
=
VKOGA_PDE
(
kernel
=
kernel
,
greedy_type
=
'
p_greedy
'
)
_
=
model_f
.
fit
(
X1
,
y1
,
X2
,
y2
,
maxIter
=
maxIter
)
_
=
model_p
.
fit
(
X1
,
y1
,
X2
,
y2
,
maxIter
=
maxIter
)
# Plot some results
plt
.
figure
(
1
)
plt
.
clf
()
plt
.
plot
(
np
.
array
(
model_f
.
train_hist
[
'
f
'
])
**
.
5
,
'
r
'
)
plt
.
plot
(
np
.
array
(
model_p
.
train_hist
[
'
f
'
])
**
.
5
,
'
b
'
)
plt
.
yscale
(
'
log
'
)
plt
.
xscale
(
'
log
'
)
plt
.
legend
([
'
f-greedy
'
,
'
p-greedy
'
])
plt
.
title
(
'
residuals
'
)
plt
.
draw
()
plt
.
figure
(
2
)
plt
.
clf
()
plt
.
plot
(
model_f
.
ctrs_
[
model_f
.
ind_ctrs1
,
0
],
model_f
.
ctrs_
[
model_f
.
ind_ctrs1
,
1
],
'
rx
'
)
plt
.
plot
(
model_f
.
ctrs_
[
model_f
.
ind_ctrs2
,
0
],
model_f
.
ctrs_
[
model_f
.
ind_ctrs2
,
1
],
'
ro
'
)
plt
.
title
(
'
Selected centers of f-greedy
'
)
plt
.
draw
()
plt
.
figure
(
3
)
plt
.
clf
()
plt
.
plot
(
model_p
.
ctrs_
[
model_p
.
ind_ctrs1
,
0
],
model_p
.
ctrs_
[
model_p
.
ind_ctrs1
,
1
],
'
bx
'
)
plt
.
plot
(
model_p
.
ctrs_
[
model_p
.
ind_ctrs2
,
0
],
model_p
.
ctrs_
[
model_p
.
ind_ctrs2
,
1
],
'
bo
'
)
plt
.
title
(
'
Selected centers of P-greedy
'
)
plt
.
draw
()
plt
.
figure
(
4
)
plt
.
clf
()
plt
.
plot
(
X1
[:,
0
],
X1
[:,
1
],
'
m.
'
)
plt
.
plot
(
X2
[:,
0
],
X2
[:,
1
],
'
r.
'
)
This diff is collapsed.
Click to expand it.
kernels_PDE.py
0 → 100644
+
237
−
0
View file @
2ed0814e
#!/usr/bin/env python3
from
abc
import
ABC
,
abstractmethod
from
scipy.spatial
import
distance_matrix
import
numpy
as
np
# Abstract kernel
class
Kernel
(
ABC
):
@abstractmethod
def
__init__
(
self
):
super
().
__init__
()
@abstractmethod
def
eval
(
self
,
x
,
y
):
pass
def
eval_prod
(
self
,
x
,
y
,
v
,
batch_size
=
100
):
N
=
x
.
shape
[
0
]
num_batches
=
int
(
np
.
ceil
(
N
/
batch_size
))
mat_vec_prod
=
np
.
zeros
((
N
,
1
))
for
idx
in
range
(
num_batches
):
idx_begin
=
idx
*
batch_size
idx_end
=
(
idx
+
1
)
*
batch_size
A
=
self
.
eval
(
x
[
idx_begin
:
idx_end
,
:],
y
)
mat_vec_prod
[
idx_begin
:
idx_end
]
=
A
@
v
return
mat_vec_prod
@abstractmethod
def
diagonal
(
self
,
X
):
pass
@abstractmethod
def
__str__
(
self
):
pass
@abstractmethod
def
set_params
(
self
,
params
):
pass
# Abstract RBF
class
L_RBF
(
Kernel
):
@abstractmethod
def
__init__
(
self
):
super
(
L_RBF
,
self
).
__init__
()
def
eval
(
self
,
x
,
y
):
return
self
.
rbf
(
self
.
ep
,
distance_matrix
(
np
.
atleast_2d
(
x
),
np
.
atleast_2d
(
y
)))
def
diagonal
(
self
,
X
):
return
np
.
ones
(
X
.
shape
[
0
])
*
self
.
rbf
(
self
.
ep
,
0
)
def
__str__
(
self
):
return
self
.
name
+
'
[gamma = %2.2e]
'
%
self
.
ep
def
set_params
(
self
,
par
):
self
.
ep
=
par
def
dd_diagonal
(
self
,
X
):
# Evaluation of (L^x - shift) (L^y - shift) k(x,y) for x=y: Likely some constant
return
np
.
ones
(
X
.
shape
[
0
])
*
self
.
dd_eval
(
np
.
zeros
((
1
,
1
)),
np
.
zeros
((
1
,
1
))).
item
()
def
dd_eval
(
self
,
x
,
y
):
# Evaluation of (L^x - shift) (L^y - shift) k(x,y)
array_dists
=
distance_matrix
(
np
.
atleast_2d
(
x
),
np
.
atleast_2d
(
y
))
return
self
.
rbf_utility
(
self
.
ep
,
array_dists
)
\
-
2
*
self
.
shift
*
self
.
d2_eval
(
x
,
y
)
-
self
.
shift
**
2
*
self
.
rbf
(
self
.
ep
,
array_dists
)
def
d2_eval
(
self
,
x
,
y
):
# Evaluation of (L^z - shift * Id) k(*,z)|_{x,y}
# Required exactly like this for "compute the nth basis", see also 14.12.21\A1
array_dists
=
distance_matrix
(
np
.
atleast_2d
(
x
),
np
.
atleast_2d
(
y
))
array_result
=
-
((
self
.
dim
-
1
)
*
self
.
rbf1_divided_by_r
(
self
.
ep
,
array_dists
)
+
self
.
rbf2
(
self
.
ep
,
array_dists
))
\
-
self
.
shift
*
self
.
rbf
(
self
.
ep
,
array_dists
)
return
array_result
def
d1_eval
(
self
,
x
,
y
):
# Evaluation of (L - shift * Id) k(*,y)|_{x}
# Required exactly like this for "compute the nth basis", see also 14.12.21\A1
# Since the LaPlace is radial, the formula is the same as d2_eval
return
self
.
d2_eval
(
x
,
y
)
def
mixed_eval_L
(
self
,
x
,
ctrs
,
list_ind_interior
):
# Kernel evaluation L^x k(x, y), whereby y1 := y[ind_interior, :] are centers in the interior,
# y2 := y[~ind_interior, :] are centers on the boundary.
# Therefore ddeval needs to be used with respect to y1, but deval with respect to y2.
list_not_ind_interior
=
[
idx
for
idx
in
range
(
x
.
shape
[
0
])
if
idx
not
in
list_ind_interior
]
array_result
=
np
.
zeros
((
x
.
shape
[
0
],
ctrs
.
shape
[
0
]))
array_result
[
list_ind_interior
,
:]
=
self
.
dd_eval
(
x
[
list_ind_interior
,
:],
ctrs
)
array_result
[
list_not_ind_interior
,
:]
=
self
.
d1_eval
(
x
[
list_not_ind_interior
,
:],
ctrs
)
return
array_result
def
mixed_eval
(
self
,
x
,
ctrs
,
list_ind_interior
):
# Kernel evaluation k(x, y), whereby y1 := y[ind_interior, :] are centers in the interior,
# y2 := y[~ind_interior, :] are centers on the boundary.
# Therefore deval needs to be used with respect to y1, but eval with respect to y2.
list_not_ind_interior
=
[
idx
for
idx
in
range
(
x
.
shape
[
0
])
if
idx
not
in
list_ind_interior
]
array_result
=
np
.
zeros
((
x
.
shape
[
0
],
ctrs
.
shape
[
0
]))
array_result
[
list_ind_interior
,
:]
=
self
.
d2_eval
(
x
[
list_ind_interior
,
:],
ctrs
)
array_result
[
list_not_ind_interior
,
:]
=
self
.
eval
(
x
[
list_not_ind_interior
,
:],
ctrs
)
return
array_result
# Implementation of concrete RBFs
class
Gaussian_laplace
(
L_RBF
):
# # Gaussian kernel phi(r) = exp(-r^2),
# used in conjunction with L = -LaPlace - shift * Id, whereby shift is some constant
# (e.g. an eigenvalue of -LaPlace).
def
__init__
(
self
,
dim
,
shift
=
0
,
ep
=
1
):
super
().
__init__
()
self
.
dim
=
dim
self
.
shift
=
shift
self
.
ep
=
1
# ToDo: Generalize this one!!!
self
.
name
=
'
gauss
'
# Implement not only the rbf, but also its derivatives
self
.
rbf
=
lambda
ep
,
r
:
np
.
exp
(
-
r
**
2
)
self
.
rbf1
=
lambda
ep
,
r
:
-
2
*
r
*
np
.
exp
(
-
r
**
2
)
self
.
rbf1_divided_by_r
=
lambda
ep
,
r
:
-
2
*
np
.
exp
(
-
r
**
2
)
self
.
rbf2
=
lambda
ep
,
r
:
(
4
*
r
**
2
-
2
)
*
np
.
exp
(
-
r
**
2
)
# Utility RBF for dd_eval: Double LaPlacian
self
.
rbf_utility
=
lambda
ep
,
r
:
\
np
.
exp
(
-
r
**
2
)
*
(
16
*
r
**
4
-
(
16
*
self
.
dim
+
32
)
*
r
**
2
+
(
4
*
self
.
dim
**
2
+
8
*
self
.
dim
))
class
wendland32_laplace
(
L_RBF
):
# Wendland kernel 3, 2: phi(r) = (1-r)_+^6 * (35r**2 + 18r + 3)
# used in conjunction with L = -LaPlace - shift * Id, whereby shift is some constant
# (e.g. an eigenvalue of -LaPlace).
# Only for dim <= 3!
def
__init__
(
self
,
dim
,
shift
=
0
,
ep
=
1
):
super
().
__init__
()
self
.
dim
=
dim
self
.
shift
=
shift
self
.
ep
=
1
# ep ToDo: Implement general case!
self
.
name
=
'
Wendland 3, 2
'
# Implement not only the rbf, but also its derivatives
self
.
rbf
=
lambda
ep
,
r
:
1
/
3
*
np
.
maximum
(
1
-
r
,
0
)
**
6
*
(
35
*
r
**
2
+
18
*
r
+
3
)
self
.
rbf1
=
lambda
ep
,
r
:
-
1
/
3
*
np
.
maximum
(
1
-
r
,
0
)
**
5
*
56
*
r
*
(
5
*
r
+
1
)
self
.
rbf1_divided_by_r
=
lambda
ep
,
r
:
-
1
/
3
*
np
.
maximum
(
1
-
r
,
0
)
**
5
*
56
*
(
5
*
r
+
1
)
self
.
rbf2
=
lambda
ep
,
r
:
1
/
3
*
np
.
maximum
(
1
-
r
,
0
)
**
4
*
56
*
(
35
*
r
**
2
-
4
*
r
-
1
)
# Utility RBF for dd_eval: Double LaPlacian
self
.
rbf_utility
=
lambda
ep
,
r
:
\
1
/
3
*
(
1680
*
self
.
dim
**
2
+
3360
*
self
.
dim
+
r
**
4
*
(
1680
*
self
.
dim
**
2
+
16800
*
self
.
dim
+
40320
)
+
r
**
3
*
(
-
6720
*
self
.
dim
**
2
-
53760
*
self
.
dim
-
100800
)
+
r
**
2
*
(
10080
*
self
.
dim
**
2
+
60480
*
self
.
dim
+
80640
)
+
r
*
(
-
6720
*
self
.
dim
**
2
-
26880
*
self
.
dim
-
20160
))
*
(
r
<
1
)
class
wendland33_laplace
(
L_RBF
):
# Wendland kernel 3, 3: phi(r) = (1-r)_+^8 * (32r**3+ 25r^2 + 8r + 1)
# used in conjunction with L = -LaPlace - shift * Id, whereby shift is some constant
# (e.g. an eigenvalue of -LaPlace).
# Only for dim <= 3!
def
__init__
(
self
,
dim
,
shift
=
0
,
ep
=
1
):
super
().
__init__
()
self
.
dim
=
dim
self
.
shift
=
shift
self
.
ep
=
1
# ep ToDo: Implement general case!
self
.
name
=
'
wendland 3, 3
'
# Implement not only the rbf, but also its derivatives
self
.
rbf
=
lambda
ep
,
r
:
np
.
maximum
(
1
-
r
,
0
)
**
8
*
(
32
*
r
**
3
+
25
*
r
**
2
+
8
*
r
+
1
)
self
.
rbf1
=
lambda
ep
,
r
:
-
np
.
maximum
(
1
-
r
,
0
)
**
7
*
22
*
r
*
(
16
*
r
**
2
+
7
*
r
+
1
)
self
.
rbf1_divided_by_r
=
lambda
ep
,
r
:
-
np
.
maximum
(
1
-
r
,
0
)
**
7
*
22
*
(
16
*
r
**
2
+
7
*
r
+
1
)
self
.
rbf2
=
lambda
ep
,
r
:
np
.
maximum
(
1
-
r
,
0
)
**
6
*
22
*
(
160
*
r
**
3
+
15
*
r
**
2
-
6
*
r
-
1
)
# Utility RBF for dd_eval: Double LaPlacian
self
.
rbf_utility
=
lambda
ep
,
r
:
\
(
528
*
self
.
dim
**
2
+
1056
*
self
.
dim
+
r
**
7
*
(
3168
*
self
.
dim
**
2
+
50688
*
self
.
dim
+
199584
)
+
r
**
6
*
(
-
18480
*
self
.
dim
**
2
-
258720
*
self
.
dim
-
887040
)
+
r
**
5
*
(
44352
*
self
.
dim
**
2
+
532224
*
self
.
dim
+
1552320
)
+
r
**
4
*
(
-
55440
*
self
.
dim
**
2
-
554400
*
self
.
dim
-
1330560
)
+
r
**
3
*
(
36960
*
self
.
dim
**
2
+
295680
*
self
.
dim
+
554400
)
+
r
**
2
*
(
-
11088
*
self
.
dim
**
2
-
66528
*
self
.
dim
-
88704
))
*
(
r
<
1
)
class
quadrMatern_laplace
(
L_RBF
):
# Quadratic matern kernel phi(r) = (3 + 3r + r^2)*exp(-r),
# used in conjunction with L = -LaPlace
def
__init__
(
self
,
dim
,
shift
=
0
,
ep
=
1
):
super
().
__init__
()
self
.
dim
=
dim
self
.
shift
=
shift
self
.
ep
=
1
# ep ToDo: Implement general case!
self
.
name
=
'
quadratic Matern
'
# Implement not only the rbf, but also its derivatives
self
.
rbf
=
lambda
ep
,
r
:
np
.
exp
(
-
r
)
*
(
3
+
3
*
r
+
r
**
2
)
self
.
rbf1
=
lambda
ep
,
r
:
np
.
exp
(
-
r
)
*
(
-
r
)
*
(
r
+
1
)
self
.
rbf1_divided_by_r
=
lambda
ep
,
r
:
np
.
exp
(
-
r
)
*
(
-
1
)
*
(
r
+
1
)
self
.
rbf2
=
lambda
ep
,
r
:
np
.
exp
(
-
r
)
*
(
r
**
2
-
r
-
1
)
# Utility RBF for dd_eval: Double LaPlacian
self
.
rbf_utility
=
lambda
ep
,
r
:
\
np
.
exp
(
-
r
)
*
(
r
**
2
-
(
2
*
self
.
dim
+
3
)
*
r
+
self
.
dim
**
2
+
2
*
self
.
dim
)
This diff is collapsed.
Click to expand it.
vkoga_PDE.py
0 → 100644
+
594
−
0
View file @
2ed0814e
# First approach to develope code for greedy PDE.
# ToDo: Not sure whether vector valued output makes sense, but let's try to implement it ..
from
vkoga_pde.kernels_PDE
import
wendland32_laplace
import
numpy
as
np
from
sklearn.base
import
BaseEstimator
from
sklearn.utils.validation
import
check_X_y
,
check_array
# VKOGA implementation
class
VKOGA_PDE
(
BaseEstimator
):
def
__init__
(
self
,
kernel
=
wendland32_laplace
(
dim
=
2
),
kernel_par
=
1
,
verbose
=
True
,
n_report
=
10
,
greedy_type
=
'
p_greedy
'
,
reg_par
=
0
,
restr_par
=
0
,
tol_f
=
1e-10
,
tol_p
=
1e-10
,
beta
=
None
):
# Set the verbosity on/off
self
.
verbose
=
verbose
# Set the frequency of report
self
.
n_report
=
n_report
# Set the params defining the method
self
.
kernel
=
kernel
self
.
kernel_par
=
kernel_par
self
.
reg_par
=
reg_par
self
.
restr_par
=
restr_par
# Define the greedy method properly
if
beta
is
None
:
self
.
greedy_type
=
greedy_type
if
self
.
greedy_type
==
'
f_greedy
'
:
self
.
beta
=
1.0
elif
self
.
greedy_type
==
'
p_greedy
'
:
self
.
beta
=
0.0
else
:
self
.
beta
=
float
(
beta
)
if
self
.
beta
==
1.0
:
self
.
greedy_type
=
'
f_greedy
'
elif
self
.
beta
==
0.0
:
self
.
greedy_type
=
'
p_greedy
'
else
:
self
.
greedy_type
=
'
beta
'
# ToDo: Did never think about the regularization parameter ..
assert
reg_par
==
0
,
'
reg_par > 0 might not be implemented correctly!
'
self
.
flag_val
=
None
# Set the stopping values
self
.
tol_f
=
tol_f
self
.
tol_p
=
tol_p
# Some further settings
self
.
ctrs_
=
None
# centers for the interior
self
.
ind_ctrs1
=
[]
# self.ctrs1_ = self.ctrs_[self.indI_ctrs1], i.e. interior
self
.
ind_ctrs2
=
[]
# self.ctrs2_ = self.ctrs_[self.indI_ctrs2], i.e. boundary
self
.
Cut_
=
None
self
.
c
=
None
# Initialize the convergence history
self
.
train_hist
=
{}
self
.
train_hist
[
'
n
'
]
=
[]
self
.
train_hist
[
'
f
'
]
=
[]
self
.
train_hist
[
'
f1
'
]
=
[]
self
.
train_hist
[
'
f2
'
]
=
[]
self
.
train_hist
[
'
p
'
]
=
[]
self
.
train_hist
[
'
p1
'
]
=
[]
self
.
train_hist
[
'
p2
'
]
=
[]
self
.
train_hist
[
'
p selected
'
]
=
[]
# list of selected power vals
self
.
train_hist
[
'
f val
'
]
=
[]
self
.
train_hist
[
'
p val
'
]
=
[]
# Initialize variables for the maximal kernel diagonal value in the interior and the boundary
# ToDo: So far only f-greedy and P-greedy is implemented in a meaningful way,
# for all other \beta values the weighting of the power function (via the kernel
# diagonal values) is not implemented.
self
.
max_kernel_diag1
=
None
self
.
max_kernel_diag2
=
None
def
selection_rule
(
self
,
f1
,
f2
,
p1
,
p2
):
# Generalization of selection rule: We have residual values and power function values
# for both the interior (f1, p1) as well as for the boundary (p1, p2).
# ToDo: So far only f-greedy and P-greedy is implemented in a meaningful way,
# for all other \beta values the weighting of the power function (via the kernel
# diagonal values) is not implemented.
if
self
.
restr_par
>
0
:
# ToDo: Not sure whether this will really work, restr_idx might be empty
p_
=
max
([
np
.
max
(
p1
),
np
.
max
(
p2
)])
restr_idx1
=
np
.
nonzero
(
p1
>=
self
.
restr_par
*
p_
)[
0
]
restr_idx2
=
np
.
nonzero
(
p2
>=
self
.
restr_par
*
p_
)[
0
]
else
:
restr_idx1
=
np
.
arange
(
len
(
p1
))
restr_idx2
=
np
.
arange
(
len
(
p2
))
f1
=
np
.
sum
(
f1
**
2
,
axis
=
1
)
f2
=
np
.
sum
(
f2
**
2
,
axis
=
1
)
# Case distinction required for fp_greedy
if
self
.
greedy_type
==
'
fp_greedy
'
:
array_weighted1
=
f1
[
restr_idx1
]
/
p1
[
restr_idx1
]
idx1
=
np
.
argmax
(
array_weighted1
)
if
len
(
f2
[
restr_idx2
])
!=
0
:
# required especially in 1D
array_weighted2
=
f2
[
restr_idx2
]
/
p2
[
restr_idx2
]
idx2
=
np
.
argmax
(
array_weighted2
)
if
array_weighted1
[
idx1
]
>
array_weighted2
[
idx2
]:
one_or_two
=
1
idx
=
restr_idx1
[
idx1
]
else
:
one_or_two
=
2
idx
=
restr_idx2
[
idx2
]
else
:
one_or_two
=
1
idx
=
restr_idx1
[
idx1
]
elif
self
.
greedy_type
==
'
p_greedy
'
:
# extra implementation for P-greedy to address weighting of powerfunc (interior, boundary)
array_weighted1
=
p1
[
restr_idx1
]
idx1
=
np
.
argmax
(
array_weighted1
)
if
len
(
f2
[
restr_idx2
])
!=
0
:
# required especially in 1D
array_weighted2
=
p2
[
restr_idx2
]
idx2
=
np
.
argmax
(
array_weighted2
)
# Pay attention at the weighting of the power functions
if
array_weighted1
[
idx1
]
/
self
.
max_kernel_diag1
\
>
array_weighted2
[
idx2
]
/
self
.
max_kernel_diag2
:
one_or_two
=
1
idx
=
restr_idx1
[
idx1
]
else
:
one_or_two
=
2
idx
=
restr_idx2
[
idx2
]
else
:
one_or_two
=
1
idx
=
restr_idx1
[
idx1
]
else
:
array_weighted1
=
f1
[
restr_idx1
]
**
self
.
beta
*
p1
[
restr_idx1
]
**
(
1
-
self
.
beta
)
idx1
=
np
.
argmax
(
array_weighted1
)
if
len
(
f2
[
restr_idx2
])
!=
0
:
# required especially in 1D
array_weighted2
=
f2
[
restr_idx2
]
**
self
.
beta
*
p2
[
restr_idx2
]
**
(
1
-
self
.
beta
)
idx2
=
np
.
argmax
(
array_weighted2
)
if
array_weighted1
[
idx1
]
>
array_weighted2
[
idx2
]:
one_or_two
=
1
idx
=
restr_idx1
[
idx1
]
else
:
one_or_two
=
2
idx
=
restr_idx2
[
idx2
]
else
:
one_or_two
=
1
idx
=
restr_idx1
[
idx1
]
# ToDo: This implementation is quite laborious ..
if
self
.
greedy_type
==
'
p_greedy
'
:
f_max1
,
p_max1
=
np
.
max
(
f1
),
np
.
max
(
p1
)
/
self
.
max_kernel_diag1
else
:
f_max1
,
p_max1
=
np
.
max
(
f1
),
np
.
max
(
p1
)
if
len
(
f2
[
restr_idx2
])
!=
0
:
if
self
.
greedy_type
==
'
p_greedy
'
:
f_max2
,
p_max2
=
np
.
max
(
f2
),
np
.
max
(
p2
)
/
self
.
max_kernel_diag2
else
:
f_max2
,
p_max2
=
np
.
max
(
f2
),
np
.
max
(
p2
)
else
:
f_max2
,
p_max2
=
0
,
0
return
(
one_or_two
,
idx
),
f_max1
,
f_max2
,
p_max1
,
p_max2
def
fit
(
self
,
X1
,
y1
,
X2
,
y2
,
X1_val
=
None
,
y1_val
=
None
,
X2_val
=
None
,
y2_val
=
None
,
maxIter
=
None
):
# Points in X1 are considered as interior points, points in X2 as boundary points.
# They can coincide (partly), but for X1 we use the L-evaluation, for X2 we use the point evaluation.
# Compute the maximal kernel diagonal value in the interior and the boundary
self
.
max_kernel_diag2
=
np
.
max
(
self
.
kernel
.
diagonal
(
X2
))
self
.
max_kernel_diag1
=
np
.
max
(
self
.
kernel
.
dd_diagonal
(
X1
))
list_ctrs
=
[]
# not that beautiful, but required to store the final centers
# Check the datasets
X1
,
y1
=
check_X_y
(
X1
,
y1
,
multi_output
=
True
)
X2
,
y2
=
check_X_y
(
X2
,
y2
,
multi_output
=
True
)
# ToDo: Check for consistency of X1, X2, X1_val, X2_val
if
len
(
y1
.
shape
)
==
1
:
y1
=
y1
[:,
None
]
if
len
(
y2
.
shape
)
==
1
:
y2
=
y2
[:,
None
]
# Check the validation datasets (if provided, otherwise set to None)
if
X1_val
is
None
or
y1_val
is
None
or
X2_val
is
None
or
y2_val
is
None
:
X1_val
=
None
y1_val
=
None
X2_val
=
None
y2_val
=
None
self
.
flag_val
=
False
else
:
self
.
flag_val
=
True
X1_val
,
y1_val
=
check_X_y
(
X1_val
,
y2_val
,
multi_output
=
True
)
X2_val
,
y2_val
=
check_X_y
(
X2_val
,
y2_val
,
multi_output
=
True
)
# We will assume in the following that X_val and X are disjoint
if
len
(
y1_val
.
shape
)
==
1
:
y1_val
=
y1_val
[:,
None
]
if
len
(
y2_val
.
shape
)
==
1
:
y2_val
=
y2_val
[:,
None
]
# Check whether already fitted
if
self
.
ctrs_
is
None
:
self
.
ctrs_
=
np
.
zeros
((
0
,
X1
.
shape
[
1
]))
self
.
Cut_
=
np
.
zeros
((
0
,
0
))
self
.
c
=
np
.
zeros
((
0
,
y1
.
shape
[
1
]))
# ToDo: If self.ctrs_ is not None, does this work?
# Check whether "new X1" and previously chosen centers overlap
list_truly_new_X1
=
[]
if
self
.
ctrs_
.
shape
[
0
]
>
0
:
for
idx_x
in
range
(
X1
.
shape
[
0
]):
if
min
(
np
.
linalg
.
norm
(
self
.
ctrs_
-
X1
[
idx_x
,
:],
axis
=
1
))
<
1e-10
:
continue
else
:
list_truly_new_X1
.
append
(
idx_x
)
else
:
list_truly_new_X1
=
list
(
range
(
X1
.
shape
[
0
]))
X1
=
X1
[
list_truly_new_X1
,
:]
y1
=
y1
[
list_truly_new_X1
,
:]
# Check whether "new X2" and previously chosen centers overlap
list_truly_new_X2
=
[]
if
self
.
ctrs_
.
shape
[
0
]
>
0
:
for
idx_x
in
range
(
X2
.
shape
[
0
]):
if
min
(
np
.
linalg
.
norm
(
self
.
ctrs_
-
X2
[
idx_x
,
:],
axis
=
1
))
<
1e-10
:
continue
else
:
list_truly_new_X2
.
append
(
idx_x
)
else
:
list_truly_new_X2
=
list
(
range
(
X2
.
shape
[
0
]))
X2
=
X2
[
list_truly_new_X2
,
:]
y2
=
y2
[
list_truly_new_X2
,
:]
# Initialize the residual and update the given y values by substracting the current model
y1
=
y1
-
self
.
predict_Ls
(
X1
)
y2
=
y2
-
self
.
predict_s
(
X2
)
if
self
.
flag_val
:
y1_val
=
y1_val
-
self
.
predict_Ls
(
X1_val
)
y2_val
=
y2_val
-
self
.
predict_s
(
X2_val
)
# Get the data dimension
N
,
q
=
y1
.
shape
[
0
]
+
y2
.
shape
[
0
],
y1
.
shape
[
1
]
N1
,
N2
=
y1
.
shape
[
0
],
y2
.
shape
[
0
]
if
self
.
flag_val
:
N_val
=
y1_val
.
shape
[
0
]
+
y2_val
.
shape
[
0
]
N1_val
,
N2_val
=
y1_val
.
shape
[
0
],
y2_val
.
shape
[
0
]
# Set maxIter_continue
if
maxIter
is
None
or
maxIter
>
N
:
self
.
maxIter
=
min
([
N
,
100
])
else
:
self
.
maxIter
=
maxIter
# Check compatibility of restriction
if
self
.
beta
==
0.0
:
# in case of P-greedy
self
.
restr_par
=
0
if
not
self
.
reg_par
==
0
:
self
.
restr_par
=
0
# Initialize list for the chosen and non-chosen indices - seperately for interior and boundary
indI1_
,
indI2_
=
[],
[]
notIndI1
,
notIndI2
=
list
(
range
(
y1
.
shape
[
0
])),
list
(
range
(
y2
.
shape
[
0
]))
c
=
np
.
zeros
((
self
.
maxIter
,
q
))
# Compute the Newton basis values (related to the old centers) on the new X
# ToDo: I think restart training etc does not work properly, since it is not debugged propely!!
# Thus the following lines of codes may be irrelevant
Vx_new_X1_old_ctrs
=
self
.
kernel
.
mixed_eval
(
X1
,
self
.
ctrs_
,
self
.
ind_ctrs1
)
@
self
.
Cut_
.
transpose
()
Vx_new_X2_old_ctrs
=
self
.
kernel
.
mixed_eval
(
X2
,
self
.
ctrs_
,
self
.
ind_ctrs1
)
@
self
.
Cut_
.
transpose
()
if
self
.
flag_val
:
Vx_val_new_X1_old_ctrs
=
self
.
kernel
.
mixed_eval
(
X1_val
,
self
.
ctrs_
,
self
.
ind_ctrs1
)
@
self
.
Cut_
.
transpose
()
Vx_val_new_X2_old_ctrs
=
self
.
kernel
.
mixed_eval
(
X2_val
,
self
.
ctrs_
,
self
.
ind_ctrs1
)
@
self
.
Cut_
.
transpose
()
# Compute the application of L to the Newton basis values (related to the old centers) on the new X
LVx_new_X1_old_ctrs
=
self
.
kernel
.
mixed_eval_L
(
X1
,
self
.
ctrs_
,
self
.
ind_ctrs1
)
@
self
.
Cut_
.
transpose
()
LVx_new_X2_old_ctrs
=
self
.
kernel
.
mixed_eval_L
(
X2
,
self
.
ctrs_
,
self
.
ind_ctrs1
)
@
self
.
Cut_
.
transpose
()
if
self
.
flag_val
:
LVx_val_new_X1_old_ctrs
=
self
.
kernel
.
mixed_eval_L
(
X1_val
,
self
.
ctrs_
,
self
.
ind_ctrs1
)
@
self
.
Cut_
.
transpose
()
LVx_val_new_X2_old_ctrs
=
self
.
kernel
.
mixed_eval_L
(
X2_val
,
self
.
ctrs_
,
self
.
ind_ctrs1
)
@
self
.
Cut_
.
transpose
()
# Initialize arrays for the (L-evaluation of the) Newton basis values ..
# .. (related to the new centers) on the new X1, X2
Vx1
=
np
.
zeros
((
N1
,
self
.
maxIter
))
Vx2
=
np
.
zeros
((
N2
,
self
.
maxIter
))
LVx1
=
np
.
zeros
((
N1
,
self
.
maxIter
))
LVx2
=
np
.
zeros
((
N2
,
self
.
maxIter
))
if
self
.
flag_val
:
Vx1_val
=
np
.
zeros
((
N1_val
,
self
.
maxIter
))
Vx2_val
=
np
.
zeros
((
N2_val
,
self
.
maxIter
))
LVx1_val
=
np
.
zeros
((
N1_val
,
self
.
maxIter
))
LVx2_val
=
np
.
zeros
((
N2_val
,
self
.
maxIter
))
# Compute the powervals on X1, X2 and X1_val, X2_val
p1
=
self
.
kernel
.
dd_diagonal
(
X1
)
+
self
.
reg_par
p1
=
p1
-
np
.
sum
(
LVx_new_X1_old_ctrs
**
2
,
axis
=
1
)
p2
=
self
.
kernel
.
diagonal
(
X2
)
+
self
.
reg_par
p2
=
p2
-
np
.
sum
(
Vx_new_X2_old_ctrs
**
2
,
axis
=
1
)
if
self
.
flag_val
:
p1_val
=
self
.
kernel
.
dd_diagonal
(
X1_val
)
+
self
.
reg_par
p1_val
=
p1_val
-
np
.
sum
(
LVx_val_new_X1_old_ctrs
**
2
,
axis
=
1
)
p2_val
=
self
.
kernel
.
diagonal
(
X2_val
)
+
self
.
reg_par
p2_val
=
p2_val
-
np
.
sum
(
Vx_val_new_X2_old_ctrs
**
2
,
axis
=
1
)
# Extend Cut_ matrix, i.e. continue to build on old self.Cut_ matrix
N_ctrs_so_far
,
N_ctrs_so_far1
,
N_ctrs_so_far2
=
\
self
.
Cut_
.
shape
[
0
],
len
(
self
.
ind_ctrs1
),
len
(
self
.
ind_ctrs2
)
Cut_
=
np
.
zeros
((
N_ctrs_so_far
+
self
.
maxIter
,
N_ctrs_so_far
+
self
.
maxIter
))
Cut_
[:
N_ctrs_so_far
,
:
N_ctrs_so_far
]
=
self
.
Cut_
# Iterative selection of new points
self
.
print_message
(
'
begin
'
)
n1
,
n2
=
0
,
0
# number of centers in interior or boundary
for
n
in
range
(
self
.
maxIter
):
# prepare
self
.
train_hist
[
'
n
'
].
append
(
self
.
ctrs_
.
shape
[
0
]
+
n
+
1
)
self
.
train_hist
[
'
f
'
].
append
([])
self
.
train_hist
[
'
f1
'
].
append
([])
self
.
train_hist
[
'
f2
'
].
append
([])
self
.
train_hist
[
'
p
'
].
append
([])
self
.
train_hist
[
'
p1
'
].
append
([])
self
.
train_hist
[
'
p2
'
].
append
([])
self
.
train_hist
[
'
p selected
'
].
append
([])
if
self
.
flag_val
:
self
.
train_hist
[
'
p val
'
].
append
([])
self
.
train_hist
[
'
f val
'
].
append
([])
# select the current index
(
one_or_two
,
idx
),
self
.
train_hist
[
'
f1
'
][
-
1
],
self
.
train_hist
[
'
f2
'
][
-
1
],
\
self
.
train_hist
[
'
p1
'
][
-
1
],
self
.
train_hist
[
'
p2
'
][
-
1
]
\
=
self
.
selection_rule
(
y1
[
notIndI1
],
y2
[
notIndI2
],
p1
[
notIndI1
],
p2
[
notIndI2
])
self
.
train_hist
[
'
f
'
][
-
1
]
=
max
(
self
.
train_hist
[
'
f1
'
][
-
1
],
self
.
train_hist
[
'
f2
'
][
-
1
])
self
.
train_hist
[
'
p
'
][
-
1
]
=
max
(
self
.
train_hist
[
'
p1
'
][
-
1
],
self
.
train_hist
[
'
p2
'
][
-
1
])
# add the current index and store selected p value and store the selected center
if
one_or_two
==
1
:
self
.
train_hist
[
'
p selected
'
][
-
1
]
=
p1
[
notIndI1
[
idx
]]
indI1_
.
append
(
notIndI1
[
idx
])
self
.
ind_ctrs1
.
append
(
N_ctrs_so_far
+
n
)
list_ctrs
.
append
(
X1
[[
notIndI1
[
idx
]],
:])
elif
one_or_two
==
2
:
self
.
train_hist
[
'
p selected
'
][
-
1
]
=
p2
[
notIndI2
[
idx
]]
indI2_
.
append
(
notIndI2
[
idx
])
self
.
ind_ctrs2
.
append
(
N_ctrs_so_far
+
n
)
list_ctrs
.
append
(
X2
[[
notIndI2
[
idx
]],
:])
if
self
.
flag_val
:
self
.
train_hist
[
'
p val
'
][
-
1
]
=
max
([
np
.
max
(
p1_val
),
np
.
max
(
p2_val
)])
self
.
train_hist
[
'
f val
'
][
-
1
]
=
max
(
[
np
.
max
(
np
.
sum
(
y1_val
**
2
,
axis
=
1
)),
np
.
max
(
np
.
sum
(
y2_val
**
2
,
axis
=
1
))])
# check if the tolerances are reacheded
if
self
.
train_hist
[
'
f
'
][
n
]
<=
self
.
tol_f
or
self
.
train_hist
[
'
p
'
][
n
]
<=
self
.
tol_p
:
n
=
n
-
1
if
one_or_two
==
1
:
n1
=
n1
-
1
elif
one_or_two
==
2
:
n2
=
n2
-
1
self
.
print_message
(
'
end
'
)
break
# compute the nth basis (including normalization), see also 14.12.21\A1
if
one_or_two
==
1
:
# Computations (this is simply expansion in Newton basis) ..
Vx1
[
notIndI1
,
n
]
=
self
.
kernel
.
d2_eval
(
X1
[
notIndI1
,
:],
X1
[
indI1_
[
n1
],
:])[:,
0
]
\
-
Vx_new_X1_old_ctrs
[
notIndI1
,
:]
@
LVx_new_X1_old_ctrs
[
indI1_
[
n1
],
:].
transpose
()
\
-
Vx1
[
notIndI1
,
:]
@
LVx1
[
indI1_
[
n1
],
:].
transpose
()
Vx2
[
notIndI2
,
n
]
=
self
.
kernel
.
d2_eval
(
X2
[
notIndI2
,
:],
X1
[
indI1_
[
n1
],
:])[:,
0
]
\
-
Vx_new_X2_old_ctrs
[
notIndI2
,
:]
@
LVx_new_X1_old_ctrs
[
indI1_
[
n1
],
:].
transpose
()
\
-
Vx2
[
notIndI2
,
:]
@
LVx1
[
indI1_
[
n1
],
:].
transpose
()
LVx1
[
notIndI1
,
n
]
=
self
.
kernel
.
dd_eval
(
X1
[
notIndI1
,
:],
X1
[
indI1_
[
n1
],
:])[:,
0
]
\
-
LVx_new_X1_old_ctrs
[
notIndI1
,
:]
@
LVx_new_X1_old_ctrs
[
indI1_
[
n1
],
:].
transpose
()
\
-
LVx1
[
notIndI1
,
:]
@
LVx1
[
indI1_
[
n1
],
:].
transpose
()
LVx2
[
notIndI2
,
n
]
=
self
.
kernel
.
dd_eval
(
X2
[
notIndI2
,
:],
X1
[
indI1_
[
n1
],
:])[:,
0
]
\
-
LVx_new_X2_old_ctrs
[
notIndI2
,
:]
@
LVx_new_X1_old_ctrs
[
indI1_
[
n1
],
:].
transpose
()
\
-
LVx2
[
notIndI2
,
:]
@
LVx1
[
indI1_
[
n1
],
:].
transpose
()
Vx1
[
indI1_
[
n1
],
n
]
+=
self
.
reg_par
# ToDo: This is likely wrong/incomplete/..
# Normalizations ..
Vx1
[
notIndI1
,
n
]
=
Vx1
[
notIndI1
,
n
]
/
np
.
sqrt
(
p1
[
indI1_
[
n1
]])
Vx2
[
notIndI2
,
n
]
=
Vx2
[
notIndI2
,
n
]
/
np
.
sqrt
(
p1
[
indI1_
[
n1
]])
LVx1
[
notIndI1
,
n
]
=
LVx1
[
notIndI1
,
n
]
/
np
.
sqrt
(
p1
[
indI1_
[
n1
]])
LVx2
[
notIndI2
,
n
]
=
LVx2
[
notIndI2
,
n
]
/
np
.
sqrt
(
p1
[
indI1_
[
n1
]])
# ToDo: Take care of the validation sets ..
# if self.flag_val:
# Vx_val[:, n] = self.kernel.eval(X_val, X[indI_[n], :])[:, 0]\
# - Vx_val_new_X_old_ctrs[:, :] @ Vx_new_X_old_ctrs[indI_[n], :].transpose()\
# - Vx_val[:, :n+1] @ Vx[indI_[n], 0:n+1].transpose()
# Vx_val[:, n] = Vx_val[:, n] / np.sqrt(p[indI_[n]])
elif
one_or_two
==
2
:
# Computations (this is simply expansion in Newton basis) ..
Vx1
[
notIndI1
,
n
]
=
self
.
kernel
.
eval
(
X1
[
notIndI1
,
:],
X2
[
indI2_
[
n2
],
:])[:,
0
]
\
-
Vx_new_X1_old_ctrs
[
notIndI1
,
:]
@
Vx_new_X2_old_ctrs
[
indI2_
[
n2
],
:].
transpose
()
\
-
Vx1
[
notIndI1
,
:]
@
Vx2
[
indI2_
[
n2
],
:].
transpose
()
Vx2
[
notIndI2
,
n
]
=
self
.
kernel
.
eval
(
X2
[
notIndI2
,
:],
X2
[
indI2_
[
n2
],
:])[:,
0
]
\
-
Vx_new_X2_old_ctrs
[
notIndI2
,
:]
@
Vx_new_X2_old_ctrs
[
indI2_
[
n2
],
:].
transpose
()
\
-
Vx2
[
notIndI2
,
:]
@
Vx2
[
indI2_
[
n2
],
:].
transpose
()
LVx1
[
notIndI1
,
n
]
=
self
.
kernel
.
d1_eval
(
X1
[
notIndI1
,
:],
X2
[
indI2_
[
n2
],
:])[:,
0
]
\
-
LVx_new_X1_old_ctrs
[
notIndI1
,
:]
@
Vx_new_X2_old_ctrs
[
indI2_
[
n2
],
:].
transpose
()
\
-
LVx1
[
notIndI1
,
:]
@
Vx2
[
indI2_
[
n2
],
:].
transpose
()
LVx2
[
notIndI2
,
n
]
=
self
.
kernel
.
d1_eval
(
X2
[
notIndI2
,
:],
X2
[
indI2_
[
n2
],
:])[:,
0
]
\
-
LVx_new_X2_old_ctrs
[
notIndI2
,
:]
@
Vx_new_X2_old_ctrs
[
indI2_
[
n2
],
:].
transpose
()
\
-
LVx2
[
notIndI2
,
:]
@
Vx2
[
indI2_
[
n2
],
:].
transpose
()
Vx2
[
indI2_
[
n2
],
n
]
+=
self
.
reg_par
# ToDo: This is likely wrong/incomplete/..
# Normalizations ..
Vx1
[
notIndI1
,
n
]
=
Vx1
[
notIndI1
,
n
]
/
np
.
sqrt
(
p2
[
indI2_
[
n2
]])
Vx2
[
notIndI2
,
n
]
=
Vx2
[
notIndI2
,
n
]
/
np
.
sqrt
(
p2
[
indI2_
[
n2
]])
LVx1
[
notIndI1
,
n
]
=
LVx1
[
notIndI1
,
n
]
/
np
.
sqrt
(
p2
[
indI2_
[
n2
]])
LVx2
[
notIndI2
,
n
]
=
LVx2
[
notIndI2
,
n
]
/
np
.
sqrt
(
p2
[
indI2_
[
n2
]])
# ToDo: Take care of the validation sets
# update the change of basis (see 14.12.21\B)
Cut_new_row
=
np
.
ones
(
N_ctrs_so_far
+
n
+
1
)
# required only for the final one
if
one_or_two
==
1
:
Cut_new_row
[:
N_ctrs_so_far
+
n
]
=
\
-
np
.
concatenate
((
LVx_new_X1_old_ctrs
[
indI1_
[
n1
],
:],
LVx1
[
indI1_
[
n1
],
:
n
]))
\
@
Cut_
[:
N_ctrs_so_far
+
n
,
:
N_ctrs_so_far
+
n
]
Cut_
[
N_ctrs_so_far
+
n
,
:
N_ctrs_so_far
+
n
+
1
]
=
Cut_new_row
/
LVx1
[
indI1_
[
n1
],
n
]
elif
one_or_two
==
2
:
Cut_new_row
[:
N_ctrs_so_far
+
n
]
=
\
-
np
.
concatenate
((
Vx_new_X2_old_ctrs
[
indI2_
[
n2
],
:],
Vx2
[
indI2_
[
n2
],
:
n
]))
\
@
Cut_
[:
N_ctrs_so_far
+
n
,
:
N_ctrs_so_far
+
n
]
Cut_
[
N_ctrs_so_far
+
n
,
:
N_ctrs_so_far
+
n
+
1
]
=
Cut_new_row
/
Vx2
[
indI2_
[
n2
],
n
]
# compute the nth coefficient
if
one_or_two
==
1
:
c
[
n
]
=
y1
[
indI1_
[
n1
]]
/
np
.
sqrt
(
p1
[
indI1_
[
n1
]])
elif
one_or_two
==
2
:
c
[
n
]
=
y2
[
indI2_
[
n2
]]
/
np
.
sqrt
(
p2
[
indI2_
[
n2
]])
# update the power functions: ToDo: For Gaussian in 1D we obtain negative power vals :(
p1
[
notIndI1
]
=
p1
[
notIndI1
]
-
LVx1
[
notIndI1
,
n
]
**
2
p2
[
notIndI2
]
=
p2
[
notIndI2
]
-
Vx2
[
notIndI2
,
n
]
**
2
if
self
.
flag_val
:
p1_val
=
p1_val
-
LVx1_val
[:,
n
]
**
2
p2_val
=
p2_val
-
Vx2_val
[:,
n
]
**
2
# update the residual
y1
[
notIndI1
]
=
y1
[
notIndI1
]
-
LVx1
[
notIndI1
,
n
][:,
None
]
*
c
[
n
]
y2
[
notIndI2
]
=
y2
[
notIndI2
]
-
Vx2
[
notIndI2
,
n
][:,
None
]
*
c
[
n
]
if
self
.
flag_val
:
y1_val
=
y1_val
-
LVx1_val
[:,
n
][:,
None
]
*
c
[
n
]
y2_val
=
y2_val
-
Vx2_val
[:,
n
][:,
None
]
*
c
[
n
]
# remove the nth index from the dictionary
if
one_or_two
==
1
:
notIndI1
.
pop
(
idx
)
n1
+=
1
elif
one_or_two
==
2
:
notIndI2
.
pop
(
idx
)
n2
+=
1
# Report some data every now and then
if
n
%
self
.
n_report
==
0
:
self
.
print_message
(
'
track
'
)
else
:
self
.
print_message
(
'
end
'
)
# Define coefficients and centers
# ToDo: Check whether everything is clipped here correctly!!
self
.
c
=
np
.
concatenate
((
self
.
c
,
c
[:
n
+
1
]))
self
.
Cut_
=
Cut_
[:
N_ctrs_so_far
+
n
+
1
,
:
N_ctrs_so_far
+
n
+
1
]
self
.
ind_ctrs1
=
self
.
ind_ctrs1
[:
N_ctrs_so_far1
+
n1
+
1
]
# ToDo: Check whether this is correct!!!!
self
.
ind_ctrs2
=
self
.
ind_ctrs2
[:
N_ctrs_so_far2
+
n2
+
1
]
self
.
coef_
=
self
.
Cut_
.
transpose
()
@
self
.
c
self
.
ctrs_
=
np
.
concatenate
((
self
.
ctrs_
,
np
.
concatenate
(
list_ctrs
[:
n
+
1
],
axis
=
0
)),
axis
=
0
)
return
self
def
predict_s
(
self
,
X
):
# Prediction of s, which might/should approximate the true solution u
X
=
check_array
(
X
)
# Compute prediction
if
self
.
ctrs_
.
shape
[
0
]
>
0
:
prediction
=
self
.
kernel
.
d2_eval
(
X
,
self
.
ctrs_
[
self
.
ind_ctrs1
])
@
self
.
coef_
[
self
.
ind_ctrs1
]
\
+
self
.
kernel
.
eval
(
X
,
self
.
ctrs_
[
self
.
ind_ctrs2
])
@
self
.
coef_
[
self
.
ind_ctrs2
]
else
:
prediction
=
np
.
zeros
((
X
.
shape
[
0
],
1
))
return
prediction
def
predict_Ls
(
self
,
X
):
# Prediction of Ls, i.e. application of L to the prediction
# Validate the input
X
=
check_array
(
X
)
# Compute prediction
if
self
.
ctrs_
.
shape
[
0
]
>
0
:
prediction
=
self
.
kernel
.
dd_eval
(
X
,
self
.
ctrs_
[
self
.
ind_ctrs1
])
@
self
.
coef_
[
self
.
ind_ctrs1
]
\
+
self
.
kernel
.
d1_eval
(
X
,
self
.
ctrs_
[
self
.
ind_ctrs2
])
@
self
.
coef_
[
self
.
ind_ctrs2
]
else
:
prediction
=
np
.
zeros
((
X
.
shape
[
0
],
1
))
return
prediction
def
print_message
(
self
,
when
):
if
self
.
verbose
and
when
==
'
begin
'
:
print
(
''
)
print
(
'
*
'
*
30
+
'
[VKOGA]
'
+
'
*
'
*
30
)
print
(
'
Training model with
'
)
print
(
'
|_ kernel : %s
'
%
self
.
kernel
)
print
(
'
|_ regularization par. : %2.2e
'
%
self
.
reg_par
)
print
(
'
|_ restriction par. : %2.2e
'
%
self
.
restr_par
)
print
(
''
)
if
self
.
verbose
and
when
==
'
end
'
:
print
(
'
Training completed with
'
)
print
(
'
|_ selected points : %8d / %8d
'
%
(
self
.
train_hist
[
'
n
'
][
-
1
],
self
.
ctrs_
.
shape
[
0
]
+
self
.
maxIter
))
if
self
.
flag_val
:
print
(
'
|_ train, val residual : %2.2e / %2.2e, %2.2e
'
%
(
self
.
train_hist
[
'
f
'
][
-
1
],
self
.
tol_f
,
self
.
train_hist
[
'
f val
'
][
-
1
]))
print
(
'
|_ train, val power fun: %2.2e / %2.2e, %2.2e
'
%
(
self
.
train_hist
[
'
p
'
][
-
1
],
self
.
tol_p
,
self
.
train_hist
[
'
p val
'
][
-
1
]))
else
:
print
(
'
|_ train residual : %2.2e / %2.2e
'
%
(
self
.
train_hist
[
'
f
'
][
-
1
],
self
.
tol_f
))
print
(
'
|_ train power fun : %2.2e / %2.2e
'
%
(
self
.
train_hist
[
'
p
'
][
-
1
],
self
.
tol_p
))
if
self
.
verbose
and
when
==
'
track
'
:
print
(
'
Training ongoing with
'
)
print
(
'
|_ selected points : %8d / %8d
'
%
(
self
.
train_hist
[
'
n
'
][
-
1
],
self
.
ctrs_
.
shape
[
0
]
+
self
.
maxIter
))
if
self
.
flag_val
:
print
(
'
|_ train, val residual : %2.2e / %2.2e, %2.2e
'
%
(
self
.
train_hist
[
'
f
'
][
-
1
],
self
.
tol_f
,
self
.
train_hist
[
'
f val
'
][
-
1
]))
print
(
'
|_ train, val power fun: %2.2e / %2.2e, %2.2e
'
%
(
self
.
train_hist
[
'
p
'
][
-
1
],
self
.
tol_p
,
self
.
train_hist
[
'
p val
'
][
-
1
]))
else
:
print
(
'
|_ train residual : %2.2e / %2.2e
'
%
(
self
.
train_hist
[
'
f
'
][
-
1
],
self
.
tol_f
))
print
(
'
|_ train power fun : %2.2e / %2.2e
'
%
(
self
.
train_hist
[
'
p
'
][
-
1
],
self
.
tol_p
))
# %% Utilities to
import
pickle
def
save_object
(
obj
,
filename
):
with
open
(
filename
,
'
wb
'
)
as
output
:
pickle
.
dump
(
obj
,
output
,
pickle
.
HIGHEST_PROTOCOL
)
def
load_object
(
filename
):
with
open
(
filename
,
'
rb
'
)
as
input
:
obj
=
pickle
.
load
(
input
)
return
obj
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment