SearchLibrium 0.0.138__tar.gz → 0.0.140__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.
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/PKG-INFO +1 -1
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/pyproject.toml +1 -1
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/MixedLogit.py +62 -6
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/_choice_model.py +2 -2
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/latent_class.py +31 -9
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/mixed_logit.py +2 -2
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/mixedrrm.py +1 -1
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/multinomial_logit.py +1 -1
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/search.py +37 -5
- searchlibrium-0.0.140/src/SearchLibrium/version.txt +1 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium.egg-info/PKG-INFO +1 -1
- searchlibrium-0.0.138/src/SearchLibrium/version.txt +0 -1
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/README.md +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/setup.cfg +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/Halton.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/Mode_Activity_Nested.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/RandomP.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/SEARCH_SM_MARIO.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/Two_Level_Nest.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/__init__.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/__main__.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/_device.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/banditsa.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/bhhh/minimize.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/boxcox_functions.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/call_meta.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/constraints_builder.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/harmony.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/hspbil.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/main.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/main_debug.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/mdcev.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/misc.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/mixed_nested.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/multinomial_nested.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/multinomial_probit.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/ordered_logit.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/ordered_logit_mixed.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/predict.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/rrm.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/sapbil.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/selection_models.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/setup.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/siman.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/test_lc_de.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/test_mario_searches.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/test_sapbil_vs_banditsa.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/threshold.py +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium.egg-info/SOURCES.txt +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium.egg-info/dependency_links.txt +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium.egg-info/entry_points.txt +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium.egg-info/requires.txt +0 -0
- {searchlibrium-0.0.138 → searchlibrium-0.0.140}/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.
|
|
3
|
+
Version: 0.0.140
|
|
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.
|
|
76
|
+
current_version = "0.0.140"
|
|
77
77
|
version_pattern = "MAJOR.MINOR.PATCH"
|
|
78
78
|
commit_message = "[skip ci] Bump version {old_version} -> {new_version}"
|
|
79
79
|
commit = true
|
|
@@ -134,13 +134,14 @@ class MixedLogit(DiscreteChoiceModel):
|
|
|
134
134
|
def setup(self, X, y, varnames=None, alts=None, isvars=None, transvars=None,
|
|
135
135
|
transformation="boxcox", ids=None, weights=None, avail=None,
|
|
136
136
|
randvars=None, panels=None, base_alt=None, fit_intercept=False,
|
|
137
|
-
init_coeff=None, maxiter=
|
|
137
|
+
init_coeff=None, maxiter=2000, correlated_vars=None,
|
|
138
138
|
n_draws=1000, halton=True, minimise_func=None,
|
|
139
|
-
batch_size=None, halton_opts=None, ftol=1e-
|
|
139
|
+
batch_size=None, halton_opts=None, ftol=1e-6,
|
|
140
140
|
gtol=1e-6, return_hess=True, return_grad=True, method="slsqp",
|
|
141
141
|
save_fitted_params=True, mnl_init=True,
|
|
142
142
|
de_init=False, de_popsize=4, de_maxiter=3, de_tol=0.5,
|
|
143
|
-
de_polish=False, l1_penalty=0.0
|
|
143
|
+
de_polish=False, l1_penalty=0.1, l2_penalty=0.5,
|
|
144
|
+
sd_penalty=0.001):
|
|
144
145
|
# {
|
|
145
146
|
self.fit_intercept = fit_intercept
|
|
146
147
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
@@ -186,6 +187,8 @@ class MixedLogit(DiscreteChoiceModel):
|
|
|
186
187
|
self.total_fun_eval = 0
|
|
187
188
|
self.method = method.lower() if hasattr(method, 'lower') else method
|
|
188
189
|
self.l1_penalty = float(l1_penalty)
|
|
190
|
+
self.l2_penalty = float(l2_penalty)
|
|
191
|
+
self.sd_penalty = float(sd_penalty)
|
|
189
192
|
self.jac = self.return_grad # scipy optimize parameter
|
|
190
193
|
self.n_draws = n_draws
|
|
191
194
|
self.batch_size = min(n_draws, batch_size) if batch_size is not None else n_draws
|
|
@@ -586,11 +589,64 @@ class MixedLogit(DiscreteChoiceModel):
|
|
|
586
589
|
args = (self.X, self.y, self.panel_info, draws, drawstrans, self.weights, self.avail, self.batch_size)
|
|
587
590
|
bounds = bnds if self.method == "L-BFGS-B" else None
|
|
588
591
|
options = {'gtol': self.gtol, 'maxiter': self.maxiter, 'disp': False}
|
|
589
|
-
|
|
592
|
+
|
|
593
|
+
# ── SD penalty wrapper: regularise SD params away from exactly zero ──
|
|
594
|
+
# When an SD parameter hits zero, the gradient vanishes and BFGS gets stuck.
|
|
595
|
+
# A small L2 penalty that activates below a threshold keeps SDs bounded
|
|
596
|
+
# away from zero without affecting the well-identified estimates.
|
|
597
|
+
if self.sd_penalty > 0 and self.Kbw > 0:
|
|
598
|
+
# SD param indices: end of beta vector, before Kftrans and Krtrans
|
|
599
|
+
sd_start = self.Kf + self.Kr + self.Kchol
|
|
600
|
+
sd_end = sd_start + self.Kbw
|
|
601
|
+
_orig_obj = self.get_loglik_gradient
|
|
602
|
+
|
|
603
|
+
def _penalised_obj(betas, *obj_args):
|
|
604
|
+
f, g = _orig_obj(betas, *obj_args)
|
|
605
|
+
for i in range(sd_start, sd_end):
|
|
606
|
+
if i >= len(betas):
|
|
607
|
+
break
|
|
608
|
+
sd_val = betas[i]
|
|
609
|
+
if sd_val < 0.05:
|
|
610
|
+
p = self.sd_penalty * (sd_val - 0.05) ** 2
|
|
611
|
+
f += p
|
|
612
|
+
if g is not None and len(g) > i:
|
|
613
|
+
g[i] += 2.0 * self.sd_penalty * (sd_val - 0.05)
|
|
614
|
+
return f, g
|
|
615
|
+
|
|
616
|
+
obj_fn = _penalised_obj
|
|
617
|
+
else:
|
|
618
|
+
obj_fn = self.get_loglik_gradient
|
|
619
|
+
|
|
620
|
+
result = minimise_func(obj_fn, betas, jac=self.jac, method=self.method,
|
|
590
621
|
args=args, tol=self.ftol, bounds=bounds, options=options)
|
|
591
|
-
print(f"[MXL]
|
|
622
|
+
print(f"[MXL] Primary minimization ({self.method}): success={result.get('success', None)}, fun={result.get('fun', float('nan')):.6g}, nit={result.get('nit', '?')}")
|
|
592
623
|
if 'x' in result:
|
|
593
|
-
print(f"[MXL]
|
|
624
|
+
print(f"[MXL] Primary betas first_values={np.asarray(result['x'])[:min(8, len(result['x']))]!r}")
|
|
625
|
+
|
|
626
|
+
# ── BFGS polish: after non-BFGS methods (SLSQP, Powell, etc.),
|
|
627
|
+
# run a quick BFGS to get the inverse Hessian for standard errors.
|
|
628
|
+
if (self.method not in ('bfgs', 'l-bfgs-b')
|
|
629
|
+
and 'x' in result
|
|
630
|
+
and result.get('success', False)):
|
|
631
|
+
try:
|
|
632
|
+
polish_opts = {'gtol': self.gtol * 10, 'maxiter': min(self.maxiter // 5, 200), 'disp': False}
|
|
633
|
+
polish_bnds = bnds # re-use same bounds
|
|
634
|
+
# Use original objective (no SD penalty) for clean Hessian
|
|
635
|
+
polish_result = minimise_func(
|
|
636
|
+
self.get_loglik_gradient,
|
|
637
|
+
np.asarray(result['x']).copy(),
|
|
638
|
+
jac=True, method='L-BFGS-B',
|
|
639
|
+
args=args, tol=self.ftol,
|
|
640
|
+
bounds=polish_bnds, options=polish_opts,
|
|
641
|
+
)
|
|
642
|
+
if polish_result.get('fun', float('inf')) < result.get('fun', float('inf')) + 0.1:
|
|
643
|
+
print(f"[MXL] BFGS polish: success={polish_result.get('success')}, "
|
|
644
|
+
f"fun={polish_result.get('fun', float('nan')):.6g}")
|
|
645
|
+
result = polish_result
|
|
646
|
+
else:
|
|
647
|
+
print(f"[MXL] BFGS polish skipped (worse objective)")
|
|
648
|
+
except Exception as e:
|
|
649
|
+
print(f"[MXL] BFGS polish failed: {e}")
|
|
594
650
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
595
651
|
|
|
596
652
|
if hasattr(self, 'method') and self.method == "L-BFGS-B": # {
|
|
@@ -147,7 +147,7 @@ class DiscreteChoiceModel(ABC):
|
|
|
147
147
|
self.reset_attributes()
|
|
148
148
|
self.fit_intercept = False
|
|
149
149
|
self.reg_penalty = 0.00 # L2 penalty strength (ridge)
|
|
150
|
-
self.l1_penalty = 0.
|
|
150
|
+
self.l1_penalty = 0.1 # L1 penalty strength (lasso)
|
|
151
151
|
self.pval_penalty = 5
|
|
152
152
|
logging.info(f'pval penalty set to {self.pval_penalty}')
|
|
153
153
|
# NOTE: The reg_penalty value is tricky to define. If too high, convergence is restricted.
|
|
@@ -167,7 +167,7 @@ class DiscreteChoiceModel(ABC):
|
|
|
167
167
|
self.converged = False
|
|
168
168
|
self.return_grad, self.return_hess, self.fit_intercept = True, True, False
|
|
169
169
|
self.scipy_optimisation = True
|
|
170
|
-
self.method, self.transformation = "
|
|
170
|
+
self.method, self.transformation = "slsqp", "boxcox"
|
|
171
171
|
|
|
172
172
|
self.trans_func = None # NEW. CHECK VALIDITY!
|
|
173
173
|
self.varnames, self.isvars, self.transvars = None, None, None
|
|
@@ -57,6 +57,10 @@ class LatentClassMixedLogit:
|
|
|
57
57
|
self.descr = "LC-MXL"
|
|
58
58
|
self.coeff_est = None
|
|
59
59
|
self.coeff_names = None
|
|
60
|
+
self.stderr = None
|
|
61
|
+
self.zvalues = None
|
|
62
|
+
self.pvalues = None
|
|
63
|
+
self.pvalues_member = None
|
|
60
64
|
self.class_betas = None
|
|
61
65
|
self.class_probs = None
|
|
62
66
|
self.class_gammas = None
|
|
@@ -1454,16 +1458,24 @@ class LatentClassMixedLogit:
|
|
|
1454
1458
|
params = np.concatenate([phi_vals, beta_flat])
|
|
1455
1459
|
n_gamma = 0
|
|
1456
1460
|
|
|
1457
|
-
# ── JAX autograd Hessian → covariance (
|
|
1461
|
+
# ── JAX autograd Hessian → covariance (numerical fallback) ──
|
|
1458
1462
|
H_a = self._autograd_hessian(params)
|
|
1459
|
-
if H_a is None
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1463
|
+
if H_a is not None and np.isfinite(H_a).all():
|
|
1464
|
+
info = H_a # hessian(-negloglik) = -hessian(loglik) = observed info
|
|
1465
|
+
se_method = "autograd-hessian"
|
|
1466
|
+
else:
|
|
1467
|
+
# JAX unavailable (or classes with differing variable sets):
|
|
1468
|
+
# fall back to a finite-difference Hessian so searches always
|
|
1469
|
+
# get standard errors and p-values.
|
|
1470
|
+
H_num = self._numerical_hessian(params, eps=eps)
|
|
1471
|
+
if not np.isfinite(H_num).all():
|
|
1472
|
+
raise RuntimeError(
|
|
1473
|
+
"Hessian could not be computed (autograd unavailable and "
|
|
1474
|
+
"finite-difference Hessian is non-finite) — standard errors "
|
|
1475
|
+
"are not available for this model."
|
|
1476
|
+
)
|
|
1477
|
+
info = -H_num # observed info = -hessian(loglik)
|
|
1478
|
+
se_method = "numerical-hessian (finite differences)"
|
|
1467
1479
|
|
|
1468
1480
|
cond_number = np.nan
|
|
1469
1481
|
cov = None
|
|
@@ -1592,6 +1604,16 @@ class LatentClassMixedLogit:
|
|
|
1592
1604
|
gamma_names.append(f"gamma_class_{c + 1}_{v}")
|
|
1593
1605
|
param_names += gamma_names
|
|
1594
1606
|
|
|
1607
|
+
# ── Standard attributes aligned with coeff_est / coeff_names ──────
|
|
1608
|
+
# The specification search reads model.stderr / model.pvalues to drive
|
|
1609
|
+
# significance-based refinement and PBIL probability updates, so the
|
|
1610
|
+
# class-beta block (which is what coeff_est holds) must be exposed here.
|
|
1611
|
+
n_beta = beta_flat.size
|
|
1612
|
+
self.stderr = se[n_phi:n_phi + n_beta]
|
|
1613
|
+
self.zvalues = t_stats[n_phi:n_phi + n_beta]
|
|
1614
|
+
self.pvalues = p_values[n_phi:n_phi + n_beta]
|
|
1615
|
+
self.pvalues_member = gamma_p_values
|
|
1616
|
+
|
|
1595
1617
|
return {
|
|
1596
1618
|
"params": params,
|
|
1597
1619
|
"se": se,
|
|
@@ -261,9 +261,9 @@ class MixedLogit(DiscreteChoiceModel):
|
|
|
261
261
|
init_coeff=None, maxiter=2000, correlated_vars=None,
|
|
262
262
|
n_draws=1000, halton=True, minimise_func=None,
|
|
263
263
|
batch_size=None, halton_opts=None, ftol=1e-6,
|
|
264
|
-
gtol=1e-6, return_hess=True, return_grad=True, method="
|
|
264
|
+
gtol=1e-6, return_hess=True, return_grad=True, method="slsqp",
|
|
265
265
|
save_fitted_params=True, mnl_init=True, fixed_thetas=None,
|
|
266
|
-
l1_penalty=0.0):
|
|
266
|
+
l1_penalty=0.1, l2_penalty=0.5):
|
|
267
267
|
# {
|
|
268
268
|
|
|
269
269
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
@@ -58,6 +58,6 @@ class MixedRandomRegret(RandomRegret, MixedLogit):
|
|
|
58
58
|
return -loglik
|
|
59
59
|
|
|
60
60
|
# Optimize the negative log-likelihood
|
|
61
|
-
self.result = minimize(neg_log_likelihood, self.beta, method='
|
|
61
|
+
self.result = minimize(neg_log_likelihood, self.beta, method='SLSQP', tol=1e-6)
|
|
62
62
|
self.beta = self.result.x
|
|
63
63
|
self.post_process()
|
|
@@ -246,7 +246,7 @@ class MultinomialLogit(DiscreteChoiceModel):
|
|
|
246
246
|
transformation="boxcox", ids=None, weights=None, avail=None,
|
|
247
247
|
base_alt=None, fit_intercept=False, init_coeff=None, maxiter=2000,
|
|
248
248
|
ftol=1e-6, gtol=1e-6, return_grad=True, return_hess=True,
|
|
249
|
-
method="
|
|
249
|
+
method="slsqp", scipy_optimisation=True, l2_penalty=0.5, l1_penalty=0.1):
|
|
250
250
|
# {
|
|
251
251
|
|
|
252
252
|
self.l2_penalty = float(l2_penalty)
|
|
@@ -592,6 +592,7 @@ class Parameters:
|
|
|
592
592
|
maxiter=2000, n_draws=1000, p_val=0.05, chosen_alts_test=None,
|
|
593
593
|
test_weight_var=None, allow_random=False, allow_bcvars=False, allow_corvars=False, models = None,
|
|
594
594
|
de_init=False, de_popsize=4, de_maxiter=3, de_tol=0.5, de_polish=False,
|
|
595
|
+
sd_penalty=0.001,
|
|
595
596
|
intercept_opts=None, base_alt=None, val_share=0.25, grad = True, hess = False, *args, **kwargs):
|
|
596
597
|
|
|
597
598
|
|
|
@@ -683,9 +684,10 @@ class Parameters:
|
|
|
683
684
|
self.de_maxiter = de_maxiter
|
|
684
685
|
self.de_tol = de_tol
|
|
685
686
|
self.de_polish = de_polish
|
|
687
|
+
self.sd_penalty = sd_penalty
|
|
686
688
|
|
|
687
689
|
# ── Regularisation (primarily for latent class) ──────────────
|
|
688
|
-
self.l1_penalty = kwargs.get('l1_penalty', 0.
|
|
690
|
+
self.l1_penalty = kwargs.get('l1_penalty', 0.1)
|
|
689
691
|
self.l2_penalty = kwargs.get('l2_penalty', 0.5)
|
|
690
692
|
|
|
691
693
|
self.intercept_opts = intercept_opts
|
|
@@ -787,7 +789,7 @@ class Parameters:
|
|
|
787
789
|
acceptable_keys = [
|
|
788
790
|
'LCR', 'verbose', 'asc_ind', 'nests', 'lambdas', 'varnest',
|
|
789
791
|
'_jax', 'all_sig', 'de_init', 'de_popsize', 'de_maxiter',
|
|
790
|
-
'de_tol', 'de_polish', 'halton_opts'
|
|
792
|
+
'de_tol', 'de_polish', 'sd_penalty', 'halton_opts'
|
|
791
793
|
]
|
|
792
794
|
|
|
793
795
|
# Assign all kwargs to self, but only if the key is in the acceptable_keys list
|
|
@@ -1001,6 +1003,27 @@ class Solution(UserDict):
|
|
|
1001
1003
|
setattr(self, key, value)
|
|
1002
1004
|
# }
|
|
1003
1005
|
|
|
1006
|
+
def __deepcopy__(self, memo):
|
|
1007
|
+
# {
|
|
1008
|
+
# Fitted model objects stored under 'model' hold module/JAX references
|
|
1009
|
+
# that cannot be deep-copied (TypeError: cannot pickle 'module' object).
|
|
1010
|
+
# The fitted model is read-only after estimation, so the copy shares it
|
|
1011
|
+
# by reference; everything else is deep-copied as usual.
|
|
1012
|
+
cls = self.__class__
|
|
1013
|
+
new = cls.__new__(cls)
|
|
1014
|
+
memo[id(self)] = new
|
|
1015
|
+
new.data = {}
|
|
1016
|
+
for k, v in self.data.items():
|
|
1017
|
+
new.data[k] = v if k == 'model' else copy.deepcopy(v, memo)
|
|
1018
|
+
for k, v in self.__dict__.items():
|
|
1019
|
+
if k == 'data':
|
|
1020
|
+
continue
|
|
1021
|
+
try:
|
|
1022
|
+
setattr(new, k, copy.deepcopy(v, memo))
|
|
1023
|
+
except TypeError:
|
|
1024
|
+
setattr(new, k, v) # share unpicklable attributes by reference
|
|
1025
|
+
return new
|
|
1026
|
+
# }
|
|
1004
1027
|
|
|
1005
1028
|
def __eq__(self, other):
|
|
1006
1029
|
"""
|
|
@@ -3985,7 +4008,8 @@ class Search():
|
|
|
3985
4008
|
de_popsize=getattr(self.param, 'de_popsize', 4),
|
|
3986
4009
|
de_maxiter=getattr(self.param, 'de_maxiter', 3),
|
|
3987
4010
|
de_tol=getattr(self.param, 'de_tol', 0.5),
|
|
3988
|
-
de_polish=getattr(self.param, 'de_polish', False)
|
|
4011
|
+
de_polish=getattr(self.param, 'de_polish', False),
|
|
4012
|
+
sd_penalty=getattr(self.param, 'sd_penalty', 0.001))
|
|
3989
4013
|
model.fit()
|
|
3990
4014
|
|
|
3991
4015
|
return model
|
|
@@ -4017,8 +4041,8 @@ class Search():
|
|
|
4017
4041
|
random_state=seed if seed is not None else 0,
|
|
4018
4042
|
optimise_membership=optimise_membership,
|
|
4019
4043
|
membership_maxiter=100,
|
|
4020
|
-
l1_penalty=getattr(self.param, 'l1_penalty', 0.
|
|
4021
|
-
l2_penalty=getattr(self.param, 'l2_penalty', 0.
|
|
4044
|
+
l1_penalty=getattr(self.param, 'l1_penalty', 0.1),
|
|
4045
|
+
l2_penalty=getattr(self.param, 'l2_penalty', 0.5),
|
|
4022
4046
|
)
|
|
4023
4047
|
|
|
4024
4048
|
membership_vars = None
|
|
@@ -4267,6 +4291,14 @@ class Search():
|
|
|
4267
4291
|
sol['model_n'] = 'latent_class'
|
|
4268
4292
|
converged = model.converged
|
|
4269
4293
|
|
|
4294
|
+
# Standard errors / p-values: needed by significance-based refinement
|
|
4295
|
+
# and PBIL updates. A failed Hessian must not abort the search.
|
|
4296
|
+
if converged and getattr(model, 'pvalues', None) is None:
|
|
4297
|
+
try:
|
|
4298
|
+
model.compute_standard_errors()
|
|
4299
|
+
except Exception as exc:
|
|
4300
|
+
print(f"[LC] standard errors unavailable for this candidate: {exc}")
|
|
4301
|
+
|
|
4270
4302
|
aic = getattr(model, 'aic', float('inf'))
|
|
4271
4303
|
bic = getattr(model, 'bic', float('inf'))
|
|
4272
4304
|
loglik = getattr(model, 'loglik', float('-inf'))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.140
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SearchLibrium
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.140
|
|
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.138
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium/test_sapbil_vs_banditsa.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{searchlibrium-0.0.138 → searchlibrium-0.0.140}/src/SearchLibrium.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|