pyANOVAapprox 2.0.2__tar.gz → 2.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/.github/workflows/documentation.yml +12 -4
  2. pyanovaapprox-2.0.4/.github/workflows/format.yml +42 -0
  3. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/.gitignore +4 -1
  4. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/PKG-INFO +1 -1
  5. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/docs/source/conf.py +16 -2
  6. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/docs/source/index.rst +2 -3
  7. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/pyproject.toml +1 -1
  8. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/simpleTest/exampleNonPeriodic.py +3 -1
  9. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/simpleTest/exampleWavelet.py +13 -7
  10. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/src/pyANOVAapprox/analysis.py +16 -29
  11. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/src/pyANOVAapprox/approx.py +85 -57
  12. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/src/pyANOVAapprox/bandwidth.py +36 -22
  13. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/src/pyANOVAapprox/errors.py +11 -23
  14. pyanovaapprox-2.0.2/.github/workflows/format.yml +0 -27
  15. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/.github/workflows/ci.yml +0 -0
  16. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/.github/workflows/release.yml +0 -0
  17. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/LICENSE +0 -0
  18. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/README.md +0 -0
  19. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/docs/Makefile +0 -0
  20. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/docs/source/Analysis.rst +0 -0
  21. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/docs/source/Approximation.rst +0 -0
  22. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/docs/source/Errors.rst +0 -0
  23. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/simpleTest/TestFunctionPeriodic.py +0 -0
  24. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/simpleTest/exampleAutoapproximate.py +0 -0
  25. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/simpleTest/exampleCheb.py +0 -0
  26. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/simpleTest/exampleClassification.py +0 -0
  27. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/simpleTest/examplePeriodic.py +0 -0
  28. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/src/pyANOVAapprox/__init__.py +2 -2
  29. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/src/pyANOVAapprox/fista.py +0 -0
  30. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/src/pyANOVAapprox/trafos.py +0 -0
  31. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/tests/TestFunctionCheb.py +0 -0
  32. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/tests/TestFunctionPeriodic.py +0 -0
  33. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/tests/cheb_fista.py +0 -0
  34. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/tests/cheb_lsqr.py +0 -0
  35. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/tests/per_fista.py +0 -0
  36. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/tests/per_lsqr.py +0 -0
  37. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/tests/run_tests.py +0 -0
  38. {pyanovaapprox-2.0.2 → pyanovaapprox-2.0.4}/tests/wav_lsqr.py +0 -0
@@ -10,20 +10,28 @@ jobs:
10
10
  runs-on: ubuntu-latest
11
11
  steps:
12
12
  - uses: actions/checkout@v4
13
+
13
14
  - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.12"
17
+
14
18
  - name: Install dependencies
15
19
  run: |
16
20
  pip install sphinx sphinx_rtd_theme myst_parser
21
+ # WICHTIG: Wenn dein Code Abhängigkeiten hat (z.B. numpy, scipy),
22
+ # installiere sie hier, sonst kann 'autodoc' den Code nicht importieren!
23
+ # pip install -e .
24
+
17
25
  - name: Sphinx build
18
26
  run: |
19
27
  cd docs
20
28
  sphinx-apidoc -o ./source ../src -f
21
29
  make html
30
+
22
31
  - name: Deploy to GitHub Pages
23
- uses: peaceiris/actions-gh-pages@v3
32
+ uses: peaceiris/actions-gh-pages@v4
24
33
  if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
25
34
  with:
26
- publish_branch: gh-pages
27
35
  github_token: ${{ secrets.GITHUB_TOKEN }}
28
- publish_dir: docs/build/html
29
- force_orphan: true
36
+ publish_dir: docs/build/html
37
+ force_orphan: true
@@ -0,0 +1,42 @@
1
+ name: Format Python Code
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - '**' # Läuft bei jedem Push auf absolut jeden Branch...
7
+ tags-ignore:
8
+ - '**' # ...ABER ignoriert jeden Push, der ein Tag (Release) ist.
9
+
10
+ jobs:
11
+ python-code-format:
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: write
15
+ pull-requests: write
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+ architecture: "x64"
23
+
24
+ - name: Display Python version
25
+ run: python --version
26
+
27
+ - name: Install packages
28
+ run: pip install black isort
29
+
30
+ - name: Formatter
31
+ run: |
32
+ black .
33
+ isort .
34
+ - name: Create Pull Request
35
+ uses: peter-evans/create-pull-request@v6
36
+ with:
37
+ token: ${{ secrets.GITHUB_TOKEN }}
38
+ commit-message: "style: auto code format"
39
+ title: "Fixes by format action"
40
+ body: "This is an auto-generated PR with code formatting fixes."
41
+ labels: automated pr
42
+ branch: python-code-format-patches
@@ -7,6 +7,7 @@ dist/
7
7
  __pycache__/
8
8
  venv/
9
9
  venv2/
10
+ .venv/
10
11
 
11
12
  bin/
12
13
  /lib64/
@@ -17,4 +18,6 @@ src/pyNFFT3/lib/AVX2/libgomp-1.dll
17
18
  src/pyNFFT3/lib/AVX2/libgcc_s_seh-1.dll
18
19
  src/pyNFFT3/lib/AVX2/libwinpthread-1.dll
19
20
  simpleTest/venv/
20
- bandwidth_detection.py
21
+ bandwidth_detection*
22
+ cross_validation*
23
+ /src/pyANOVAapprox/.ipynb_checkpoints/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyANOVAapprox
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: Approximation Package for High-Dimensional Functions
5
5
  Project-URL: Homepage, https://github.com/NFFT/pyGroupedTransforms
6
6
  Author-email: Felix Wirth <fwi012001@gmail.com>
@@ -3,6 +3,13 @@
3
3
  # For the full list of built-in configuration values, see the documentation:
4
4
  # https://www.sphinx-doc.org/en/master/usage/configuration.html
5
5
 
6
+ import os
7
+ import sys
8
+
9
+ # -- Path setup --------------------------------------------------------------
10
+ # Macht den src-Ordner für Sphinx und autodoc auffindbar
11
+ sys.path.insert(0, os.path.abspath("../../src"))
12
+
6
13
  # -- Project information -----------------------------------------------------
7
14
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8
15
 
@@ -14,7 +21,13 @@ release = "29.07.2025"
14
21
  # -- General configuration ---------------------------------------------------
15
22
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
16
23
 
17
- extensions = ["sphinx.ext.mathjax"]
24
+ # Erweiterungen hinzugefügt, damit automodule, Mathe und Markdown funktionieren
25
+ extensions = [
26
+ "sphinx.ext.autodoc",
27
+ "sphinx.ext.napoleon", # Unterstützt Google/NumPy-Style Docstrings
28
+ "sphinx.ext.mathjax",
29
+ "myst_parser",
30
+ ]
18
31
 
19
32
  templates_path = ["_templates"]
20
33
  exclude_patterns = []
@@ -23,5 +36,6 @@ exclude_patterns = []
23
36
  # -- Options for HTML output -------------------------------------------------
24
37
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
25
38
 
26
- html_theme = "alabaster"
39
+ # Auf das im Workflow genutzte Theme umgestellt
40
+ html_theme = "sphinx_rtd_theme"
27
41
  html_static_path = ["_static"]
@@ -16,6 +16,5 @@ documentation for details.
16
16
  :caption: Contents:
17
17
 
18
18
  Analysis
19
- Approximation
20
- Errors
21
-
19
+ modules
20
+ Errors
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pyANOVAapprox"
7
- version = "2.0.2"
7
+ version = "2.0.4"
8
8
  authors = [
9
9
  { name="Felix Wirth", email="fwi012001@gmail.com" },
10
10
  ]
@@ -150,7 +150,9 @@ print(
150
150
  ################################################
151
151
 
152
152
  Umask = np.append(np.array([True]), gsis > 1e-2)
153
- U = [ads.getSetting().U[i] for i in np.arange(0, len(Umask))[Umask]] # get important subsets
153
+ U = [
154
+ ads.getSetting().U[i] for i in np.arange(0, len(Umask))[Umask]
155
+ ] # get important subsets
154
156
  bws = M / (math.log10(M) * (len(U) - 1)) # calculate frequencies per subset
155
157
  N = [
156
158
  math.floor(bws ** (1 / max(1, len(u))) / 2) * 2 for u in U
@@ -74,20 +74,23 @@ anova_model = ANOVAapprox.approx(X, y, ds=q, basis=basis, N=N)
74
74
 
75
75
  #### Do approximation by least-squares ###
76
76
  anova_model.approximate(lam=lambdas, solver="lsqr")
77
- print("Total number of used parameters = " + str(len(anova_model.fc[lambdas[0]].vec())))
77
+ print(
78
+ "Total number of used parameters = "
79
+ + str(len(anova_model.getFc()[lambdas[0]].vec()))
80
+ )
78
81
 
79
82
  #######################
80
83
  ## Analyze the model ##
81
84
  #######################
82
85
 
83
86
  ### Do sensitivity analysis ####
84
- anova_model.get_GSI(
85
- lam=0.0
86
- ) # calculates indices for importance of terms (gsis is vector, with indices belonging to terms in anova_model.U)
87
- anova_model.get_GSI(lam=0.0, Dict=true)
87
+ gsis = anova_model.get_GSI(
88
+ lam=0.0
89
+ ) # calculates indices for importance of terms (gsis is vector, with indices belonging to terms in anova_model.getSetting().U)
90
+ anova_model.get_GSI(lam=0.0, Dict=True)
88
91
 
89
92
  y_min_calc = 10 ** (np.min(np.log10(gsis)) - 0.5)
90
- label = list(anova_model.U[1:])
93
+ label = list(anova_model.getSetting().U[1:])
91
94
  l = len(label)
92
95
  plt.figure()
93
96
  x_values = np.arange(1, l + 1)
@@ -128,7 +131,10 @@ anova_model = anova_model.approx(
128
131
  X=X, y=y, U=U, N=[i + 2 for i in N], basis=basis
129
132
  ) # increase number of paramers in N for the important terms
130
133
  anova_model.approximate(lam=lambdas)
131
- print("Total number of used parameters = " + str(len(anova_model.fc[lambdas[0]].vec())))
134
+ print(
135
+ "Total number of used parameters = "
136
+ + str(len(anova_model.getFc()[lambdas[0]].vec()))
137
+ )
132
138
  mse_train = anova_model.get_mse(lam=0.0)
133
139
  mse_test = anova_model.get_mse(X=X_test, y=y_test, lam=0.0)
134
140
  print("MSE on test points after ANOVA truncation: " + str(mse_test))
@@ -3,22 +3,22 @@ import numpy as np
3
3
 
4
4
 
5
5
  def _variances(self, settingnr, lam, Dict): # helpfunction for get_variances
6
- setting = self.getSetting(settingnr)
6
+ setting = self.getSetting(settingnr, lam)
7
7
  if setting.basis.startswith("chui"):
8
- variances = self.getFc(settingnr)[lam].norms(
8
+ variances = self.getFc(settingnr, lam)[lam].norms(
9
9
  Dict=False, m=int(setting.basis[-1])
10
10
  )
11
11
  else:
12
- variances = self.getFc(settingnr)[lam].norms()
12
+ variances = self.getFc(settingnr, lam)[lam].norms()
13
13
 
14
14
  variances = variances[1:]
15
15
  if Dict:
16
16
  if a.basis.startswith("chui"):
17
- variances = self.getFc(settingnr)[lam].norms(
17
+ variances = self.getFc(settingnr, lam)[lam].norms(
18
18
  Dict=True, m=int(setting.basis[-1])
19
19
  )
20
20
  else:
21
- variances = self.getFc(settingnr)[lam].norms(Dict=True)
21
+ variances = self.getFc(settingnr, lam)[lam].norms(Dict=True)
22
22
 
23
23
  return {u: variances[u] for u in list(variances)}
24
24
  else:
@@ -37,31 +37,28 @@ def get_variances(self, settingnr=None, lam=None, Dict=False):
37
37
  elif (
38
38
  lam == None
39
39
  ): # get_variances( a::approx; dict::Bool = false )::Dict{Float64,Union{Vector{Float64},Dict{Vector{Int},Float64}}}
40
- return {
41
- l: self._variances(settingnr, l, Dict)
42
- for l in self.getSetting(settingnr).lam
43
- }
40
+ return {l: self._variances(settingnr, l, Dict) for l in self.lam.keys()}
44
41
 
45
42
 
46
43
  def _GSI(self, settingnr, lam, Dict): # helpfunction for get_GSI
47
- setting = self.getSetting(settingnr)
44
+ setting = self.getSetting(settingnr, lam)
48
45
  if setting.basis.startswith("chui"):
49
46
  variances = np.square(
50
- self.getFc(settingnr)[lam].norms(Dict=False, m=int(setting.basis[-1]))
47
+ self.getFc(settingnr, lam)[lam].norms(Dict=False, m=int(setting.basis[-1]))
51
48
  )
52
49
  else:
53
- variances = np.square(self.getFc(settingnr)[lam].norms())
50
+ variances = np.square(self.getFc(settingnr, lam)[lam].norms())
54
51
 
55
52
  variances = variances[1:]
56
53
  variance_f = sum(variances)
57
54
 
58
55
  if Dict:
59
56
  if setting.basis.startswith("chui"):
60
- variances = self.getFc(settingnr)[lam].norms(
57
+ variances = self.getFc(settingnr, lam)[lam].norms(
61
58
  Dict=True, m=int(setting.basis[-1])
62
59
  )
63
60
  else:
64
- variances = self.getFc(settingnr)[lam].norms(Dict=True)
61
+ variances = self.getFc(settingnr, lam)[lam].norms(Dict=True)
65
62
  return {u: (variances[u] ** 2) / variance_f for u in list(variances)}
66
63
 
67
64
  else:
@@ -78,9 +75,7 @@ def get_GSI(self, settingnr=None, lam=None, Dict=False):
78
75
  ): # get_GSI(a::approx, λ::Float64; dict::Bool = false,)::Union{Vector{Float64},Dict{Vector{Int},Float64}}
79
76
  return self._GSI(settingnr, lam, Dict)
80
77
  else: # get_GSI( a::approx; dict::Bool = false )::Dict{Float64,Union{Vector{Float64},Dict{Vector{Int},Float64}}}
81
- return {
82
- l: self._GSI(settingnr, l, Dict) for l in self.getSetting(settingnr).lam
83
- }
78
+ return {l: self._GSI(settingnr, l, Dict) for l in self.lam.keys()}
84
79
 
85
80
 
86
81
  def _AttributeRanking(self, settingnr, lam): # helpfunction for get_AttributeRanking
@@ -120,17 +115,14 @@ def get_AttributeRanking(self, settingnr=None, lam=None):
120
115
  if (
121
116
  lam is None
122
117
  ): # get_AttributeRanking( a::approx, λ::Float64 )::Dict{Float64,Vector{Float64}}
123
- return {
124
- l: self._AttributeRanking(settingnr, l)
125
- for l in self.getSetting(settingnr).lam
126
- }
118
+ return {l: self._AttributeRanking(settingnr, l) for l in self.lam.keys()}
127
119
  else: # get_AttributeRanking( a::approx, λ::Float64 )::Vector{Float64}
128
120
  return self._AttributeRanking(settingnr, lam)
129
121
 
130
122
 
131
123
  def _ActiveSet(self, eps, settingnr, lam): # helpfunction for get_ActiveSet
132
124
 
133
- U = self.getSetting(settingnr).U[1:]
125
+ U = self.getSetting(settingnr, lam).U[1:]
134
126
  lengths = [len(u) for u in U]
135
127
  ds = max(lengths)
136
128
 
@@ -162,10 +154,7 @@ def get_ActiveSet(self, eps, settingnr=None, lam=None):
162
154
  if (
163
155
  lam is None
164
156
  ): # get_ActiveSet(a::approx, eps::Vector{Float64})::Dict{Float64,Vector{Vector{Int}}}
165
- return {
166
- l: self._ActiveSet(eps, settingnr, l)
167
- for l in self.getSetting(settingnr).lam
168
- }
157
+ return {l: self._ActiveSet(eps, settingnr, l) for l in self.lam.keys()}
169
158
  else: # get_ActiveSet(a::approx, eps::Vector{Float64}, λ::Float64)::Vector{Vector{Int}}
170
159
  return self._ActiveSet(eps, settingnr, lam)
171
160
 
@@ -190,9 +179,7 @@ def get_ShapleyValues(self, settingnr=None, lam=None):
190
179
  This function returns the Shapley values of the approximation for all reg. parameters ``\lambda``, if lam == None, as a dictionary of vectors of length `a.d`. Otherwise for the provided lam as a vector of length `a.d`.
191
180
  """
192
181
  if lam is None: # get_ShapleyValues(a::approx)::Dict{Float64,Vector{Float64}}
193
- return {
194
- l: self._ShapleyValues(settingnr, l) for l in self.getSetting(settingnr).lam
195
- }
182
+ return {l: self._ShapleyValues(settingnr, l) for l in self.lam.keys()}
196
183
  else: # get_ShapleyValues(a::approx, λ::Float64)::Vector{Float64}
197
184
  return self._ShapleyValues(settingnr, lam)
198
185
 
@@ -1,4 +1,5 @@
1
1
  import copy
2
+ import warnings
2
3
 
3
4
  import pyANOVAapprox.analysis as ANOVAanalysis
4
5
  import pyANOVAapprox.bandwidth as ANOVAbandwidth
@@ -186,8 +187,13 @@ class approx:
186
187
  lam={0.0},
187
188
  ):
188
189
 
190
+ if type(lam) is np.ndarray:
191
+ lam = {float(i) for i in lam}
192
+ if type(lam) is list:
193
+ lam = set(lam)
194
+
189
195
  self.X = X
190
-
196
+
191
197
  if X.shape[0] != y.shape[0]:
192
198
  raise ValueError("X and y have different lengths.")
193
199
 
@@ -222,6 +228,9 @@ class approx:
222
228
  self.trafo = [None]
223
229
  self.fc = [{}]
224
230
  self.aktsetting = 0
231
+ self.lam = {}
232
+ for i in lam:
233
+ self.lam[i] = [0]
225
234
 
226
235
  if setting.N is not None:
227
236
  self.addTrafo()
@@ -233,17 +242,24 @@ class approx:
233
242
  self.trafo.append(None)
234
243
  self.fc.append({})
235
244
 
236
- def getSettingNr(self, settingnr=None):
237
- return self.aktsetting if settingnr is None else settingnr
245
+ def getSettingNr(self, settingnr=None, lam=None, warn=True):
246
+ if lam is None:
247
+ return self.aktsetting if settingnr is None else settingnr
248
+ else:
249
+ if lam in self.lam.keys():
250
+ return self.lam[lam][-1] if settingnr is None else settingnr
251
+ else:
252
+ warnings.warn("Not yet approximated for this lambda")
253
+ return self.aktsetting if settingnr is None else settingnr
238
254
 
239
- def getSetting(self, settingnr=None):
240
- return self.setting[self.getSettingNr(settingnr)]
255
+ def getSetting(self, settingnr=None, lam=None):
256
+ return self.setting[self.getSettingNr(settingnr, lam)]
241
257
 
242
- def getTrafo(self, settingnr=None):
243
- return self.trafo[self.getSettingNr(settingnr)]
258
+ def getTrafo(self, settingnr=None, lam=None):
259
+ return self.trafo[self.getSettingNr(settingnr, lam)]
244
260
 
245
- def getFc(self, settingnr=None):
246
- return self.fc[self.getSettingNr(settingnr)]
261
+ def getFc(self, settingnr=None, lam=None):
262
+ return self.fc[self.getSettingNr(settingnr, lam)]
247
263
 
248
264
  def addTrafo(self, settingnr=None):
249
265
  setting = self.getSetting(settingnr)
@@ -356,6 +372,7 @@ class approx:
356
372
  If lam is a np.ndarray of dtype float, this function computes the approximation for the regularization parameters contained in lam.
357
373
  If lam is a float, this function computes the approximation for the regularization parameter lam.
358
374
  """
375
+
359
376
  setting = self.getSetting(settingnr)
360
377
 
361
378
  if lam is None:
@@ -393,6 +410,7 @@ class approx:
393
410
  solver_verbose,
394
411
  solver_tol,
395
412
  ):
413
+ settingnrs = []
396
414
  setting = self.getSetting(settingnr)
397
415
 
398
416
  n = len(self.y)
@@ -401,13 +419,14 @@ class approx:
401
419
 
402
420
  D = dict([(u, tuple([1.0] * len(u))) for u in setting.U])
403
421
  t = dict([(u, tuple([1.0] * len(u))) for u in setting.U])
404
-
405
- if verbosity>3:
422
+
423
+ if verbosity > 3:
406
424
  if not os.path.exists("log"):
407
425
  os.mkdir("log")
408
- with open('log/log.csv', 'w', newline='') as csvfile:
409
- csv.writer(csvfile, delimiter=',').writerow(["it"] + setting.U)
410
-
426
+ with open("log/log.csv", "w", newline="") as csvfile:
427
+ csv.writer(csvfile, delimiter=",").writerow(["it"] + setting.U)
428
+ if verbosity > 2:
429
+ print("B =", B)
411
430
  for idx in range(maxiter):
412
431
  if verbosity > 0:
413
432
  print("===== Iteration ", str(idx + 1), " =====")
@@ -415,6 +434,7 @@ class approx:
415
434
  if setting.N is not None:
416
435
  self.addSetting(setting)
417
436
  settingnr = self.aktsetting
437
+ settingnrs = settingnrs + [settingnr]
418
438
  setting = self.getSetting(settingnr)
419
439
 
420
440
  setting.N = [bw[i] for i in setting.U]
@@ -423,11 +443,13 @@ class approx:
423
443
  if verbosity > 0:
424
444
  for i in setting.U:
425
445
  print("bw in", str(i), ":", bw[i])
426
- #print("bw in iteration", str(idx + 1), "are", str(bw))
446
+ # print("bw in iteration", str(idx + 1), "are", str(bw))
427
447
  print()
428
448
  if verbosity > 3:
429
- with open('log/log.csv', 'a', newline='') as csvfile:
430
- csv.writer(csvfile, delimiter=',').writerow(["bw in it"+str(idx+1)] + [str(bw[i]) for i in setting.U])
449
+ with open("log/log.csv", "a", newline="") as csvfile:
450
+ csv.writer(csvfile, delimiter=",").writerow(
451
+ ["bw in it" + str(idx + 1)] + [str(bw[i]) for i in setting.U]
452
+ )
431
453
  self.approximate(
432
454
  lam=lam,
433
455
  solver=solver,
@@ -440,23 +462,39 @@ class approx:
440
462
  D, t = self.estimate_rates(lam=lam, verbosity=verbosity)
441
463
  if verbosity > 1:
442
464
  for i in setting.U:
443
- print("estimated rates for", str(i), ": D = ", str(D[i]), "and t = ", str(t[i]) )
465
+ print(
466
+ "estimated rates for",
467
+ str(i),
468
+ ": D = ",
469
+ str(D[i]),
470
+ "and t = ",
471
+ str(t[i]),
472
+ )
444
473
  print()
445
-
446
- #print(
474
+
475
+ # print(
447
476
  # "estimated rates in iteration",
448
477
  # str(idx + 1),
449
478
  # "are D =",
450
479
  # str(D),
451
480
  # "and t =",
452
481
  # str(t),
453
- #)
482
+ # )
454
483
  if verbosity > 3:
455
- with open('log/log.csv', 'a', newline='') as csvfile:
456
- wr = csv.writer(csvfile, delimiter=',')
457
- wr.writerow(["D in it"+str(idx+1)] + [str(D[i]) for i in setting.U])
458
- wr.writerow(["t in it"+str(idx+1)] + [str(t[i]) for i in setting.U])
459
- return D, t
484
+ with open("log/log.csv", "a", newline="") as csvfile:
485
+ wr = csv.writer(csvfile, delimiter=",")
486
+ wr.writerow(
487
+ ["D in it" + str(idx + 1)] + [str(D[i]) for i in setting.U]
488
+ )
489
+ wr.writerow(
490
+ ["t in it" + str(idx + 1)] + [str(t[i]) for i in setting.U]
491
+ )
492
+
493
+ if all([math.isnan(j) for j in sum([D[i] for i in D], [])]) and all(
494
+ [math.isnan(j) for j in sum([t[i] for i in D], [])]
495
+ ):
496
+ break
497
+ return settingnrs
460
498
 
461
499
  def autoapproximate(
462
500
  self,
@@ -471,18 +509,21 @@ class approx:
471
509
  solver_verbose=False,
472
510
  solver_tol=1e-8,
473
511
  ):
512
+ settingnr = self.getSettingNr(settingnr)
474
513
  setting = self.getSetting(settingnr)
475
514
 
476
515
  if lam is None:
477
516
  lam = setting.lam
478
517
  else:
518
+ if isinstance(lam, np.ndarray):
519
+ lam = {float(l) for l in lam}
479
520
  if not isinstance(lam, set):
480
521
  lam = {lam}
481
522
  setting.lam = lam
482
523
 
483
- if len(lam) == 1:
484
- return self._autoapproximate(
485
- lam.pop(),
524
+ for l in lam:
525
+ settingnrs = self._autoapproximate(
526
+ l,
486
527
  settingnr=settingnr,
487
528
  B=B,
488
529
  maxiter=maxiter,
@@ -493,22 +534,7 @@ class approx:
493
534
  solver_verbose=solver_verbose,
494
535
  solver_tol=solver_tol,
495
536
  )
496
- else:
497
- return {
498
- l: self._autoapproximate(
499
- l,
500
- settingnr=settingnr,
501
- B=B,
502
- maxiter=maxiter,
503
- solver=solver,
504
- verbosity=verbosity,
505
- solver_max_iter=solver_max_iter,
506
- solver_weights=solver_weights,
507
- solver_verbose=solver_verbose,
508
- solver_tol=solver_tol,
509
- )
510
- for l in lam
511
- }
537
+ self.lam[l] = self.lam[l] + settingnrs
512
538
 
513
539
  def evaluate(self, settingnr=None, lam=None, X=None):
514
540
  """
@@ -519,7 +545,7 @@ class approx:
519
545
  - If only lam is given: evaluate at self.X for specific lam.
520
546
  - If neither are given: evaluate at self.X for all lam.
521
547
  """
522
- setting = self.getSetting(settingnr)
548
+ setting = self.getSetting(settingnr, lam)
523
549
 
524
550
  if X is not None:
525
551
  if setting.basis == "per" and (np.min(X) < -0.5 or np.max(X) >= 0.5):
@@ -537,14 +563,14 @@ class approx:
537
563
  basis_vect=setting.basis_vect,
538
564
  )
539
565
  else:
540
- trafo = self.getTrafo(settingnr)
566
+ trafo = self.getTrafo(settingnr, lam)
541
567
 
542
568
  if (
543
569
  lam is not None
544
570
  ): # evaluate( a::approx; X::Matrix{Float64}, λ::Float64 )::Union{Vector{ComplexF64},Vector{Float64}}
545
- return trafo @ self.getFc(settingnr)[lam]
571
+ return trafo @ self.getFc(settingnr, lam)[lam]
546
572
  else: # evaluate( a::approx; X::Matrix{Float64} )::Dict{Float64,Union{Vector{ComplexF64},Vector{Float64}}}
547
- return {λ: trafo @ self.getFc(settingnr)[λ] for λ in setting.lam}
573
+ return {λ: trafo @ self.getFc(settingnr, lam)[λ] for λ in self.lam.keys()}
548
574
 
549
575
  def evaluateANOVAterms(self, settingnr=None, X=None, lam=None):
550
576
  """
@@ -553,7 +579,7 @@ class approx:
553
579
  - If lam is given: evaluate at X for specific lam.
554
580
  - If lam is not given: evaluate at X for all lam.
555
581
  """
556
- setting = self.getSetting(settingnr)
582
+ setting = self.getSetting(settingnr, lam)
557
583
  if X is None:
558
584
  X = self.X
559
585
 
@@ -567,9 +593,11 @@ class approx:
567
593
  Xt = transformX(X, setting.basis)
568
594
 
569
595
  if self.getSetting(settingnr).basis == "per":
570
- values = np.zeros((Xt.shape[0], len(self.setting[settingnr].U)), "complex")
596
+ values = np.zeros(
597
+ (Xt.shape[0], len(self.getSetting(settingnr).U)), "complex"
598
+ )
571
599
  else:
572
- values = np.zeros((Xt.shape[0], len(self.setting[settingnr].U)), "float")
600
+ values = np.zeros((Xt.shape[0], len(self.getSetting(settingnr).U)), "float")
573
601
 
574
602
  trafo = GroupedTransform(
575
603
  system=gt_systems[setting.basis],
@@ -583,14 +611,14 @@ class approx:
583
611
  lam is not None
584
612
  ): # evaluateANOVAterms( a::approx; X::Matrix{Float64}, λ::Float64 )::Union{Matrix{ComplexF64},Matrix{Float64}}
585
613
  for j, u in enumerate(setting.U):
586
- values[:, j] = trafo[u] @ self.getFc(settingnr)[lam][u]
614
+ values[:, j] = trafo[u] @ self.getFc(settingnr, lam)[lam][u]
587
615
  return values
588
616
  else: # evaluateANOVAterms( a::approx; X::Matrix{Float64} )::Dict{Float64,Union{Matrix{ComplexF64},Matrix{Float64}}}
589
617
  results = {}
590
- for λ in setting.lam:
618
+ for λ in self.lam.keys():
591
619
  vals = np.zeros_like(values)
592
620
  for j, u in enumerate(setting.U):
593
- vals[:, j] = trafo[u] @ self.getFc(settingnr)[λ][u]
621
+ vals[:, j] = trafo[u] @ self.getFc(settingnr, lam)[λ][u]
594
622
  results[λ] = vals
595
623
  return results
596
624
 
@@ -601,7 +629,7 @@ class approx:
601
629
  - If lam is given: evaluate at X for specific lam.
602
630
  - If lam is not given: evaluate at X for all lam.
603
631
  """
604
- setting = self.getSetting(settingnr)
632
+ setting = self.getSetting(settingnr, lam)
605
633
 
606
634
  if X is None:
607
635
  X = self.X
@@ -631,7 +659,7 @@ class approx:
631
659
 
632
660
  else: # evaluateSHAPterms( a::approx; X::Matrix{Float64} )::Dict{Float64,Union{Matrix{ComplexF64},Matrix{Float64}}}
633
661
  results = {}
634
- for l in setting.lam:
662
+ for l in self.lam.keys():
635
663
  terms = self.evaluateANOVAterms(X, l)
636
664
 
637
665
  Dtype = np.complex128 if setting.basis == "per" else np.float64
@@ -5,11 +5,11 @@ def getfcu(ghat, u):
5
5
  idx = [s.u for s in ghat.settings].index(u)
6
6
  bws = ghat.settings[idx].bandwidths
7
7
 
8
- #fcu = ghat[u].reshape(bws[::-1] - 1)
9
- #fcu = np.permute_dims(fcu, range(len(bws))[::-1])
8
+ # fcu = ghat[u].reshape(bws[::-1] - 1)
9
+ # fcu = np.permute_dims(fcu, range(len(bws))[::-1])
10
10
  fcu = ghat[u].reshape(bws - 1)
11
11
  fcu = np.permute_dims(fcu, range(len(bws)))
12
-
12
+
13
13
  return fcu
14
14
 
15
15
 
@@ -30,6 +30,9 @@ def getaxissum(ghat, u, j, system):
30
30
  else:
31
31
  raise ValueError("For this basis is estimate rates not implemented")
32
32
  # fcuj = np.concatenate(fcuj[math.ceil(bws[j]/2):] + [fcuj[0]]) + fcuj[math.ceil(bws[j]/2)-1::-1]
33
+
34
+ fcuj = np.cumsum(fcuj[::-1])[::-1]
35
+
33
36
  return fcuj
34
37
 
35
38
 
@@ -37,13 +40,14 @@ def compute_bandwidth(B, D, t):
37
40
  us = set(D.keys()) - {()}
38
41
  bw = {u: [6] * len(u) for u in us}
39
42
  bw[()] = []
40
-
43
+
41
44
  minfreqs = sum(math.prod((bw[u][j] - 1) for j in range(len(u))) for u in us)
42
45
  if B < minfreqs:
43
46
  raise ValueError(f"Budget too small: {B} < {minfreqs}")
44
47
 
45
48
  def fun_lmbda_u(lmbda, u):
46
- p1 = math.prod((bw[u][j] - 1) for j in range(len(u)) if math.isnan(t[u][j]))
49
+ p1 = math.prod((bw[u][j] - 1) for j in range(len(u)) if not math.isnan(t[u][j]))
50
+
47
51
  p2 = math.prod(
48
52
  ((2 * t[u][j] * D[u][j]) / lmbda) ** (0.5 / t[u][j])
49
53
  for j in range(len(u))
@@ -69,7 +73,7 @@ def compute_bandwidth(B, D, t):
69
73
  bwuj = ((2 * t[u][j] * D[u][j]) / (lmbda * sizeIu)) ** (
70
74
  0.5 / t[u][j]
71
75
  ) + 1
72
- bw[u][j] = 2 * round(0.5 * bwuj)
76
+ bw[u][j] = max(6, 2 * round(0.5 * bwuj))
73
77
 
74
78
  return bw
75
79
 
@@ -123,19 +127,19 @@ def estimate_rates(self, lam, settingnr=None, verbosity=0):
123
127
 
124
128
  mcl = np.exp(most_common_value(np.log(abs(self.getFc(settingnr)[lam].data))))
125
129
  threshold = 100 * mcl**2
126
-
127
- if verbosity>5:
128
- if not os.path.exists(os.path.join("log","figures")):
129
- os.mkdir(os.path.join("log","figures"))
130
+
131
+ if verbosity > 5:
132
+ if not os.path.exists(os.path.join("log", "figures")):
133
+ os.mkdir(os.path.join("log", "figures"))
130
134
  num = 0
131
-
135
+
132
136
  system = self.getTrafo(settingnr).system
133
137
 
134
138
  for u in us:
135
139
  if len(u) == 0:
136
140
  continue
137
141
 
138
- if verbosity>5:
142
+ if verbosity > 5:
139
143
  fig, ax = plt.subplots()
140
144
  ax.set_xscale("log")
141
145
  ax.set_yscale("log")
@@ -154,17 +158,16 @@ def estimate_rates(self, lam, settingnr=None, verbosity=0):
154
158
  None,
155
159
  )
156
160
 
157
- if verbosity>5:
161
+ if verbosity > 5:
158
162
 
159
163
  y = np.cumsum(axissum[::-1])[::-1]
160
164
  ax.plot(
161
- range(1, len(y) + 1),
162
- y,
165
+ axissumnum,
166
+ axissum,
163
167
  label=u[j],
164
168
  marker="o",
165
169
  # color=j
166
170
  )
167
-
168
171
  if (idx is None) or idx >= len(axissum):
169
172
  D[u][j] = math.nan
170
173
  t[u][j] = math.nan
@@ -174,7 +177,7 @@ def estimate_rates(self, lam, settingnr=None, verbosity=0):
174
177
  D[u][j] = Duj
175
178
  t[u][j] = -tuj / 2
176
179
 
177
- if verbosity>5:
180
+ if verbosity > 5:
178
181
  x = np.arange(1, idx + 1)
179
182
  ax.plot(
180
183
  x,
@@ -182,12 +185,23 @@ def estimate_rates(self, lam, settingnr=None, verbosity=0):
182
185
  linewidth=2,
183
186
  # color=j
184
187
  )
185
- if verbosity>5:
186
- #plt.figure(figsize=(12, 9))
187
- #for ax in ps:
188
+ if verbosity > 5:
189
+ # plt.figure(figsize=(12, 9))
190
+ # for ax in ps:
188
191
  # plt.sca(ax)
189
- fig.savefig(os.path.join("log","figures", str(num).strip()+ "_rates_" + str(u).strip() + ".png"))
192
+ time = ""
193
+ if verbosity > 8:
194
+ from datetime import datetime
195
+
196
+ time = str(round(datetime.timestamp(datetime.now())))
197
+ fig.savefig(
198
+ os.path.join(
199
+ "log",
200
+ "figures",
201
+ time + "_" + str(num).strip() + "_rates_" + str(u).strip() + ".png",
202
+ )
203
+ )
190
204
  num = num + 1
191
- plt.close(fig)
205
+ plt.close(fig)
192
206
 
193
207
  return D, t
@@ -37,10 +37,7 @@ def get_l2_error(self, settingnr=None, X=None, y=None, lam=None):
37
37
  if lam is not None:
38
38
  return self._l2_error(settingnr, lam, X, y)
39
39
  else:
40
- return {
41
- l: self._l2_error(settingnr, l, X, y)
42
- for l in self.getSetting(settingnr).lam
43
- }
40
+ return {l: self._l2_error(settingnr, l, X, y) for l in self.lam.keys()}
44
41
 
45
42
 
46
43
  def _mse(self, settingnr, lam, X, y): # helpfunction for get_mse
@@ -77,9 +74,7 @@ def get_mse(self, settingnr=None, X=None, y=None, lam=None):
77
74
  if lam is not None:
78
75
  return self._mse(settingnr, lam, X, y)
79
76
  else:
80
- return {
81
- l: self._mse(settingnr, l, X, y) for l in self.getSetting(settingnr).lam
82
- }
77
+ return {l: self._mse(settingnr, l, X, y) for l in self.lam.keys()}
83
78
 
84
79
 
85
80
  def _mad(self, settingnr, lam, X, y): # helpfunction for get_mad
@@ -116,21 +111,21 @@ def get_mad(self, settingnr=None, X=None, y=None, lam=None):
116
111
  if lam is not None:
117
112
  return self._mad(settingnr, lam, X, y)
118
113
  else:
119
- return {
120
- l: self._mad(settingnr, l, X, y) for l in self.getSetting(settingnr).lam
121
- }
114
+ return {l: self._mad(settingnr, l, X, y) for l in self.lam.keys()}
122
115
 
123
116
 
124
117
  def _L2_error(self, norm, bc_fun, settingnr, lam):
125
118
  # print(settingnr)
126
- if self.getSetting(settingnr).basis in {"per", "cos", "cheb", "std", "mixed"}:
119
+ if self.getSetting(settingnr, lam).basis in {"per", "cos", "cheb", "std", "mixed"}:
127
120
  error = norm**2
128
- index_set = gt.get_IndexSet(self.getTrafo(settingnr).settings, self.X.shape[1])
121
+ index_set = gt.get_IndexSet(
122
+ self.getTrafo(settingnr, lam).settings, self.X.shape[1]
123
+ )
129
124
 
130
125
  for i in range(index_set.shape[1]):
131
126
  k = index_set[:, i]
132
127
  error += (
133
- abs(bc_fun(k) - self.getFc(settingnr)[lam][i]) ** 2
128
+ abs(bc_fun(k) - self.getFc(settingnr, lam)[lam][i]) ** 2
134
129
  - abs(bc_fun(k)) ** 2
135
130
  )
136
131
 
@@ -153,10 +148,7 @@ def get_L2_error(self, norm, bc_fun, settingnr=None, lam=None):
153
148
  if lam is not None:
154
149
  return self._L2_error(norm, bc_fun, settingnr, lam)
155
150
  else:
156
- return {
157
- l: self._L2_error(norm, bc_fun, settingnr, l)
158
- for l in self.getSetting(settingnr).lam
159
- }
151
+ return {l: self._L2_error(norm, bc_fun, settingnr, l) for l in self.lam.keys()}
160
152
 
161
153
 
162
154
  def _acc(self, settingnr, lam, X, y): # helpfunction for get_acc
@@ -176,9 +168,7 @@ def get_acc(self, settingnr=None, X=None, y=None, lam=None):
176
168
  if lam is not None:
177
169
  return self._acc(settingnr, lam, X, y)
178
170
  else:
179
- return {
180
- l: self._acc(settingnr, l, X, y) for l in self.getSetting(settingnr).lam
181
- }
171
+ return {l: self._acc(settingnr, l, X, y) for l in self.lam.keys()}
182
172
 
183
173
 
184
174
  def auc_score(y_true, y_pred_proba):
@@ -240,9 +230,7 @@ def get_auc(self, settingnr=None, X=None, y=None, lam=None):
240
230
  if lam is not None:
241
231
  return self._auc(settingnr, lam, X, y)
242
232
  else:
243
- return {
244
- l: self._auc(settingnr, l, X, y) for l in self.getSetting(settingnr).lam
245
- }
233
+ return {l: self._auc(settingnr, l, X, y) for l in self.lam.keys()}
246
234
 
247
235
 
248
236
  __all__ = [
@@ -1,27 +0,0 @@
1
- name: Format Python Code
2
- on: push
3
- jobs:
4
- python-code-format:
5
- runs-on: ubuntu-latest
6
- steps:
7
- - uses: actions/checkout@v2
8
- - uses: actions/setup-python@v4
9
- with:
10
- python-version: "3.12"
11
- architecture: "x64"
12
- - name: Display Python version
13
- run: python --version
14
- - name: Install packages
15
- run: pip install black isort
16
- - name: Formatter
17
- run: |
18
- black .
19
- isort .
20
- - name: Create Pull Request
21
- uses: peter-evans/create-pull-request@v3
22
- with:
23
- commit-message: Auto code format
24
- title: Fixes by format action
25
- body: This is an auto-generated PR with fixes.
26
- labels: automated pr
27
- branch: python-code-format-patches
File without changes
File without changes
@@ -1,4 +1,6 @@
1
+ import csv
1
2
  import math
3
+ import os
2
4
  import threading
3
5
  from math import acos, isnan
4
6
 
@@ -8,8 +10,6 @@ from pyGroupedTransforms import *
8
10
  from scipy.optimize import bisect
9
11
  from scipy.sparse.linalg import lsqr
10
12
  from scipy.special import erf
11
- import csv
12
- import os
13
13
 
14
14
  # from sklearn.metrics import roc_auc_score
15
15