SearchLibrium 0.0.129__tar.gz → 0.0.131__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.129 → searchlibrium-0.0.131}/PKG-INFO +1 -1
  2. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/pyproject.toml +1 -1
  3. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/harmony.py +5 -0
  4. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/search.py +126 -65
  5. searchlibrium-0.0.131/src/SearchLibrium/version.txt +1 -0
  6. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium.egg-info/PKG-INFO +1 -1
  7. searchlibrium-0.0.129/src/SearchLibrium/version.txt +0 -1
  8. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/README.md +0 -0
  9. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/setup.cfg +0 -0
  10. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/Halton.py +0 -0
  11. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/MixedLogit.py +0 -0
  12. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/Mode_Activity_Nested.py +0 -0
  13. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/RandomP.py +0 -0
  14. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/SEARCH_SM_MARIO.py +0 -0
  15. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/Two_Level_Nest.py +0 -0
  16. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/__init__.py +0 -0
  17. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/__main__.py +0 -0
  18. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/_choice_model.py +0 -0
  19. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/_device.py +0 -0
  20. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/banditsa.py +0 -0
  21. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/bhhh/minimize.py +0 -0
  22. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/boxcox_functions.py +0 -0
  23. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/call_meta.py +0 -0
  24. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/constraints_builder.py +0 -0
  25. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/hspbil.py +0 -0
  26. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/latent_class.py +0 -0
  27. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/main.py +0 -0
  28. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/main_debug.py +0 -0
  29. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/mdcev.py +0 -0
  30. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/misc.py +0 -0
  31. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/mixed_logit.py +0 -0
  32. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/mixed_nested.py +0 -0
  33. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/mixedrrm.py +0 -0
  34. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/multinomial_logit.py +0 -0
  35. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/multinomial_nested.py +0 -0
  36. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/multinomial_probit.py +0 -0
  37. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/ordered_logit.py +0 -0
  38. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/ordered_logit_mixed.py +0 -0
  39. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/predict.py +0 -0
  40. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/rrm.py +0 -0
  41. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/sapbil.py +0 -0
  42. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/selection_models.py +0 -0
  43. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/setup.py +0 -0
  44. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/siman.py +0 -0
  45. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/test_lc_de.py +0 -0
  46. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/test_mario_searches.py +0 -0
  47. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/test_sapbil_vs_banditsa.py +0 -0
  48. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium/threshold.py +0 -0
  49. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium.egg-info/SOURCES.txt +0 -0
  50. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium.egg-info/dependency_links.txt +0 -0
  51. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium.egg-info/entry_points.txt +0 -0
  52. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/src/SearchLibrium.egg-info/requires.txt +0 -0
  53. {searchlibrium-0.0.129 → searchlibrium-0.0.131}/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.129
3
+ Version: 0.0.131
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.129"
76
+ current_version = "0.0.131"
77
77
  version_pattern = "MAJOR.MINOR.PATCH"
78
78
  commit_message = "[skip ci] Bump version {old_version} -> {new_version}"
79
79
  commit = true
@@ -1647,6 +1647,11 @@ class HarmonySearch(Search):
1647
1647
  ''' call_search can use the same driver code as call_siman. '''
1648
1648
  ''' ---------------------------------------------------------------- '''
1649
1649
 
1650
+ def copy_solution(self, sol):
1651
+ """Deep-copy a Solution — mirror of Search.copy_solution for SA-compat."""
1652
+ import copy
1653
+ return copy.deepcopy(sol)
1654
+
1650
1655
  def run_search(self, existing_sols=None):
1651
1656
  # {
1652
1657
  """Override the inherited no-op run_search with the real HS loop."""
@@ -1223,6 +1223,20 @@ class Search():
1223
1223
  self.idnum = idnum
1224
1224
  self.local_impr = 0
1225
1225
 
1226
+ # ── Banlist: specifications that have already failed ────────────
1227
+ # Signatures (SHA-256 hashes) of specs that crashed or returned
1228
+ # non-converged / infinite results. These are never visited again.
1229
+ self._banlist = set()
1230
+
1231
+ # ── Variable-level failure tracker ──────────────────────────────
1232
+ # {varname: count} — how many times each variable appeared in a
1233
+ # failed (non-convergent, exception, infinite-LL) specification.
1234
+ # When count exceeds _var_attrition_limit the variable is removed
1235
+ # from the available lists so it never appears again for the
1236
+ # remainder of the search.
1237
+ self._var_failures = {}
1238
+ self._var_attrition_limit = 15 # failures before permanent removal
1239
+
1226
1240
  # ── Latent class feature toggles ────────────────────────────────
1227
1241
  self.optimise_class = kwargs.get('optimise_class', False)
1228
1242
  self.optimise_membership = kwargs.get('optimise_membership', False)
@@ -2160,6 +2174,24 @@ class Search():
2160
2174
  if 'corvars' in solution and v in solution['corvars']:
2161
2175
  solution['corvars'] = [x for x in solution['corvars'] if x != v]
2162
2176
 
2177
+ def _cull_attrited_vars(self):
2178
+ """Remove variables from the available pools that have exceeded the
2179
+ failure threshold. Once removed they stay out for the rest of
2180
+ the search, freeing the algorithm to focus on viable variables.
2181
+ """
2182
+ limit = self._var_attrition_limit
2183
+ forced = set(self._get_forced_vars())
2184
+ to_kill = {v for v, cnt in self._var_failures.items()
2185
+ if cnt >= limit and v not in forced}
2186
+ if not to_kill:
2187
+ return
2188
+ for v in sorted(to_kill):
2189
+ if hasattr(self.param, 'avail_asvars') and v in self.param.avail_asvars:
2190
+ self.param.avail_asvars.remove(v)
2191
+ if hasattr(self.param, 'avail_isvars') and v in self.param.avail_isvars:
2192
+ self.param.avail_isvars.remove(v)
2193
+ print(f" [attrition] '{v}' removed from search after {self._var_failures[v]} failures")
2194
+
2163
2195
  def _apply_latent_class_constraints(self, solution, lc_constraints):
2164
2196
  """Apply constraints specific to latent class models."""
2165
2197
  # Example: force variables to appear only in certain classes
@@ -3685,6 +3717,11 @@ class Search():
3685
3717
  ''' ---------------------------------------------------------- '''
3686
3718
  def evaluate_solution(self, sol):
3687
3719
  # {
3720
+ sig = self.setup_signature(sol)
3721
+ if sig in self._banlist:
3722
+ sol['converged'] = False
3723
+ return (sol, False)
3724
+
3688
3725
  as_vars, is_vars, rand_vars, bc_vars, corvars, asc_ind = self.get_components(sol)
3689
3726
  all_vars = is_vars + as_vars
3690
3727
  all_vars = [var for var in self.param.varnames if var in all_vars]
@@ -3692,17 +3729,32 @@ class Search():
3692
3729
  # Estimate model if input variables are present in specification
3693
3730
  if not all_vars:
3694
3731
  sol['converged'] = False
3732
+ self._banlist.add(sig)
3695
3733
  return (sol, False)
3696
3734
 
3697
- # Run backward elimination if enabled (default True), otherwise a single fit
3698
- all_sig = getattr(self.param, 'all_sig', True)
3699
- if all_sig:
3700
- sol, converged = self.backward_eliminate(sol)
3701
- else:
3702
- result = self.evaluate_model(sol)
3703
- aic, bic, loglik, mae, asvars, isvars, randvars, bcvars, corvars, converged, sol = result
3704
- sol['bcvars'] = bcvars
3705
- sol['aic'], sol['bic'], sol['loglik'], sol['mae'] = aic, bic, loglik, mae
3735
+ try:
3736
+ # Run backward elimination if enabled (default True), otherwise a single fit
3737
+ all_sig = getattr(self.param, 'all_sig', True)
3738
+ if all_sig:
3739
+ sol, converged = self.backward_eliminate(sol)
3740
+ else:
3741
+ result = self.evaluate_model(sol)
3742
+ aic, bic, loglik, mae, asvars, isvars, randvars, bcvars, corvars, converged, sol = result
3743
+ sol['bcvars'] = bcvars
3744
+ sol['aic'], sol['bic'], sol['loglik'], sol['mae'] = aic, bic, loglik, mae
3745
+ except Exception:
3746
+ sol['converged'] = False
3747
+ self._banlist.add(sig)
3748
+ if hasattr(self, 'best_solution') and self.best_solution is not None:
3749
+ fail_vars = set(as_vars + is_vars)
3750
+ base_as = set(self.best_solution.get('asvars', []))
3751
+ base_is = set(self.best_solution.get('isvars', []))
3752
+ new_vars = fail_vars - (base_as | base_is)
3753
+ if new_vars:
3754
+ for v in new_vars:
3755
+ self._var_failures[v] = self._var_failures.get(v, 0) + 1
3756
+ self._cull_attrited_vars()
3757
+ return (sol, False)
3706
3758
 
3707
3759
  if converged or (isinstance(sol.get('loglik'), float) and math.isfinite(sol.get('loglik', float('nan')))):
3708
3760
  # {
@@ -3720,6 +3772,20 @@ class Search():
3720
3772
  # {
3721
3773
  self.not_converged += 1
3722
3774
  sol['converged'] = False
3775
+ # ── Banlist: never visit this exact specification again
3776
+ self._banlist.add(sig)
3777
+ # ── Variable attrition: only blame vars ADDED vs the
3778
+ # currently-accepted best (neighbour) model.
3779
+ # Skip entirely on init / startup — no baseline yet.
3780
+ if hasattr(self, 'best_solution') and self.best_solution is not None:
3781
+ fail_vars = set(as_vars + is_vars)
3782
+ base_as = set(self.best_solution.get('asvars', []))
3783
+ base_is = set(self.best_solution.get('isvars', []))
3784
+ new_vars = fail_vars - (base_as | base_is)
3785
+ if new_vars:
3786
+ for v in new_vars:
3787
+ self._var_failures[v] = self._var_failures.get(v, 0) + 1
3788
+ self._cull_attrited_vars()
3723
3789
  # ── Convergence diagnostic: explain why the model did not converge
3724
3790
  self._diagnose_nonconvergence(sol, model_n=sol.get('model_n', ''))
3725
3791
  # }
@@ -4009,17 +4075,8 @@ class Search():
4009
4075
  model.fit()
4010
4076
 
4011
4077
 
4012
- #def fit_random_regret(self, X=None, y=None, varnames = None, alts = None, isvars = None, transvars = None, ids =None, weights = None, panels = None, avail = None, base_alt = None, df,, **kwargs):
4013
- #if df # does not have co
4014
-
4015
- def fit_random_regret(self, df):
4016
- model = RandomRegret(df=df, short=False, normalize=True)
4017
- #a = (df, s)
4018
- # model.setup_long()
4019
- # model.setup(X, y, varnames,alts , isvars, transvars, ids, weights, panels, avail, base_alt)
4020
- model.fit()
4021
- model.report()
4022
- return model
4078
+ # fit_random_regret moved below with transvars support
4079
+ # (see evaluate_rrm section)
4023
4080
 
4024
4081
 
4025
4082
 
@@ -4166,40 +4223,27 @@ class Search():
4166
4223
 
4167
4224
  def evaluate_nested_logit(self, sol):
4168
4225
  """Evaluates a Nested Logit model."""
4169
- # Extract relevant model parameters
4170
- sol =self.apply_constraints(sol)
4226
+ sol = self.apply_constraints(sol)
4171
4227
  as_vars, is_vars, asc_ind = sol['asvars'], sol['isvars'], sol['asc_ind']
4172
- logging.info('testing state')
4173
- state = sol['state']
4174
-
4175
- #state['nest_vars']
4176
- #sol = self.apply_constraints(sol)
4177
- #sol = self.repair_solution(sol)
4178
- # Define nests and lambda values (adjust based on your data)
4179
- # TODO NEED TO FEED IN THE NESTS FROM params
4228
+ bc_vars = self.define_bc_vars(sol)
4180
4229
  nests = self.param.nests
4181
4230
  lambdas = self.param.lambdas
4182
- #lambdas_mapping = self.param.lambdas_mapping
4183
4231
 
4184
- # Filter the variables to include in the model
4185
4232
  all_vars = as_vars + is_vars
4186
4233
  if len(all_vars) == 0:
4187
4234
  raise ValueError('need a variable: todo debug why')
4188
4235
  all_vars = [var for var in self.param.varnames if var in all_vars]
4189
4236
  nest_vars = [var for var in self.param.varnest if var in all_vars]
4190
4237
 
4191
- #to do if nest_vars is None repair
4192
- # if nest vars)not in all_var repair
4193
-
4194
- # Prepare data for the nested logit model
4195
4238
  X, y = self.param.df[all_vars].values, self.param.choices
4196
4239
  X_nest = self.param.df[nest_vars]
4197
4240
 
4198
- # Fit the Nested Logit model
4199
4241
  model = NestedLogit(_jax=getattr(self.param, '_jax', True))
4200
- model.setup(X=X, X_nest = X_nest, y=y, varnames=all_vars, isvars=is_vars,
4242
+ model.setup(X=X, X_nest=X_nest, y=y, varnames=all_vars, isvars=is_vars,
4201
4243
  alts=self.param.alt_var, ids=self.param.choice_id,
4202
- nests=nests, lambdas=lambdas, fit_intercept=asc_ind, return_grad=self.param.grad, return_hess=self.param.hess)
4244
+ nests=nests, lambdas=lambdas, fit_intercept=asc_ind,
4245
+ transvars=bc_vars,
4246
+ return_grad=self.param.grad, return_hess=self.param.hess)
4203
4247
 
4204
4248
  model.fit()
4205
4249
 
@@ -4234,6 +4278,7 @@ class Search():
4234
4278
  sol = self.apply_constraints(sol)
4235
4279
  as_vars, is_vars, asc_ind = sol['asvars'], sol['isvars'], sol['asc_ind']
4236
4280
  randvars = sol.get('randvars', {})
4281
+ bc_vars = self.define_bc_vars(sol)
4237
4282
 
4238
4283
  nests = self.param.nests
4239
4284
  lambdas = self.param.lambdas
@@ -4257,6 +4302,7 @@ class Search():
4257
4302
  nests=nests,
4258
4303
  lambdas=lambdas,
4259
4304
  randvars=randvars,
4305
+ transvars=bc_vars,
4260
4306
  panels=self.param.ind_id,
4261
4307
  fit_intercept=asc_ind,
4262
4308
  n_draws=n_draws,
@@ -4277,30 +4323,26 @@ class Search():
4277
4323
 
4278
4324
 
4279
4325
  def evaluate_nested_logit_ml(self, sol):
4280
- """Evaluates a Nested Logit model."""
4281
- # Extract relevant model parameters
4326
+ """Evaluates a Multi-Layer Nested Logit model."""
4282
4327
  as_vars, is_vars, asc_ind = sol['asvars'], sol['isvars'], sol['asc_ind']
4328
+ bc_vars = self.define_bc_vars(sol)
4283
4329
 
4284
- # Define nests and lambda values (adjust based on your data)
4285
- #TODO NEED TO FEED IN THE NESTS FROM params
4286
4330
  nests = self.param.nests
4287
4331
  lambdas = self.param.lambdas
4288
4332
  lambdas_mapping = self.param.lambdas_mapping
4289
4333
 
4290
- # Filter the variables to include in the model
4291
4334
  all_vars = as_vars + is_vars
4292
- if len(all_vars) ==0:
4335
+ if len(all_vars) == 0:
4293
4336
  raise ValueError('need a variable: todo debug why')
4294
4337
  all_vars = [var for var in self.param.varnames if var in all_vars]
4295
4338
 
4296
- # Prepare data for the nested logit model
4297
4339
  X, y = self.param.df[all_vars].values, self.param.choices
4298
4340
 
4299
- # Fit the Nested Logit model
4300
4341
  model = MultiLayerNestedLogit()
4301
4342
  model.setup(X=X, y=y, varnames=all_vars, isvars=is_vars,
4302
- alts=self.param.alt_var, ids=self.param.choice_id,
4303
- nests=nests, lambdas=lambdas, lambdas_mapping = lambdas_mapping, fit_intercept=asc_ind, return_grad=False)
4343
+ alts=self.param.alt_var, ids=self.param.choice_id,
4344
+ nests=nests, lambdas=lambdas, lambdas_mapping=lambdas_mapping,
4345
+ transvars=bc_vars, fit_intercept=asc_ind, return_grad=False)
4304
4346
 
4305
4347
  model.fit()
4306
4348
 
@@ -4383,26 +4425,42 @@ class Search():
4383
4425
 
4384
4426
  return sub, all_attr_vars
4385
4427
 
4386
- def fit_random_regret(self, df, use_jax=True):
4387
- model = RandomRegret(df=df, short=False, normalize=True)
4388
- if use_jax:
4389
- model.fit_jax()
4428
+ def fit_random_regret(self, df, use_jax=True, transvars=None):
4429
+ if transvars:
4430
+ # Build model via setup() so transvars flow through pre_process
4431
+ # RRM uses attribute_vars as the model variables
4432
+ all_vars = list(df.columns.difference(['id', 'alt', 'choice', 'weight']))
4433
+ X = df[all_vars].values
4434
+ y = df['choice'].values.astype(np.int32)
4435
+ alts = df['alt'].values.astype(np.int32)
4436
+ ids = df['id'].values.astype(np.int32)
4437
+ model = RandomRegret()
4438
+ model.setup(X=X, y=y, varnames=all_vars, alts=alts, ids=ids,
4439
+ transvars=[v for v in transvars if v in all_vars])
4440
+ if use_jax:
4441
+ model.fit_jax()
4442
+ else:
4443
+ model.fit()
4390
4444
  else:
4391
- model.fit()
4445
+ model = RandomRegret(df=df, short=False, normalize=True)
4446
+ if use_jax:
4447
+ model.fit_jax()
4448
+ else:
4449
+ model.fit()
4392
4450
  model.report()
4393
4451
  return model
4394
4452
 
4395
4453
  def evaluate_rrm(self, sol):
4396
4454
  as_vars, is_vars, asc_ind = sol['asvars'], sol['isvars'], sol['asc_ind']
4455
+ bc_vars = self.define_bc_vars(sol)
4456
+ bc_vars = [v for v in bc_vars if v not in self.param.isvarnames]
4397
4457
 
4398
4458
  df, attr_vars = self._build_rrm_df(self.param.df, as_vars, is_vars)
4399
- model = self.fit_random_regret(df=df)
4459
+ model = self.fit_random_regret(df=df, transvars=bc_vars)
4400
4460
  sol['model'] = model
4401
4461
  sol['coeff'] = model.coeff_est if hasattr(model, 'coeff_est') else model.beta
4402
4462
  converged = model.converged
4403
4463
  aic, bic, loglik = model.aic, model.bic, model.loglik
4404
- bc_vars = self.define_bc_vars(sol)
4405
- bc_vars = [v for v in bc_vars if v not in self.param.isvarnames]
4406
4464
  rand_vars, cor_vars = {}, []
4407
4465
 
4408
4466
  if self.mae_is_an_objective():
@@ -4432,7 +4490,8 @@ class Search():
4432
4490
  try:
4433
4491
  model.setup(X=X, y=y, varnames=all_vars, alts=self.param.alt_var,
4434
4492
  isvars=is_vars, ids=self.param.choice_id,
4435
- randvars=rand_vars, panels=self.param.ind_id,
4493
+ randvars=rand_vars, transvars=bc_vars,
4494
+ panels=self.param.ind_id,
4436
4495
  avail=self.param.avail, base_alt=self.param.base_alt,
4437
4496
  maxiter=self.param.maxiter, ftol=self.param.ftol,
4438
4497
  gtol=self.param.gtol)
@@ -4457,12 +4516,13 @@ class Search():
4457
4516
  def evaluate_ordered_logit(self,sol):
4458
4517
 
4459
4518
  as_vars, is_vars, asc_ind = sol['asvars'], sol['isvars'], sol['asc_ind']
4519
+ bc_vars = self.define_bc_vars(sol)
4520
+ bc_vars = [var for var in bc_vars if var not in self.param.isvarnames]
4460
4521
 
4461
4522
 
4462
4523
  all_vars = as_vars + is_vars
4463
4524
 
4464
4525
  all_vars = [var for var in self.param.varnames if var in all_vars]
4465
- all_vars = all_vars
4466
4526
 
4467
4527
  # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4468
4528
  #df_long = misc.wide_to_long(self.param.df, id_col='id', alt_list=self.param.alt_var, alt_name='alt')
@@ -4470,13 +4530,13 @@ class Search():
4470
4530
  #y = df_long['choice']
4471
4531
  X, y = self.param.df[all_vars], self.param.choices
4472
4532
  J = len(np.unique(self.param.alt_var))
4473
- model = self.fit_ordered_logit(X=X, y=y, ids = self.param.choice_id, varnames = all_vars, choices = J)
4474
- sol['model'] = model # Store the model object
4533
+ model = self.fit_ordered_logit(X=X, y=y, ids=self.param.choice_id,
4534
+ varnames=all_vars, choices=J,
4535
+ transvars=bc_vars)
4536
+ sol['model'] = model
4475
4537
  sol['coeff'] = model.coeff_est
4476
4538
  converged = model.converged
4477
4539
  aic, bic, loglik = model.aic, model.bic, model.loglik
4478
- bc_vars = self.define_bc_vars(sol)
4479
- bc_vars = [var for var in bc_vars if var not in self.param.isvarnames]
4480
4540
  alts = self.param.alt_var
4481
4541
  rand_vars, cor_vars = {}, []
4482
4542
 
@@ -4498,7 +4558,7 @@ class Search():
4498
4558
  return tuple
4499
4559
 
4500
4560
 
4501
- def fit_ordered_logit(self, X, y, ids, varnames, choices):
4561
+ def fit_ordered_logit(self, X, y, ids, varnames, choices, transvars=None):
4502
4562
 
4503
4563
 
4504
4564
  moll = OrderedLogitLong(X=X.values,
@@ -4509,7 +4569,8 @@ class Search():
4509
4569
  distr='logit',
4510
4570
  start=None,
4511
4571
  normalize=False,
4512
- fit_intercept=False)
4572
+ fit_intercept=False,
4573
+ transvars=transvars or [])
4513
4574
 
4514
4575
  moll.fit(method='BFGS')
4515
4576
  moll.report()
@@ -0,0 +1 @@
1
+ 0.0.131
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SearchLibrium
3
- Version: 0.0.129
3
+ Version: 0.0.131
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.129