SearchLibrium 0.0.236__tar.gz → 0.0.239__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 (78) hide show
  1. {searchlibrium-0.0.236/src/SearchLibrium.egg-info → searchlibrium-0.0.239}/PKG-INFO +1 -1
  2. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/pyproject.toml +1 -1
  3. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/MixedLogit.py +105 -1
  4. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/_choice_model.py +53 -0
  5. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/ordered_logit.py +45 -3
  6. searchlibrium-0.0.239/src/SearchLibrium/version.txt +1 -0
  7. {searchlibrium-0.0.236 → searchlibrium-0.0.239/src/SearchLibrium.egg-info}/PKG-INFO +1 -1
  8. searchlibrium-0.0.236/src/SearchLibrium/version.txt +0 -1
  9. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/LICENSE +0 -0
  10. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/MANIFEST.in +0 -0
  11. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/README.md +0 -0
  12. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/setup.cfg +0 -0
  13. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/BEHier.py +0 -0
  14. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/Halton.py +0 -0
  15. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/MixedLogitGSE.py +0 -0
  16. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/Mode_Activity_Nested.py +0 -0
  17. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/RandomP.py +0 -0
  18. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/SEARCH_SM_MARIO.py +0 -0
  19. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/Two_Level_Nest.py +0 -0
  20. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/__init__.py +0 -0
  21. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/__main__.py +0 -0
  22. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/_device.py +0 -0
  23. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/abm.py +0 -0
  24. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/accessibility.py +0 -0
  25. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/banditsa.py +0 -0
  26. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/boxcox_functions.py +0 -0
  27. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/call_meta.py +0 -0
  28. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/constraints_builder.py +0 -0
  29. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/data/Swissmetro_final.csv +0 -0
  30. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/data/TravelMode.csv +0 -0
  31. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/data/electricity.csv +0 -0
  32. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/harmony.py +0 -0
  33. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/hspbil.py +0 -0
  34. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/jax_utils.py +0 -0
  35. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/larch_compat.py +0 -0
  36. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/larch_models.py +0 -0
  37. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/latent_class.py +0 -0
  38. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/logistic_regression.py +0 -0
  39. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/main.py +0 -0
  40. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/main_debug.py +0 -0
  41. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/mdcev.py +0 -0
  42. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/misc.py +0 -0
  43. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/mixed_logit.py +0 -0
  44. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/mixed_nested.py +0 -0
  45. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/mixedrrm.py +0 -0
  46. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/multinomial_logit.py +0 -0
  47. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/multinomial_nested.py +0 -0
  48. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/multinomial_probit.py +0 -0
  49. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/numba_engine.py +0 -0
  50. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/numba_mxl.py +0 -0
  51. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/numba_rrm.py +0 -0
  52. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/ordered_logit_mixed.py +0 -0
  53. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/predict.py +0 -0
  54. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/regularization.py +0 -0
  55. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/rrm.py +0 -0
  56. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/sample_data.py +0 -0
  57. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/sapbil.py +0 -0
  58. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/search.py +0 -0
  59. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/selection_models.py +0 -0
  60. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/setup.py +0 -0
  61. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/siman.py +0 -0
  62. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/skims.py +0 -0
  63. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/sparseea_agds.py +0 -0
  64. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/test_lc_de.py +0 -0
  65. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/test_mario_searches.py +0 -0
  66. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/test_sapbil_vs_banditsa.py +0 -0
  67. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/threshold.py +0 -0
  68. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium/zero_inflated_ordered_probit.py +0 -0
  69. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium.egg-info/SOURCES.txt +0 -0
  70. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium.egg-info/dependency_links.txt +0 -0
  71. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium.egg-info/entry_points.txt +0 -0
  72. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium.egg-info/requires.txt +0 -0
  73. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/src/SearchLibrium.egg-info/top_level.txt +0 -0
  74. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/tests/test_jax_mask_equivalence.py +0 -0
  75. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/tests/test_larch_integration.py +0 -0
  76. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/tests/test_lc_smart_init.py +0 -0
  77. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/tests/test_per_alternative_random.py +0 -0
  78. {searchlibrium-0.0.236 → searchlibrium-0.0.239}/tests/test_zero_inflated_ordered_probit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SearchLibrium
3
- Version: 0.0.236
3
+ Version: 0.0.239
4
4
  Summary: Automated specification search for discrete choice models (logit, mixed logit, nested logit, RRM and more) using metaheuristic optimisation
5
5
  Author: Mario Fernando Taco Morales, Alexander Paz, Prithvi Beeramole, Robert Burdett
6
6
  Author-email: Zeke Ahern <z.ahern@qut.edu.au>
@@ -129,7 +129,7 @@ Documentation = "https://pypi.org/project/SearchLibrium/"
129
129
  realpython = "SearchLibrium.__main__:main"
130
130
 
131
131
  [tool.bumpver]
132
- current_version = "0.0.236"
132
+ current_version = "0.0.239"
133
133
  version_pattern = "MAJOR.MINOR.PATCH"
134
134
  commit_message = "[skip ci] Bump version {old_version} -> {new_version}"
135
135
  commit = true
@@ -171,7 +171,7 @@ class MixedLogit(DiscreteChoiceModel):
171
171
  save_fitted_params=True, mnl_init=True,
172
172
  de_init=False, de_popsize=4, de_maxiter=3, de_tol=0.5,
173
173
  de_polish=False, l1_penalty=0.0, reg_penalty=0.001, sd_penalty=0.0,
174
- engine=None):
174
+ engine=None, asc_share_init=True):
175
175
  # {
176
176
  self.fit_intercept = fit_intercept
177
177
  # L2 ridge regularisation strength (default ON). Keeps the Hessian
@@ -182,6 +182,11 @@ class MixedLogit(DiscreteChoiceModel):
182
182
  # Extra L2 penalty applied specifically to the random-coefficient standard
183
183
  # deviations (Br_w). Shrinks weakly-identified / runaway SDs toward zero.
184
184
  self.sd_penalty = float(sd_penalty)
185
+ # Seed alternative-specific constants from observed shares
186
+ # (alpha_j = log(s_j / s_base); detected as exact alternative
187
+ # dummies, no naming convention needed). Disable with
188
+ # asc_share_init=False.
189
+ self.asc_share_init = bool(asc_share_init)
185
190
  # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
186
191
  # RECAST AS NUMPY NDARRAY
187
192
  # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -613,11 +618,98 @@ class MixedLogit(DiscreteChoiceModel):
613
618
  ''' Function. Fit Mixed Logit model '''
614
619
  ''' ---------------------------------------------------------- '''
615
620
 
621
+ def _seed_asc_from_shares(self, betas):
622
+ """Overwrite ASC fixed/random-mean starts with log-share ratios.
623
+
624
+ For alternative dummies the plain-logit MLE is closed form:
625
+ ``alpha_j = log(s_j / s_base)`` (the BLP-contraction starting
626
+ point; exact for intercepts-only, excellent with covariates).
627
+ ASC columns are detected from the design (columns exactly
628
+ indicating one alternative), so no naming convention is needed.
629
+ Positions without positive shares on both sides keep their
630
+ incoming value. Returns ``(betas, n_seeded)``; never raises.
631
+ """
632
+ try:
633
+ import numpy as _np
634
+ betas = _np.asarray(betas, dtype=float).copy()
635
+ Kf, Kr = int(self.Kf), int(self.Kr)
636
+ if Kf + Kr <= 0:
637
+ return betas, 0
638
+ X = _np.asarray(self.X, dtype=float)
639
+ if X.ndim != 4:
640
+ return betas, 0
641
+ N, P, J = X.shape[0], X.shape[1], X.shape[2]
642
+ if J < 2:
643
+ return betas, 0
644
+ # observed shares over valid choice situations
645
+ y = _np.asarray(self.y)
646
+ if y.ndim == 4:
647
+ y = y[..., 0]
648
+ if y.ndim == 3 and y.shape == (N, P, J):
649
+ _flat = y.reshape(N * P, J)
650
+ cnt = _flat.sum(axis=0)
651
+ den = float((_flat.sum(axis=1) > 0).sum())
652
+ elif y.ndim <= 2:
653
+ idx = y.astype(int).ravel()
654
+ idx = idx[(idx >= 0) & (idx < J)]
655
+ if idx.size == 0:
656
+ return betas, 0
657
+ cnt = _np.bincount(idx, minlength=J).astype(float)
658
+ den = float(idx.size)
659
+ else:
660
+ return betas, 0
661
+ if not den > 0:
662
+ return betas, 0
663
+ shares = cnt / den
664
+ try:
665
+ base = int(self.base_alt)
666
+ except Exception:
667
+ base = 0
668
+ if not (0 <= base < J) or not shares[base] > 0:
669
+ return betas, 0
670
+ try:
671
+ fx = _np.where(_np.asarray(self.fxidx, dtype=bool))[0]
672
+ rv = _np.where(_np.asarray(self.rvidx, dtype=bool))[0]
673
+ except Exception:
674
+ return betas, 0
675
+ if len(fx) < Kf or len(rv) < Kr:
676
+ return betas, 0
677
+ alt1 = _np.arange(J)
678
+ n_seed = 0
679
+ for f in range(Kf): # fixed means <-> design column fx[f]
680
+ col = X[:, :, :, int(fx[f])]
681
+ for j in range(J):
682
+ if bool((col == (alt1 == j)).all()):
683
+ if shares[j] > 0:
684
+ betas[f] = float(_np.log(shares[j] / shares[base]))
685
+ n_seed += 1
686
+ break
687
+ for k in range(Kr): # random means <-> design column rv[k]
688
+ col = X[:, :, :, int(rv[k])]
689
+ for j in range(J):
690
+ if bool((col == (alt1 == j)).all()):
691
+ if shares[j] > 0:
692
+ betas[Kf + k] = float(
693
+ _np.log(shares[j] / shares[base]))
694
+ n_seed += 1
695
+ break
696
+ return betas, n_seed
697
+ except Exception:
698
+ try:
699
+ import numpy as _np
700
+ return _np.asarray(betas, dtype=float), 0
701
+ except Exception:
702
+ return betas, 0
703
+
616
704
  def fit(self):
617
705
  # {
618
706
  # Generate draws:
619
707
  draws, drawstrans = self.generate_draws(self.N, self.n_draws, self.halton)
620
708
  self.draws, self.drawstrans = draws, drawstrans # Record generated values
709
+ # Explicit caller-supplied starts are respected verbatim (no ASC
710
+ # share seeding); the MNL block below may still overwrite
711
+ # self.init_coeff when it runs.
712
+ _explicit_init = self.init_coeff is not None
621
713
 
622
714
  # Optional numba engine (first-class; the T4 minimise_func patch is
623
715
  # the equivalent for older installs). Resolved here so the JAX fast
@@ -692,6 +784,18 @@ class MixedLogit(DiscreteChoiceModel):
692
784
  self.init_coeff = None
693
785
  betas = np.repeat(0.1, n_coeff)
694
786
 
787
+ # Log-share seeding for alternative-specific constants detected as
788
+ # exact alternative dummies: alpha_j = log(s_j / s_base). Cheap
789
+ # closed form; skipped for explicitly user-supplied starts.
790
+ if getattr(self, 'asc_share_init', True) and not _explicit_init:
791
+ try:
792
+ betas, _n_asc = self._seed_asc_from_shares(betas)
793
+ if _n_asc and not _SL_QUIET:
794
+ print(f"[MXL] ASC share-init seeded {_n_asc} constants "
795
+ f"(log-share ratios).")
796
+ except Exception:
797
+ pass
798
+
695
799
  positive_bound = (0, infinity)
696
800
  any_bound = (-infinity, infinity)
697
801
  lmda_bound = (-5, 1)
@@ -495,6 +495,59 @@ class DiscreteChoiceModel(ABC):
495
495
 
496
496
  # }
497
497
 
498
+ def cov_params(self, robust=False):
499
+ """Parameter covariance matrix aligned to ``coeff_names``.
500
+
501
+ Mediation/path analysis (e.g. SEM-style direct + indirect effects)
502
+ needs off-diagonal covariances, not just standard errors. Returns a
503
+ ``pandas.DataFrame`` (p x p) with ``coeff_names`` as index/columns.
504
+
505
+ Parameters
506
+ ----------
507
+ robust : bool, default False
508
+ If True, prefer the sandwich ``robust_varcov`` when present and
509
+ shape-consistent; otherwise fall back to the inverse Hessian.
510
+
511
+ Notes
512
+ -----
513
+ Uses ``self.robust_varcov`` (if ``robust=True``) else ``self.Hinv``.
514
+ Both are stored by ``fit()``/``post_process()``. Raises ValueError
515
+ when no p x p finite covariance is available (e.g. the model was fit
516
+ with ``return_hess=False``).
517
+ """
518
+ import numpy as _np
519
+ import pandas as _pd
520
+ est = _np.asarray(getattr(self, 'coeff_est', []), dtype=float).ravel()
521
+ p = int(est.size)
522
+ if p == 0:
523
+ raise ValueError("cov_params: model has no estimated coefficients "
524
+ "(was fit() called?)")
525
+ _V = None
526
+ if robust:
527
+ _V = getattr(self, 'robust_varcov', None)
528
+ if _V is None:
529
+ _V = getattr(self, 'Hinv', None)
530
+ if _V is None:
531
+ raise ValueError("cov_params: no covariance available "
532
+ "(missing Hinv/robust_varcov — refit with "
533
+ "return_hess=True)")
534
+ try:
535
+ _V = _np.asarray(_V, dtype=float)
536
+ except Exception as _e:
537
+ raise ValueError(f"cov_params: stored covariance not array-like "
538
+ f"({_e!r})")
539
+ if _V.shape != (p, p) or not _np.all(_np.isfinite(_V)):
540
+ raise ValueError(f"cov_params: stored covariance has shape "
541
+ f"{tuple(_V.shape)}, expected ({p}, {p}) with "
542
+ f"finite entries")
543
+ _raw_names = getattr(self, 'coeff_names', None)
544
+ _names = (list(_raw_names) if _raw_names is not None else [])
545
+ if len(_names) != p:
546
+ _names = [str(_n) for _n in _names[:p]]
547
+ _names += [f"param_{i}" for i in range(len(_names), p)]
548
+ _V = (_V + _V.T) / 2.0 # enforce symmetry against float noise
549
+ return _pd.DataFrame(_V, index=_names, columns=_names)
550
+
498
551
  def num_of_exceeding_pvalues(self, pvalues, threshold):
499
552
  """
500
553
  :param pvalues: array of pvalues
@@ -212,7 +212,7 @@ class OrderedLogit():
212
212
  self.J = kwargs.get('J') # Number of ordinal categories => categories = {0, 1, ..., J-1}
213
213
 
214
214
 
215
- self.fit_intercept = kwargs.get('fit_intercept')
215
+ self.fit_intercept = kwargs.get('fit_intercept', False)
216
216
  self.nparams = self.K + self.J -1 +int(self.fit_intercept) # i.e., intercept + self.K + self.J - 1
217
217
 
218
218
  self.params = kwargs.get('start')
@@ -244,7 +244,7 @@ class OrderedLogit():
244
244
  else:
245
245
  self.distr = distr
246
246
 
247
- self.fit_intercept = kwargs.get('fit_intercept') # Add intercept
247
+ self.fit_intercept = kwargs.get('fit_intercept', False) # Add intercept
248
248
  self.define_labels()
249
249
  # }
250
250
 
@@ -282,7 +282,10 @@ class OrderedLogit():
282
282
  # {
283
283
  #y_latent = X.dot(beta[1:]) # Compute dot product, i.e., X.beta
284
284
  if(self.fit_intercept):
285
- y_latent = beta[0] # Add beta[0] to each y_latent value, i.e. y_latent[n] += beta_0 for n=1,...,N
285
+ # params layout is [intercept, K slopes, ...]: broadcast the
286
+ # intercept across cases (a bare beta[0] scalar here silently
287
+ # dropped every slope and broke all downstream shapes).
288
+ y_latent = beta[0] + X.dot(beta[1:1 + self.K])
286
289
  else:
287
290
  y_latent=X.dot(beta)
288
291
  return y_latent
@@ -559,6 +562,12 @@ class OrderedLogit():
559
562
  # {
560
563
  hessian = self.get_hessian(tol)
561
564
  inverse = self.np.linalg.pinv(hessian) # Conventional approach
565
+ # Keep the full covariance for path/mediation analysis (delta-method
566
+ # SEs of indirect effects need off-diagonal terms, not just stderr).
567
+ try:
568
+ self.varcov = np.asarray(inverse, dtype=float)
569
+ except Exception:
570
+ self.varcov = None
562
571
  diag = self.np.diagonal(inverse)
563
572
  # jax arrays (self.np when _jax=True) don't support boolean-mask
564
573
  # in-place assignment; clip() returns a new array either way.
@@ -573,6 +582,39 @@ class OrderedLogit():
573
582
  self.stderr = self.np.sqrt(diag_copy)
574
583
  # }
575
584
 
585
+ def cov_params(self, robust=False):
586
+ """Parameter covariance matrix aligned to ``labels``.
587
+
588
+ Mirrors ``DiscreteChoiceModel.cov_params`` (see _choice_model.py) so
589
+ mediation/path analysis can use off-diagonal terms. Ordered logit
590
+ has no sandwich variant, so ``robust`` is accepted and ignored.
591
+ """
592
+ import pandas as _pd
593
+ _est = np.asarray(getattr(self, 'params', []), dtype=float).ravel()
594
+ _p = int(_est.size)
595
+ if _p == 0:
596
+ raise ValueError("cov_params: model has no estimated parameters "
597
+ "(was fit() called?)")
598
+ _V = getattr(self, 'varcov', None)
599
+ if _V is None:
600
+ raise ValueError("cov_params: no covariance available "
601
+ "(missing varcov — refit so compute_stderr runs)")
602
+ try:
603
+ _V = np.asarray(_V, dtype=float)
604
+ except Exception as _e:
605
+ raise ValueError(f"cov_params: stored covariance not array-like "
606
+ f"({_e!r})")
607
+ if _V.shape != (_p, _p) or not np.all(np.isfinite(_V)):
608
+ raise ValueError(f"cov_params: stored covariance has shape "
609
+ f"{tuple(_V.shape)}, expected ({_p}, {_p}) with "
610
+ f"finite entries")
611
+ _names = list(getattr(self, 'labels', []) or [])
612
+ if len(_names) != _p:
613
+ _names = [str(_n) for _n in _names[:_p]]
614
+ _names += [f"param_{i}" for i in range(len(_names), _p)]
615
+ _V = (_V + _V.T) / 2.0
616
+ return _pd.DataFrame(_V, index=_names, columns=_names)
617
+
576
618
  ''' ---------------------------------------------------------- '''
577
619
  ''' Function. '''
578
620
  ''' ---------------------------------------------------------- '''
@@ -0,0 +1 @@
1
+ 0.0.239
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SearchLibrium
3
- Version: 0.0.236
3
+ Version: 0.0.239
4
4
  Summary: Automated specification search for discrete choice models (logit, mixed logit, nested logit, RRM and more) using metaheuristic optimisation
5
5
  Author: Mario Fernando Taco Morales, Alexander Paz, Prithvi Beeramole, Robert Burdett
6
6
  Author-email: Zeke Ahern <z.ahern@qut.edu.au>
@@ -1 +0,0 @@
1
- 0.0.236
File without changes