SearchLibrium 0.0.134__tar.gz → 0.0.136__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.

Potentially problematic release.


This version of SearchLibrium might be problematic. Click here for more details.

Files changed (53) hide show
  1. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/PKG-INFO +1 -1
  2. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/pyproject.toml +1 -1
  3. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/MixedLogit.py +23 -0
  4. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/latent_class.py +1 -1
  5. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/mixed_logit.py +6 -0
  6. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/search.py +69 -0
  7. searchlibrium-0.0.136/src/SearchLibrium/version.txt +1 -0
  8. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium.egg-info/PKG-INFO +1 -1
  9. searchlibrium-0.0.134/src/SearchLibrium/version.txt +0 -1
  10. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/README.md +0 -0
  11. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/setup.cfg +0 -0
  12. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/Halton.py +0 -0
  13. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/Mode_Activity_Nested.py +0 -0
  14. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/RandomP.py +0 -0
  15. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/SEARCH_SM_MARIO.py +0 -0
  16. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/Two_Level_Nest.py +0 -0
  17. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/__init__.py +0 -0
  18. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/__main__.py +0 -0
  19. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/_choice_model.py +0 -0
  20. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/_device.py +0 -0
  21. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/banditsa.py +0 -0
  22. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/bhhh/minimize.py +0 -0
  23. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/boxcox_functions.py +0 -0
  24. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/call_meta.py +0 -0
  25. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/constraints_builder.py +0 -0
  26. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/harmony.py +0 -0
  27. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/hspbil.py +0 -0
  28. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/main.py +0 -0
  29. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/main_debug.py +0 -0
  30. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/mdcev.py +0 -0
  31. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/misc.py +0 -0
  32. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/mixed_nested.py +0 -0
  33. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/mixedrrm.py +0 -0
  34. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/multinomial_logit.py +0 -0
  35. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/multinomial_nested.py +0 -0
  36. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/multinomial_probit.py +0 -0
  37. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/ordered_logit.py +0 -0
  38. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/ordered_logit_mixed.py +0 -0
  39. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/predict.py +0 -0
  40. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/rrm.py +0 -0
  41. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/sapbil.py +0 -0
  42. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/selection_models.py +0 -0
  43. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/setup.py +0 -0
  44. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/siman.py +0 -0
  45. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/test_lc_de.py +0 -0
  46. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/test_mario_searches.py +0 -0
  47. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/test_sapbil_vs_banditsa.py +0 -0
  48. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium/threshold.py +0 -0
  49. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium.egg-info/SOURCES.txt +0 -0
  50. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium.egg-info/dependency_links.txt +0 -0
  51. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium.egg-info/entry_points.txt +0 -0
  52. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium.egg-info/requires.txt +0 -0
  53. {searchlibrium-0.0.134 → searchlibrium-0.0.136}/src/SearchLibrium.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SearchLibrium
3
- Version: 0.0.134
3
+ Version: 0.0.136
4
4
  Summary: A Python package for econometric models driven by search
5
5
  Author: Mario Fernando Taco Morales, Alexander Paz, Prithvi Beeramole, Robert Burdett
6
6
  Author-email: Zeke Ahern <z.ahern@qut.edu.au>
@@ -73,7 +73,7 @@ Documentation = "https://pypi.org/project/SearchLibrium/"
73
73
  realpython = "SearchLibrium.__main__:main"
74
74
 
75
75
  [tool.bumpver]
76
- current_version = "0.0.134"
76
+ current_version = "0.0.136"
77
77
  version_pattern = "MAJOR.MINOR.PATCH"
78
78
  commit_message = "[skip ci] Bump version {old_version} -> {new_version}"
79
79
  commit = true
@@ -597,6 +597,14 @@ class MixedLogit(DiscreteChoiceModel):
597
597
  H = self.get_hessian(result['x'], self._get_loglik_gradient)
598
598
  result['hess_inv'] = np.linalg.inv(H)
599
599
  # }
600
+ else:
601
+ # ── Non-L-BFGS-B path (e.g. JAX): compute Hessian numerically
602
+ # so post_process can extract standard errors
603
+ try:
604
+ H = self.get_hessian(result['x'], self._get_loglik_gradient, eps=1e-6)
605
+ result['hess_inv'] = np.linalg.inv(H)
606
+ except Exception:
607
+ pass
600
608
 
601
609
  if self.save_fitted_params:
602
610
  self.compute_fitted_params(self.y, self.p, self.panel_info, self.Br)
@@ -796,6 +804,21 @@ class MixedLogit(DiscreteChoiceModel):
796
804
 
797
805
  result = sp_min(
798
806
  _obj, betas, jac=True, method=self.method, options=opts)
807
+
808
+ # ── JAX autograd Hessian for standard errors ───────────────
809
+ try:
810
+ hess_cache_key = _cache_key + ('hess',)
811
+ _compiled_hess = self._mxl_jit_cache.get(hess_cache_key)
812
+ if _compiled_hess is None:
813
+ _compiled_hess = jax.jit(jax.hessian(_fn))
814
+ self._mxl_jit_cache[hess_cache_key] = _compiled_hess
815
+ b_opt = jnp.array(result['x'], dtype=jnp.float64)
816
+ H = _compiled_hess(b_opt, X_jax, y_jax, pi_jax, draws_jax)
817
+ H_np = np.asarray(H, dtype=float)
818
+ result['hess_inv'] = np.linalg.inv(H_np)
819
+ except Exception:
820
+ pass
821
+
799
822
  return result
800
823
 
801
824
  except Exception as e:
@@ -42,7 +42,7 @@ class LatentClassMixedLogit:
42
42
  optimise_membership=True,
43
43
  membership_maxiter=50,
44
44
  l1_penalty=0.0,
45
- l2_penalty=0.0,
45
+ l2_penalty=0.1,
46
46
  ):
47
47
  self.n_classes = int(n_classes)
48
48
  self.maxiter = int(maxiter)
@@ -619,6 +619,12 @@ class MixedLogit(DiscreteChoiceModel):
619
619
  H = get_hessian(result['x'], self._get_loglik_gradient)
620
620
  result['hess_inv'] = np.linalg.inv(H)
621
621
  # }
622
+ else:
623
+ try:
624
+ H = get_hessian(result['x'], self._get_loglik_gradient, eps=1e-6)
625
+ result['hess_inv'] = np.linalg.inv(H)
626
+ except Exception:
627
+ pass
622
628
 
623
629
  if self.save_fitted_params:
624
630
  self.compute_fitted_params(self.y, self.p, self.panel_info, self.Br)
@@ -684,6 +684,10 @@ class Parameters:
684
684
  self.de_tol = de_tol
685
685
  self.de_polish = de_polish
686
686
 
687
+ # ── Regularisation (primarily for latent class) ──────────────
688
+ self.l1_penalty = kwargs.get('l1_penalty', 0.0)
689
+ self.l2_penalty = kwargs.get('l2_penalty', 0.1)
690
+
687
691
  self.intercept_opts = intercept_opts
688
692
  self.base_alt = base_alt
689
693
  self.val_share = val_share
@@ -4013,6 +4017,8 @@ class Search():
4013
4017
  random_state=seed if seed is not None else 0,
4014
4018
  optimise_membership=optimise_membership,
4015
4019
  membership_maxiter=100,
4020
+ l1_penalty=getattr(self.param, 'l1_penalty', 0.0),
4021
+ l2_penalty=getattr(self.param, 'l2_penalty', 0.0),
4016
4022
  )
4017
4023
 
4018
4024
  membership_vars = None
@@ -4216,6 +4222,65 @@ class Search():
4216
4222
  # }
4217
4223
 
4218
4224
 
4225
+ ''' ---------------------------------------------------------- '''
4226
+ ''' Function. Fit and Evaluate Latent Class Model '''
4227
+ ''' ---------------------------------------------------------- '''
4228
+
4229
+ def evaluate_lc(self, sol):
4230
+ # {
4231
+ sol = self.apply_constraints(sol)
4232
+ as_vars = sol.get('asvars', [])
4233
+ is_vars = sol.get('isvars', [])
4234
+ asc_ind = sol.get('asc_ind', False)
4235
+ bc_vars = self.define_bc_vars(sol)
4236
+ all_vars = [var for var in self.param.varnames if var in (as_vars + is_vars)]
4237
+
4238
+ class_params_spec = sol.get('class_params_spec', None)
4239
+ member_params_spec = sol.get('member_params_spec', None)
4240
+ num_classes = getattr(self.param, 'num_classes', 2)
4241
+
4242
+ X = self.param.df[all_vars].values
4243
+ y = self.param.choices
4244
+ ids = self.param.choice_id if self.param.choice_id is not None else self.param.ind_id
4245
+
4246
+ alts = self.param.alt_var
4247
+ if alts is None:
4248
+ alts = np.ones(len(y), dtype=int)
4249
+
4250
+ model = self.fit_lcm(
4251
+ X=X, y=y, varnames=all_vars,
4252
+ class_params_spec=class_params_spec,
4253
+ member_params_spec=member_params_spec,
4254
+ num_classes=num_classes,
4255
+ ids=ids,
4256
+ transvars=bc_vars,
4257
+ maxiter=self.param.maxiter,
4258
+ gtol=self.param.gtol,
4259
+ avail=self.param.avail,
4260
+ weights=self.param.weights,
4261
+ alts=alts,
4262
+ base_alt=self.param.base_alt,
4263
+ )
4264
+
4265
+ sol['model'] = model
4266
+ sol['coeff'] = model.coeff_est
4267
+ sol['model_n'] = 'latent_class'
4268
+ converged = model.converged
4269
+
4270
+ aic = getattr(model, 'aic', float('inf'))
4271
+ bic = getattr(model, 'bic', float('inf'))
4272
+ loglik = getattr(model, 'loglik', float('-inf'))
4273
+
4274
+ mae = float('inf')
4275
+
4276
+ if getattr(self.param, 'verbose', False):
4277
+ model.summarise()
4278
+
4279
+ tuple_ = (aic, bic, loglik, mae, as_vars, is_vars, {}, bc_vars, [], converged, sol)
4280
+ return tuple_
4281
+ # }
4282
+
4283
+
4219
4284
  ''' ---------------------------------------------------------- '''
4220
4285
  ''' Function. Fit and Evaluate Nested Logit Model '''
4221
4286
  ''' ---------------------------------------------------------- '''
@@ -4582,6 +4647,10 @@ class Search():
4582
4647
  ''' ---------------------------------------------------------- '''
4583
4648
  def evaluate_model(self, sol):
4584
4649
  # {
4650
+ # ── Latent class override: if param says latent, do latent ──
4651
+ if getattr(self.param, 'latent_class', False):
4652
+ return self.evaluate_lc(sol)
4653
+
4585
4654
  model_n = sol.get('model_n', '')
4586
4655
 
4587
4656
  # ── Pre-fit collinearity / prerequisite check ────────────────
@@ -0,0 +1 @@
1
+ 0.0.136
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SearchLibrium
3
- Version: 0.0.134
3
+ Version: 0.0.136
4
4
  Summary: A Python package for econometric models driven by search
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.134