SearchLibrium 0.0.200__tar.gz → 0.0.201__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.200/src/SearchLibrium.egg-info → searchlibrium-0.0.201}/PKG-INFO +1 -1
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/pyproject.toml +1 -1
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/harmony.py +182 -113
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/search.py +17 -7
- searchlibrium-0.0.201/src/SearchLibrium/version.txt +1 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201/src/SearchLibrium.egg-info}/PKG-INFO +1 -1
- searchlibrium-0.0.200/src/SearchLibrium/version.txt +0 -1
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/LICENSE +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/MANIFEST.in +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/README.md +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/setup.cfg +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/BEHier.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/Halton.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/MixedLogit.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/MixedLogitGSE.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/Mode_Activity_Nested.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/RandomP.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/SEARCH_SM_MARIO.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/Two_Level_Nest.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/__init__.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/__main__.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/_choice_model.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/_device.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/banditsa.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/boxcox_functions.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/call_meta.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/constraints_builder.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/data/Swissmetro_final.csv +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/data/TravelMode.csv +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/data/electricity.csv +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/hspbil.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/latent_class.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/logistic_regression.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/main.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/main_debug.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/mdcev.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/misc.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/mixed_logit.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/mixed_nested.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/mixedrrm.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/multinomial_logit.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/multinomial_nested.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/multinomial_probit.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/ordered_logit.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/ordered_logit_mixed.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/predict.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/rrm.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/sample_data.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/sapbil.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/selection_models.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/setup.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/siman.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/sparseea_agds.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/test_lc_de.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/test_mario_searches.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/test_sapbil_vs_banditsa.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/threshold.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/zero_inflated_ordered_probit.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium.egg-info/SOURCES.txt +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium.egg-info/dependency_links.txt +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium.egg-info/entry_points.txt +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium.egg-info/requires.txt +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium.egg-info/top_level.txt +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/tests/test_per_alternative_random.py +0 -0
- {searchlibrium-0.0.200 → searchlibrium-0.0.201}/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.
|
|
3
|
+
Version: 0.0.201
|
|
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>
|
|
@@ -100,7 +100,7 @@ Documentation = "https://pypi.org/project/SearchLibrium/"
|
|
|
100
100
|
realpython = "SearchLibrium.__main__:main"
|
|
101
101
|
|
|
102
102
|
[tool.bumpver]
|
|
103
|
-
current_version = "0.0.
|
|
103
|
+
current_version = "0.0.201"
|
|
104
104
|
version_pattern = "MAJOR.MINOR.PATCH"
|
|
105
105
|
commit_message = "[skip ci] Bump version {old_version} -> {new_version}"
|
|
106
106
|
commit = true
|
|
@@ -255,106 +255,96 @@ class HarmonySearch(Search):
|
|
|
255
255
|
''' A new solution, could either be built from an existing one '''
|
|
256
256
|
''' or constructed randomly. '''
|
|
257
257
|
''' ---------------------------------------------------------- '''
|
|
258
|
-
def build_solution(self, memory,
|
|
258
|
+
def build_solution(self, memory, harm_rate):
|
|
259
259
|
# {
|
|
260
|
-
"""
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
#
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
#
|
|
316
|
-
new_randvars = {k: v for k, v in
|
|
260
|
+
""" Build a new solution using Harmony Memory Consideration.
|
|
261
|
+
|
|
262
|
+
For each candidate variable, with probability harm_rate (HMCR), copy
|
|
263
|
+
the value from a randomly selected harmony in memory; otherwise
|
|
264
|
+
generate a random value. This is the standard HS approach.
|
|
265
|
+
"""
|
|
266
|
+
new_sol = Solution(nb_crit=self.nb_crit)
|
|
267
|
+
|
|
268
|
+
# Collect all candidate variables
|
|
269
|
+
all_as_candidates = list(self.param.asvarnames or [])
|
|
270
|
+
all_is_candidates = list(self.param.isvarnames or [])
|
|
271
|
+
|
|
272
|
+
# --- AS variables: for each, decide via HMCR ---
|
|
273
|
+
new_asvars = []
|
|
274
|
+
for var in all_as_candidates:
|
|
275
|
+
if self.param.generator.rand() <= harm_rate:
|
|
276
|
+
# Copy from memory: randomly pick a harmony and take its value for this var
|
|
277
|
+
if memory:
|
|
278
|
+
chosen = self.param.generator.choice(len(memory))
|
|
279
|
+
if var in memory[chosen].get('asvars', []):
|
|
280
|
+
new_asvars.append(var)
|
|
281
|
+
else:
|
|
282
|
+
# Random generation: include with some base probability
|
|
283
|
+
if self.param.generator.rand() < 0.3: # base inclusion probability
|
|
284
|
+
new_asvars.append(var)
|
|
285
|
+
|
|
286
|
+
# Enforce pre-specified
|
|
287
|
+
new_asvars = sorted(list(set().union(new_asvars, self.param.ps_asvars or [])))
|
|
288
|
+
new_asvars = self.remove_redundant_asvars(new_asvars, self.param.trans_asvars, self.param.asvarnames)
|
|
289
|
+
new_asvars = self.remove_collinear_vars(new_asvars)
|
|
290
|
+
new_asvars = self._apply_mutual_exclusion_filter(new_asvars)
|
|
291
|
+
new_sol['asvars'] = new_asvars
|
|
292
|
+
|
|
293
|
+
# --- IS variables: same HMCR logic ---
|
|
294
|
+
new_isvars = []
|
|
295
|
+
for var in all_is_candidates:
|
|
296
|
+
if self.param.generator.rand() <= harm_rate:
|
|
297
|
+
if memory:
|
|
298
|
+
chosen = self.param.generator.choice(len(memory))
|
|
299
|
+
if var in memory[chosen].get('isvars', []):
|
|
300
|
+
new_isvars.append(var)
|
|
301
|
+
else:
|
|
302
|
+
if self.param.generator.rand() < 0.3:
|
|
303
|
+
new_isvars.append(var)
|
|
304
|
+
|
|
305
|
+
new_isvars = sorted(list(set().union(new_isvars, self.param.ps_isvars or [])))
|
|
306
|
+
new_isvars = self.remove_collinear_vars(new_isvars)
|
|
307
|
+
new_isvars = self._apply_mutual_exclusion_filter(new_isvars)
|
|
308
|
+
new_sol['isvars'] = new_isvars
|
|
309
|
+
|
|
310
|
+
# --- Derived features (randvars, bcvars, corvars) from chosen harmony ---
|
|
311
|
+
# Pick one reference harmony for derived features
|
|
312
|
+
if memory and (new_asvars or new_isvars):
|
|
313
|
+
ref_sol = memory[self.param.generator.choice(len(memory))]
|
|
314
|
+
|
|
315
|
+
# Random coefficients: only for variables that are in asvars
|
|
316
|
+
new_randvars = {k: v for k, v in ref_sol.get('randvars', {}).items() if k in new_asvars}
|
|
317
317
|
new_sol['randvars'] = new_randvars
|
|
318
318
|
|
|
319
|
-
|
|
320
|
-
|
|
319
|
+
# Box-Cox: only for vars in asvars, not in corvars
|
|
320
|
+
ref_bcvars = ref_sol.get('bcvars', [])
|
|
321
|
+
new_bcvars = [v for v in ref_bcvars if v in new_asvars and v not in self.param.ps_corvars]
|
|
321
322
|
new_sol['bcvars'] = new_bcvars
|
|
322
323
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
324
|
+
# Correlation: only for randvars, not bcvars
|
|
325
|
+
ref_corvars = ref_sol.get('corvars', [])
|
|
326
|
+
if ref_corvars:
|
|
327
|
+
new_corvars = [v for v in ref_corvars if v in new_randvars and v not in new_bcvars]
|
|
328
|
+
new_sol['corvars'] = new_corvars
|
|
329
|
+
else:
|
|
330
|
+
new_sol['corvars'] = []
|
|
328
331
|
|
|
329
|
-
#
|
|
330
|
-
new_sol['asc_ind'] =
|
|
332
|
+
# Intercept
|
|
333
|
+
new_sol['asc_ind'] = ref_sol.get('asc_ind', False)
|
|
331
334
|
|
|
332
|
-
#
|
|
335
|
+
# Latent class specs
|
|
333
336
|
if getattr(self.param, 'allow_latent_corvars', False):
|
|
334
|
-
if
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
346
|
-
if chosen_sol['member_params_spec'] is not None:
|
|
347
|
-
# {
|
|
348
|
-
member_params_spec = copy.deepcopy(chosen_sol['member_params_spec'])
|
|
349
|
-
for ii, member_params in enumerate(member_params_spec):
|
|
350
|
-
# {
|
|
351
|
-
member_params_index = self.param.generator.choice(bin, size=len(member_params), p=prob)
|
|
352
|
-
member_params_spec[ii] = np.array([i for (i, v) in zip(member_params, member_params_index) if v],
|
|
353
|
-
dtype=member_params.dtype)
|
|
354
|
-
# }
|
|
355
|
-
new_sol['member_params_spec'] = member_params_spec
|
|
356
|
-
# }
|
|
357
|
-
# }
|
|
337
|
+
if ref_sol.get('class_params_spec') is not None:
|
|
338
|
+
new_sol['class_params_spec'] = copy.deepcopy(ref_sol['class_params_spec'])
|
|
339
|
+
if ref_sol.get('member_params_spec') is not None:
|
|
340
|
+
new_sol['member_params_spec'] = copy.deepcopy(ref_sol['member_params_spec'])
|
|
341
|
+
else:
|
|
342
|
+
new_sol['randvars'] = {}
|
|
343
|
+
new_sol['bcvars'] = []
|
|
344
|
+
new_sol['corvars'] = []
|
|
345
|
+
new_sol['asc_ind'] = False
|
|
346
|
+
new_sol['class_params_spec'] = None
|
|
347
|
+
new_sol['member_params_spec'] = None
|
|
358
348
|
|
|
359
349
|
self._enforce_mutual_exclusion(new_sol)
|
|
360
350
|
return new_sol
|
|
@@ -425,33 +415,79 @@ class HarmonySearch(Search):
|
|
|
425
415
|
|
|
426
416
|
''' ---------------------------------------------------------- '''
|
|
427
417
|
''' Function. Performs the pitch adjustment operation to '''
|
|
428
|
-
''' fine-tune a given solution.
|
|
429
|
-
'''
|
|
430
|
-
'''
|
|
431
|
-
'''
|
|
418
|
+
''' fine-tune a given solution. In classic HS, this is a '''
|
|
419
|
+
''' small perturbation (x_new = x_old +- bw * rand()). For '''
|
|
420
|
+
''' discrete model specification, we make SMALL changes: '''
|
|
421
|
+
''' toggle one variable, change one distribution, etc. '''
|
|
432
422
|
''' ---------------------------------------------------------- '''
|
|
433
423
|
def pitch_adjustment(self, sol, pitch):
|
|
434
424
|
# {
|
|
435
425
|
adjusted_solution = copy.deepcopy(sol)
|
|
436
|
-
perturbations = [self.perturb_asfeature, self.perturb_isfeature, self.perturb_model_t]
|
|
437
|
-
|
|
438
|
-
if self.param.allow_random:
|
|
439
|
-
perturbations.append(self.perturb_randfeature)
|
|
440
|
-
if adjusted_solution['randvars']:
|
|
441
|
-
perturbations.append(self.perturb_distribution)
|
|
442
426
|
|
|
427
|
+
# Bandwidth controls how many small changes to make
|
|
428
|
+
# In standard HS: x_new = x_old + bw * randn()
|
|
429
|
+
# Here: bw ~ pitch, make 0-2 small changes
|
|
430
|
+
n_changes = 0
|
|
431
|
+
if self.param.generator.rand() < pitch:
|
|
432
|
+
n_changes = 1
|
|
433
|
+
if self.param.generator.rand() < pitch:
|
|
434
|
+
n_changes = 2
|
|
435
|
+
|
|
436
|
+
# Build list of SMALL adjustment operations
|
|
437
|
+
small_adjustments = []
|
|
438
|
+
|
|
439
|
+
# AS variable: add one missing or remove one present (small toggle)
|
|
440
|
+
if self.param.asvarnames:
|
|
441
|
+
missing_as = [v for v in self.param.asvarnames if v not in adjusted_solution.get('asvars', [])]
|
|
442
|
+
present_as = [v for v in adjusted_solution.get('asvars', []) if v not in self.param.ps_asvars]
|
|
443
|
+
if missing_as:
|
|
444
|
+
small_adjustments.append(('add_as', lambda s: self._add_one_asvar(s, missing_as)))
|
|
445
|
+
if present_as:
|
|
446
|
+
small_adjustments.append(('rem_as', lambda s: self._remove_one_asvar(s, present_as)))
|
|
447
|
+
|
|
448
|
+
# IS variable: similar
|
|
449
|
+
if self.param.isvarnames:
|
|
450
|
+
missing_is = [v for v in self.param.isvarnames if v not in adjusted_solution.get('isvars', [])]
|
|
451
|
+
present_is = [v for v in adjusted_solution.get('isvars', []) if v not in self.param.ps_isvars]
|
|
452
|
+
if missing_is:
|
|
453
|
+
small_adjustments.append(('add_is', lambda s: self._add_one_isvar(s, missing_is)))
|
|
454
|
+
if present_is:
|
|
455
|
+
small_adjustments.append(('rem_is', lambda s: self._remove_one_isvar(s, present_is)))
|
|
456
|
+
|
|
457
|
+
# Random coefficient: change distribution of ONE random var
|
|
458
|
+
if self.param.allow_random and adjusted_solution.get('randvars'):
|
|
459
|
+
small_adjustments.append(('chg_dist', self.perturb_distribution))
|
|
460
|
+
|
|
461
|
+
# Box-Cox: add/remove one
|
|
443
462
|
if self.param.allow_bcvars:
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
463
|
+
missing_bc = [v for v in adjusted_solution.get('asvars', [])
|
|
464
|
+
if v not in adjusted_solution.get('bcvars', []) and v not in self.param.ps_corvars]
|
|
465
|
+
present_bc = [v for v in adjusted_solution.get('bcvars', []) if v not in self.param.ps_bcvars]
|
|
466
|
+
if missing_bc:
|
|
467
|
+
small_adjustments.append(('add_bc', lambda s: self._add_one_bcvar(s, missing_bc)))
|
|
468
|
+
if present_bc:
|
|
469
|
+
small_adjustments.append(('rem_bc', lambda s: self._remove_one_bcvar(s, present_bc)))
|
|
470
|
+
|
|
471
|
+
# Correlation: add/remove one (needs >=2 randvars)
|
|
472
|
+
if self.param.allow_corvars and len(adjusted_solution.get('randvars', {})) >= 2:
|
|
473
|
+
missing_cor = [v for v in adjusted_solution.get('randvars', {})
|
|
474
|
+
if v not in adjusted_solution.get('corvars', []) and v not in adjusted_solution.get('bcvars', [])]
|
|
475
|
+
present_cor = [v for v in adjusted_solution.get('corvars', []) if v not in self.param.ps_corvars]
|
|
476
|
+
if missing_cor:
|
|
477
|
+
small_adjustments.append(('add_cor', lambda s: self._add_one_corvar(s, missing_cor)))
|
|
478
|
+
if present_cor:
|
|
479
|
+
small_adjustments.append(('rem_cor', lambda s: self._remove_one_corvar(s, present_cor)))
|
|
480
|
+
|
|
481
|
+
# Model type change (rare)
|
|
482
|
+
if self.param.avail_models is not None and len(self.param.avail_models) > 1:
|
|
483
|
+
small_adjustments.append(('chg_model', self.perturb_model_t))
|
|
484
|
+
|
|
485
|
+
# Apply n_changes small adjustments
|
|
486
|
+
for _ in range(n_changes):
|
|
487
|
+
if not small_adjustments:
|
|
488
|
+
break
|
|
489
|
+
_, adj_fn = self.param.generator.choice(small_adjustments)
|
|
490
|
+
adjusted_solution = adj_fn(adjusted_solution)
|
|
455
491
|
|
|
456
492
|
adjusted_solution['randvars'] = self.normalize_randvars(
|
|
457
493
|
adjusted_solution['asvars'],
|
|
@@ -463,6 +499,39 @@ class HarmonySearch(Search):
|
|
|
463
499
|
return adjusted_solution, converged
|
|
464
500
|
# }
|
|
465
501
|
|
|
502
|
+
# --- Helper methods for small single-variable adjustments ---
|
|
503
|
+
def _add_one_asvar(self, sol, candidates):
|
|
504
|
+
var = self.param.generator.choice(candidates)
|
|
505
|
+
return self.perturb_add_asfeature(sol) if hasattr(self, 'perturb_add_asfeature') else sol
|
|
506
|
+
|
|
507
|
+
def _remove_one_asvar(self, sol, candidates):
|
|
508
|
+
var = self.param.generator.choice(candidates)
|
|
509
|
+
return self.perturb_remove_asfeature(sol) if hasattr(self, 'perturb_remove_asfeature') else sol
|
|
510
|
+
|
|
511
|
+
def _add_one_isvar(self, sol, candidates):
|
|
512
|
+
var = self.param.generator.choice(candidates)
|
|
513
|
+
return self.perturb_add_isfeature(sol) if hasattr(self, 'perturb_add_isfeature') else sol
|
|
514
|
+
|
|
515
|
+
def _remove_one_isvar(self, sol, candidates):
|
|
516
|
+
var = self.param.generator.choice(candidates)
|
|
517
|
+
return self.perturb_remove_isfeature(sol) if hasattr(self, 'perturb_remove_isfeature') else sol
|
|
518
|
+
|
|
519
|
+
def _add_one_bcvar(self, sol, candidates):
|
|
520
|
+
var = self.param.generator.choice(candidates)
|
|
521
|
+
return self.perturb_add_bcfeature(sol) if hasattr(self, 'perturb_add_bcfeature') else sol
|
|
522
|
+
|
|
523
|
+
def _remove_one_bcvar(self, sol, candidates):
|
|
524
|
+
var = self.param.generator.choice(candidates)
|
|
525
|
+
return self.perturb_remove_bcfeature(sol) if hasattr(self, 'perturb_remove_bcfeature') else sol
|
|
526
|
+
|
|
527
|
+
def _add_one_corvar(self, sol, candidates):
|
|
528
|
+
var = self.param.generator.choice(candidates)
|
|
529
|
+
return self.perturb_add_corfeature(sol) if hasattr(self, 'perturb_add_corfeature') else sol
|
|
530
|
+
|
|
531
|
+
def _remove_one_corvar(self, sol, candidates):
|
|
532
|
+
var = self.param.generator.choice(candidates)
|
|
533
|
+
return self.perturb_remove_corfeature(sol) if hasattr(self, 'perturb_remove_corfeature') else sol
|
|
534
|
+
|
|
466
535
|
''' ---------------------------------------------------------- '''
|
|
467
536
|
''' Function. Extracts the best features '''
|
|
468
537
|
''' ---------------------------------------------------------- '''
|
|
@@ -4934,7 +4934,8 @@ class Search():
|
|
|
4934
4934
|
model.setup(X=X, y=y, varnames=varnames, isvars=isvars, alts=alts,
|
|
4935
4935
|
ids=ids, transvars=transvars, fit_intercept=fit_intercept, init_coeff=init_coeff,
|
|
4936
4936
|
weights=weights, avail=avail, base_alt=base_alt, maxiter=maxiter, ftol=ftol, gtol=gtol,
|
|
4937
|
-
l2_penalty=
|
|
4937
|
+
l2_penalty=getattr(self.param, 'l2_penalty', 0.5),
|
|
4938
|
+
l1_penalty=getattr(self.param, 'l1_penalty', 0.1))
|
|
4938
4939
|
model.fit()
|
|
4939
4940
|
|
|
4940
4941
|
return model
|
|
@@ -5051,8 +5052,7 @@ class Search():
|
|
|
5051
5052
|
isvars = [i for i in isvars if i not in randvars.keys()]
|
|
5052
5053
|
|
|
5053
5054
|
return isvars, varnames, fit_intercept
|
|
5054
|
-
|
|
5055
|
-
def fit_mxl(self, X, y, varnames, alts, isvars, transvars, ids, panels, randvars, corvars,
|
|
5055
|
+
def fit_mxl(self, X, y, varnames, alts, isvars, transvars, ids, panels, randvars, corvars,
|
|
5056
5056
|
fit_intercept, init_coeff, n_draws, weights, avail, base_alt, maxiter, ftol, gtol, save_fitted_params,
|
|
5057
5057
|
halton_opts=None):
|
|
5058
5058
|
# {
|
|
@@ -5063,6 +5063,7 @@ class Search():
|
|
|
5063
5063
|
# repair the model..
|
|
5064
5064
|
isvars, varnames, fit_intercept = self.process_variables(isvars, varnames, randvars)
|
|
5065
5065
|
|
|
5066
|
+
|
|
5066
5067
|
model.setup(X=X, y=y, varnames=varnames, isvars=isvars, alts=alts, transvars=transvars, ids=ids,
|
|
5067
5068
|
randvars=randvars, panels=panels, fit_intercept=fit_intercept, correlated_vars=corvars, n_draws=n_draws,
|
|
5068
5069
|
init_coeff=init_coeff, weights=weights, avail=avail, base_alt=base_alt, maxiter=maxiter,
|
|
@@ -5072,7 +5073,9 @@ class Search():
|
|
|
5072
5073
|
de_maxiter=getattr(self.param, 'de_maxiter', 3),
|
|
5073
5074
|
de_tol=getattr(self.param, 'de_tol', 0.5),
|
|
5074
5075
|
de_polish=getattr(self.param, 'de_polish', False),
|
|
5075
|
-
sd_penalty=getattr(self.param, 'sd_penalty', 0.001)
|
|
5076
|
+
sd_penalty=getattr(self.param, 'sd_penalty', 0.001),
|
|
5077
|
+
reg_penalty=getattr(self.param, 'l2_penalty', 0.5),
|
|
5078
|
+
l1_penalty=getattr(self.param, 'l1_penalty', 0.1))
|
|
5076
5079
|
model.fit()
|
|
5077
5080
|
|
|
5078
5081
|
return model
|
|
@@ -5638,12 +5641,17 @@ class Search():
|
|
|
5638
5641
|
model = RandomRegret()
|
|
5639
5642
|
model.setup(X=X, y=y, varnames=all_vars, alts=alts, ids=ids,
|
|
5640
5643
|
transvars=[v for v in transvars if v in all_vars])
|
|
5644
|
+
# Apply shrinkage parameters from search parameters
|
|
5645
|
+
model.reg_penalty = getattr(self.param, 'l2_penalty', 0.5)
|
|
5646
|
+
model.l1_penalty = getattr(self.param, 'l1_penalty', 0.1)
|
|
5641
5647
|
if use_jax:
|
|
5642
5648
|
model.fit_jax()
|
|
5643
5649
|
else:
|
|
5644
5650
|
model.fit()
|
|
5645
5651
|
else:
|
|
5646
5652
|
model = RandomRegret(df=df, short=False, normalize=True)
|
|
5653
|
+
model.reg_penalty = getattr(self.param, 'l2_penalty', 0.5)
|
|
5654
|
+
model.l1_penalty = getattr(self.param, 'l1_penalty', 0.1)
|
|
5647
5655
|
if use_jax:
|
|
5648
5656
|
model.fit_jax()
|
|
5649
5657
|
else:
|
|
@@ -5745,9 +5753,7 @@ class Search():
|
|
|
5745
5753
|
# COMPUTE MAE
|
|
5746
5754
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
5747
5755
|
if self.mae_is_an_objective():
|
|
5748
|
-
|
|
5749
|
-
# which mismatched the test design matrix).
|
|
5750
|
-
X_test = self.param.df_test[all_vars]
|
|
5756
|
+
X_test, _ = self._get_orthogonalized_X(all_vars) # Use same transformation for test
|
|
5751
5757
|
y_test = self.param.test_choices if getattr(self.param, 'test_choices', None) is not None else self.param.choices
|
|
5752
5758
|
ids_test = self.param.test_choice_id if getattr(self.param, 'test_choice_id', None) is not None else self.param.choice_id
|
|
5753
5759
|
test_model = self.fit_ordered_logit(X=X_test, y=y_test, ids=ids_test, varnames=all_vars)
|
|
@@ -5780,6 +5786,10 @@ class Search():
|
|
|
5780
5786
|
fit_intercept=False,
|
|
5781
5787
|
transvars=transvars or [])
|
|
5782
5788
|
|
|
5789
|
+
# Apply shrinkage parameters from search parameters
|
|
5790
|
+
moll.reg_penalty = getattr(self.param, 'l2_penalty', 0.5)
|
|
5791
|
+
moll.l1_penalty = getattr(self.param, 'l1_penalty', 0.1)
|
|
5792
|
+
|
|
5783
5793
|
moll.fit(method='BFGS')
|
|
5784
5794
|
moll.report()
|
|
5785
5795
|
return moll
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.201
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SearchLibrium
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.201
|
|
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.200
|
|
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
|
|
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.200 → searchlibrium-0.0.201}/src/SearchLibrium/test_sapbil_vs_banditsa.py
RENAMED
|
File without changes
|
|
File without changes
|
{searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium/zero_inflated_ordered_probit.py
RENAMED
|
File without changes
|
|
File without changes
|
{searchlibrium-0.0.200 → searchlibrium-0.0.201}/src/SearchLibrium.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|