hoppMCMC 2.2.1__tar.gz → 2.2.2__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 (23) hide show
  1. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/NEWS.txt +1 -1
  2. {hoppmcmc-2.2.1/src/hoppMCMC.egg-info → hoppmcmc-2.2.2}/PKG-INFO +1 -1
  3. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/pyproject.toml +1 -1
  4. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/setup.cfg +1 -1
  5. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC/__init__.py +1 -1
  6. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2/src/hoppMCMC.egg-info}/PKG-INFO +1 -1
  7. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/inferFun/__init__.py +118 -45
  8. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/LICENSE +0 -0
  9. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/MANIFEST.in +0 -0
  10. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/README.txt +0 -0
  11. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/setup.py +0 -0
  12. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC/doc/hoppMCMC_manual.pdf +0 -0
  13. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC/examples/example1.py +0 -0
  14. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC/examples/example2.py +0 -0
  15. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC/examples/example3.py +0 -0
  16. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC/examples/example4.py +0 -0
  17. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC/examples/example5.py +0 -0
  18. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC.egg-info/SOURCES.txt +0 -0
  19. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC.egg-info/dependency_links.txt +0 -0
  20. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC.egg-info/not-zip-safe +0 -0
  21. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC.egg-info/requires.txt +0 -0
  22. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/hoppMCMC.egg-info/top_level.txt +0 -0
  23. {hoppmcmc-2.2.1 → hoppmcmc-2.2.2}/src/myMPI/__init__.py +0 -0
@@ -1,7 +1,7 @@
1
1
  News
2
2
  ====
3
3
 
4
- 2.2.1
4
+ 2.2.2
5
5
  -----
6
6
 
7
7
  *Release date: 13-Jul-2026*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hoppMCMC
3
- Version: 2.2.1
3
+ Version: 2.2.2
4
4
  Summary: An adaptive basin-hopping Markov-chain Monte Carlo algorithm for Bayesian optimisation
5
5
  Author: Kamil Erguler
6
6
  Author-email: Kamil Erguler <kerguler@gmail.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hoppMCMC"
7
- version = "2.2.1"
7
+ version = "2.2.2"
8
8
  description = "An adaptive basin-hopping Markov-chain Monte Carlo algorithm for Bayesian optimisation"
9
9
  authors = [{ name = "Kamil Erguler", email = "kerguler@gmail.com" }]
10
10
  license = { text = "GPLv3" }
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = hoppMCMC
3
- version = 2.2.1
3
+ version = 2.2.2
4
4
  description = Adaptive basin-hopping MCMC and ABC inference with MPI support
5
5
  author = Kamil Erguler
6
6
  author_email = k.erguler@cyi.ac.cy
@@ -6,7 +6,7 @@ This is the python (v2.7) implementation of the hoppMCMC algorithm aiming to ide
6
6
 
7
7
  """
8
8
 
9
- __version__ = '2.2.1'
9
+ __version__ = '2.2.2'
10
10
 
11
11
  import os
12
12
  import sys
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hoppMCMC
3
- Version: 2.2.1
3
+ Version: 2.2.2
4
4
  Summary: An adaptive basin-hopping Markov-chain Monte Carlo algorithm for Bayesian optimisation
5
5
  Author: Kamil Erguler
6
6
  Author-email: Kamil Erguler <kerguler@gmail.com>
@@ -477,64 +477,137 @@ class inferABCModels:
477
477
 
478
478
  def log_kernel_density(self, model_id, x, mean, kernel):
479
479
  inferpar = self.inferpars[model_id]
480
-
481
- x_inf = x[inferpar]
482
- mean_inf = mean[inferpar]
480
+ x_inf = numpy.asarray(x[inferpar], dtype=float)
481
+ mean_inf = numpy.asarray(mean[inferpar], dtype=float)
482
+ d = len(inferpar)
483
483
 
484
484
  kernel = numpy.asarray(kernel, dtype=float)
485
485
  if kernel.ndim == 0:
486
- kernel = numpy.array([[kernel]])
487
-
488
- # Add small jitter to avoid singular covariance problems.
489
- kernel = kernel + numpy.eye(kernel.shape[0]) * self.jitter
490
-
491
- eigvals, eigvecs = numpy.linalg.eigh(kernel)
492
- if numpy.any(eigvals <= 0):
486
+ kernel = numpy.array([[float(kernel)]], dtype=float)
487
+ elif kernel.ndim == 1:
488
+ kernel = numpy.diag(kernel)
489
+ elif kernel.ndim != 2:
493
490
  raise ValueError(
494
- f"Kernel is not positive definite for model {model_id} "
495
- f"({self.model_names[model_id]}), even after jitter. "
496
- f"Eigenvalues={eigvals}\nKernel:\n{kernel}"
491
+ f"Kernel for model {model_id} ({self.model_names[model_id]}) "
492
+ f"must be scalar, 1D, or 2D. Got shape {kernel.shape}."
497
493
  )
498
-
499
- condition_number = eigvals[-1] / eigvals[0]
500
- if condition_number > 1e10 and self.verbose:
501
- smallest = numpy.argmin(eigvals)
502
- bad_direction = eigvecs[:, smallest]
503
- culprit_order = numpy.argsort(numpy.abs(bad_direction))[::-1]
504
- print(
505
- f"Warning: near-singular kernel for model {model_id} "
494
+ if kernel.shape != (d, d):
495
+ raise ValueError(
496
+ f"Kernel dimension mismatch for model {model_id} "
506
497
  f"({self.model_names[model_id]}). "
507
- f"condition_number={condition_number:g}, "
508
- f"eigenvalues={eigvals}",
509
- flush=True
498
+ f"Expected {(d, d)}, got {kernel.shape}. "
499
+ f"inferpar={inferpar}"
510
500
  )
511
- print(" Dominant dimensions in smallest-eigenvalue direction:", flush=True)
512
- for k in culprit_order:
501
+ if not numpy.all(numpy.isfinite(x_inf)):
502
+ raise ValueError(
503
+ f"Non-finite x values for model {model_id} "
504
+ f"({self.model_names[model_id]}): {x_inf}"
505
+ )
506
+ if not numpy.all(numpy.isfinite(mean_inf)):
507
+ raise ValueError(
508
+ f"Non-finite kernel mean for model {model_id} "
509
+ f"({self.model_names[model_id]}): {mean_inf}"
510
+ )
511
+ if not numpy.all(numpy.isfinite(kernel)):
512
+ raise ValueError(
513
+ f"Non-finite kernel entries for model {model_id} "
514
+ f"({self.model_names[model_id]}):\n{kernel}"
515
+ )
516
+ # Symmetrise, just in case floating-point noise introduced asymmetry.
517
+ kernel = 0.5 * (kernel + kernel.T)
518
+ diag = numpy.diag(kernel)
519
+ if numpy.any(diag < 0):
520
+ raise ValueError(
521
+ f"Negative diagonal variance in kernel for model {model_id} "
522
+ f"({self.model_names[model_id]}).\n"
523
+ f"Diagonal={diag}\n"
524
+ f"Kernel=\n{kernel}"
525
+ )
526
+ eigvals, eigvecs = numpy.linalg.eigh(kernel)
527
+ max_eig = numpy.max(eigvals)
528
+ # A purely absolute jitter may be too small if the largest variance is large.
529
+ # This floor is both absolute and relative.
530
+ eig_floor = max(self.jitter, self.jitter * max(1.0, max_eig))
531
+ if numpy.any(eigvals <= eig_floor):
532
+ bad = numpy.where(eigvals <= eig_floor)[0]
533
+ if self.verbose:
513
534
  print(
514
- f" kernel dim {k}, parameter index {inferpar[k]}, "
515
- f"loading={bad_direction[k]:g}, "
516
- f"variance={kernel[k, k]:g}",
535
+ f"Warning: regularising near-singular kernel for model {model_id} "
536
+ f"({self.model_names[model_id]}).",
517
537
  flush=True
518
538
  )
519
-
520
- sd = numpy.sqrt(numpy.diag(kernel))
521
- corr = kernel / numpy.outer(sd, sd)
522
- if self.verbose:
523
- print(" correlation matrix:", flush=True)
524
- print(corr, flush=True)
525
-
526
- high = numpy.where(numpy.abs(corr) > 0.98)
527
-
528
- for a, b in zip(high[0], high[1]):
529
- if a < b:
539
+ print(f" parameter indices: {inferpar}", flush=True)
540
+ print(f" diagonal variances: {diag}", flush=True)
541
+ print(f" eigenvalues before regularisation: {eigvals}", flush=True)
542
+ print(f" eigenvalue floor: {eig_floor:g}", flush=True)
543
+ #
544
+ for b in bad:
545
+ direction = eigvecs[:, b]
546
+ order = numpy.argsort(numpy.abs(direction))[::-1]
530
547
  print(
531
- f" high correlation: kernel dims {a}-{b}, "
532
- f"parameter indices {inferpar[a]}-{inferpar[b]}, "
533
- f"corr={corr[a, b]:g}",
548
+ f" problematic eigenvalue {b}: {eigvals[b]:g}",
534
549
  flush=True
535
550
  )
536
-
537
- return multivariate_normal.logpdf(x_inf, mean=mean_inf, cov=kernel, allow_singular=False)
551
+ print(" strongest dimensions in this direction:", flush=True)
552
+ for k in order[:min(5, len(order))]:
553
+ print(
554
+ f" kernel dim {k}, parameter index {inferpar[k]}, "
555
+ f"loading={direction[k]:g}, "
556
+ f"variance={diag[k]:g}",
557
+ flush=True
558
+ )
559
+ # Regularise eigenvalues, not only diagonal entries.
560
+ eigvals = numpy.maximum(eigvals, eig_floor)
561
+ kernel = eigvecs @ numpy.diag(eigvals) @ eigvecs.T
562
+ kernel = 0.5 * (kernel + kernel.T)
563
+ # Final check by Cholesky. This is stricter and clearer than waiting for SciPy.
564
+ try:
565
+ numpy.linalg.cholesky(kernel)
566
+ except numpy.linalg.LinAlgError as err:
567
+ eigvals_final = numpy.linalg.eigvalsh(kernel)
568
+ raise ValueError(
569
+ f"Kernel is still not positive definite for model {model_id} "
570
+ f"({self.model_names[model_id]}).\n"
571
+ f"parameter indices={inferpar}\n"
572
+ f"x={x_inf}\n"
573
+ f"mean={mean_inf}\n"
574
+ f"diagonal variances={numpy.diag(kernel)}\n"
575
+ f"eigenvalues={eigvals_final}\n"
576
+ f"kernel=\n{kernel}"
577
+ ) from err
578
+ try:
579
+ value = multivariate_normal.logpdf(
580
+ x_inf,
581
+ mean=mean_inf,
582
+ cov=kernel,
583
+ allow_singular=False
584
+ )
585
+ except Exception as err:
586
+ eigvals_final = numpy.linalg.eigvalsh(kernel)
587
+ raise ValueError(
588
+ f"multivariate_normal.logpdf failed for model {model_id} "
589
+ f"({self.model_names[model_id]}).\n"
590
+ f"parameter indices={inferpar}\n"
591
+ f"x={x_inf}\n"
592
+ f"mean={mean_inf}\n"
593
+ f"diagonal variances={numpy.diag(kernel)}\n"
594
+ f"eigenvalues={eigvals_final}\n"
595
+ f"kernel=\n{kernel}"
596
+ ) from err
597
+ if not numpy.isfinite(value):
598
+ eigvals_final = numpy.linalg.eigvalsh(kernel)
599
+ raise ValueError(
600
+ f"Non-finite kernel log density for model {model_id} "
601
+ f"({self.model_names[model_id]}).\n"
602
+ f"logpdf={value}\n"
603
+ f"parameter indices={inferpar}\n"
604
+ f"x={x_inf}\n"
605
+ f"mean={mean_inf}\n"
606
+ f"diagonal variances={numpy.diag(kernel)}\n"
607
+ f"eigenvalues={eigvals_final}\n"
608
+ f"kernel=\n{kernel}"
609
+ )
610
+ return value
538
611
 
539
612
  def calc_weights(self, mat_new, mat_old, kernels):
540
613
  """
File without changes
File without changes
File without changes
File without changes
File without changes