SearchLibrium 0.0.112__tar.gz → 0.0.113__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.112 → searchlibrium-0.0.113}/PKG-INFO +1 -1
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/pyproject.toml +1 -1
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/multinomial_logit.py +80 -2
- searchlibrium-0.0.113/src/SearchLibrium/version.txt +1 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium.egg-info/PKG-INFO +1 -1
- searchlibrium-0.0.112/src/SearchLibrium/version.txt +0 -1
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/README.md +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/setup.cfg +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/Halton.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/MixedLogit.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/Mode_Activity_Nested.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/RandomP.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/SEARCH_SM_MARIO.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/Two_Level_Nest.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/__init__.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/__main__.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/_choice_model.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/_device.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/banditsa.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/bhhh/minimize.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/boxcox_functions.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/call_meta.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/constraints_builder.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/harmony.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/latent_class.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/main.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/main_debug.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/mdcev.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/misc.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/mixed_logit.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/mixed_nested.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/mixedrrm.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/multinomial_nested.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/multinomial_probit.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/ordered_logit.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/ordered_logit_mixed.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/rrm.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/sapbil.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/search.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/selection_models.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/setup.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/siman.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/test_lc_de.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/test_mario_searches.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/test_sapbil_vs_banditsa.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/threshold.py +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium.egg-info/SOURCES.txt +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium.egg-info/dependency_links.txt +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium.egg-info/entry_points.txt +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium.egg-info/requires.txt +0 -0
- {searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium.egg-info/top_level.txt +0 -0
|
@@ -60,7 +60,7 @@ Homepage = "https://github.com/zahern/HypothesisX"
|
|
|
60
60
|
realpython = "SearchLibrium.__main__:main"
|
|
61
61
|
|
|
62
62
|
[tool.bumpver]
|
|
63
|
-
current_version = "0.0.
|
|
63
|
+
current_version = "0.0.113"
|
|
64
64
|
version_pattern = "MAJOR.MINOR.PATCH"
|
|
65
65
|
commit_message = "[skip ci] Bump version {old_version} -> {new_version}"
|
|
66
66
|
commit = true
|
|
@@ -246,10 +246,12 @@ 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="bfgs", scipy_optimisation=True):
|
|
249
|
+
method="bfgs", scipy_optimisation=True, l2_penalty=0.0):
|
|
250
250
|
# {
|
|
251
251
|
|
|
252
|
-
|
|
252
|
+
self.l2_penalty = float(l2_penalty)
|
|
253
|
+
if self.l2_penalty > 0:
|
|
254
|
+
self.reassign_penalty(self.l2_penalty * 0.5)
|
|
253
255
|
self.fit_intercept = fit_intercept
|
|
254
256
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
255
257
|
# RECAST AS NUMPY NDARRAY
|
|
@@ -342,6 +344,78 @@ class MultinomialLogit(DiscreteChoiceModel):
|
|
|
342
344
|
|
|
343
345
|
# }
|
|
344
346
|
|
|
347
|
+
def wide_setup(self, X_wide, y_chosen, varnames, n_alts,
|
|
348
|
+
ids=None, empirical_init=False, **setup_kw):
|
|
349
|
+
"""
|
|
350
|
+
Setup accepting wide-format data (one row per case, features shared
|
|
351
|
+
across alternatives) instead of the standard long format.
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
X_wide : ndarray (N, F)
|
|
356
|
+
Feature matrix - one row per case. All features are treated as
|
|
357
|
+
alternative-INVARIANT (isvars).
|
|
358
|
+
y_chosen : ndarray (N,) int
|
|
359
|
+
Chosen alternative index per case in [0, n_alts-1].
|
|
360
|
+
varnames : list[str]
|
|
361
|
+
Feature names matching X_wide columns.
|
|
362
|
+
n_alts : int
|
|
363
|
+
Number of alternatives (K).
|
|
364
|
+
ids : ndarray (N,) int, optional
|
|
365
|
+
Case identifiers. Defaults to np.arange(N).
|
|
366
|
+
empirical_init : bool
|
|
367
|
+
If True, initialise betas from empirical log-shares rather than
|
|
368
|
+
zeros - better warm-start for imbalanced alternatives.
|
|
369
|
+
**setup_kw
|
|
370
|
+
Forwarded to self.setup().
|
|
371
|
+
"""
|
|
372
|
+
N = len(y_chosen)
|
|
373
|
+
K = int(n_alts)
|
|
374
|
+
if ids is None:
|
|
375
|
+
ids = np.arange(N, dtype=np.int32)
|
|
376
|
+
else:
|
|
377
|
+
ids = np.asarray(ids, dtype=np.int32)
|
|
378
|
+
|
|
379
|
+
rows = []
|
|
380
|
+
for alt in range(K):
|
|
381
|
+
chunk = np.zeros((N, X_wide.shape[1] + 2))
|
|
382
|
+
chunk[:, :X_wide.shape[1]] = X_wide
|
|
383
|
+
chunk[:, -2] = float(alt)
|
|
384
|
+
chunk[:, -1] = (y_chosen == alt).astype(float)
|
|
385
|
+
rows.append(chunk)
|
|
386
|
+
long = np.vstack(rows)
|
|
387
|
+
ids_long = np.tile(ids, K)
|
|
388
|
+
|
|
389
|
+
sort_idx = np.lexsort((long[:, -2].astype(np.int32), ids_long))
|
|
390
|
+
X_long = long[sort_idx, :-2].astype(float)
|
|
391
|
+
y_long = long[sort_idx, -1].astype(float)
|
|
392
|
+
ids_sorted = ids_long[sort_idx]
|
|
393
|
+
alts = np.arange(K, dtype=np.int32)
|
|
394
|
+
full_varnames = list(varnames)
|
|
395
|
+
|
|
396
|
+
if empirical_init and 'init_coeff' not in setup_kw:
|
|
397
|
+
counts = np.bincount(y_chosen.astype(int), minlength=K).astype(float)
|
|
398
|
+
counts = np.maximum(counts, 1.0)
|
|
399
|
+
log_shares = np.log(counts / counts.sum())
|
|
400
|
+
init_intercepts = log_shares[1:] - log_shares[0]
|
|
401
|
+
init = np.zeros((K - 1) * len(full_varnames))
|
|
402
|
+
for k in range(K - 1):
|
|
403
|
+
init[k * len(full_varnames)] = init_intercepts[k]
|
|
404
|
+
setup_kw['init_coeff'] = init
|
|
405
|
+
|
|
406
|
+
setup_kw.pop('empirical_init', None)
|
|
407
|
+
|
|
408
|
+
self.setup(
|
|
409
|
+
X=X_long, y=y_long,
|
|
410
|
+
varnames=full_varnames,
|
|
411
|
+
alts=alts,
|
|
412
|
+
isvars=full_varnames,
|
|
413
|
+
ids=ids_sorted,
|
|
414
|
+
base_alt=0,
|
|
415
|
+
**setup_kw,
|
|
416
|
+
)
|
|
417
|
+
return self
|
|
418
|
+
|
|
345
419
|
|
|
346
420
|
def predict_setup(self, X_new, varnames, est_coeff, avail_new=None, isvars = None, ids = None, transvars = None, alts =None, y = None, J = None, base_alt=None, fit_intercept=True):
|
|
347
421
|
"""
|
|
@@ -552,6 +626,8 @@ class MultinomialLogit(DiscreteChoiceModel):
|
|
|
552
626
|
loglik = loglik * weights[:, 0] # doesn't matter which column
|
|
553
627
|
loglik = np.sum(loglik) # Sum up the elements
|
|
554
628
|
if not self.return_grad:
|
|
629
|
+
if self.l2_penalty > 0:
|
|
630
|
+
loglik = loglik - 0.5 * self.l2_penalty * np.dot(betas, betas)
|
|
555
631
|
return (-loglik, )
|
|
556
632
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
557
633
|
# Individual contribution to the gradient
|
|
@@ -597,6 +673,8 @@ class MultinomialLogit(DiscreteChoiceModel):
|
|
|
597
673
|
|
|
598
674
|
self.Hinv = self.function_hessian(grad) # Conditionally compute Hinv based upon return_hess flag
|
|
599
675
|
grad = np.sum(grad, axis=0) # Compute grad_[i] = sum(j, grad[i][j])
|
|
676
|
+
if self.l2_penalty > 0 and self.return_grad:
|
|
677
|
+
grad = grad - self.l2_penalty * betas
|
|
600
678
|
self.gtol_res = np.linalg.norm(grad, ord=np.inf) # Compute the norm of "grad"
|
|
601
679
|
|
|
602
680
|
penalty = self.regularize_loglik(betas)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.113
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.0.112
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium/test_sapbil_vs_banditsa.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{searchlibrium-0.0.112 → searchlibrium-0.0.113}/src/SearchLibrium.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|