hitspy 0.1.0__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.
hitspy-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kostas Florios
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ global-include *.cpp *.hpp *.h
hitspy-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,160 @@
1
+ Metadata-Version: 2.4
2
+ Name: hitspy
3
+ Version: 0.1.0
4
+ Summary: Fast OpenMP-parallelized discrete tabu search with pybind11 C++ bindings for maximum score estimator
5
+ Author-email: Kostas Florios <cflorios@aueb.gr>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/kflorios/hitspy
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: C++
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: numpy>=1.20.0
16
+ Dynamic: license-file
17
+
18
+ # hitspy
19
+
20
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
21
+
22
+ **hitspy** is a high-performance Python package providing C++ implementation of discrete Tabu Search algorithm for maximum score estimation. Powered by **pybind11** and parallelized with **OpenMP**, `hitspy` accelerates Hyperplanes Intersection evaluation and search routines across large search spaces.
23
+
24
+ ---
25
+
26
+ ## Key Features
27
+
28
+ - ⚡ **High Performance**: Core optimization routines written in C++ for maximum throughput.
29
+ - 🚀 **Parallel Computing**: OpenMP multi-threading support for fast neighborhood evaluations.
30
+ - 🐍 **Seamless Python API**: Native `numpy` array integration using `pybind11`.
31
+
32
+ ---
33
+
34
+ ## Installation
35
+
36
+ ### From Source (Locally)
37
+
38
+ Download the main folder of the package, cd to it and provide
39
+ in your preferred environment:
40
+
41
+ ```bash
42
+ pip install -e .
43
+ ```
44
+ ---
45
+
46
+ ### Requirements
47
+ Python 3.8 and above, MS Visual Studio 2019 and above
48
+
49
+ ## API Reference
50
+
51
+ ### `hitspy.run_tabu_search`
52
+
53
+ Executes an OpenMP-accelerated discrete Tabu Search algorithm for maximum score estimator computation.
54
+
55
+ ```python
56
+ hitspy.run_tabu_search(
57
+ X: numpy.ndarray,
58
+ y: numpy.ndarray,
59
+ b0: float,
60
+ d: float,
61
+ iSeed: int
62
+ ) -> numpy.ndarray
63
+ ```
64
+
65
+ ## Complete Example: Multi-Seed Benchmark (20 Random Starts)
66
+
67
+ This benchmark demonstrates how to execute 20 distinct random restarts using `hitspy`, summarize overall solution quality, and compute selection frequency statistics for each attribute.
68
+
69
+ ```python
70
+ import numpy as np
71
+ import pandas as pd
72
+ from hitspy import run_tabu_search
73
+
74
+ # 1. Load and preprocess data
75
+ X_raw = np.loadtxt("X.txt")
76
+ y_raw = np.loadtxt("y.txt")
77
+
78
+ X = X_raw[:, 1:]
79
+ y = y_raw[:, 1].astype(int)
80
+ X=np.transpose(X)
81
+ X = np.ascontiguousarray(X, dtype=np.float64)
82
+
83
+ # 2. Parameters
84
+ b0 = -1.0
85
+ d = 10000
86
+ num_runs = 20
87
+
88
+ # Generate 20 distinct random seeds
89
+ np.random.seed(42) # For reproducible seed generation
90
+ seeds = np.random.randint(0, 1000000, size=num_runs)
91
+
92
+ # 3. Storage for results
93
+ results_list = []
94
+
95
+ print("Starting 20 runs...")
96
+
97
+ # 4. Run loop
98
+ for i in range(num_runs):
99
+ print('run...:',i+1)
100
+ current_seed = int(seeds[i])
101
+
102
+ # Execute Tabu Search
103
+ #attrs, coeffs, score = run_tabu_search(X=X, y=y, b0=b0, d=d, iSeed=current_seed)
104
+ attrs, coeffs, score = run_tabu_search(X=X, y=y, b0=b0, d=d, iSeed=12345+i+1)
105
+
106
+ # hitspy returns 'attributes' as a 1D numpy array/list and 'score' / 'coeffs' as keys in the dict
107
+
108
+ # Save metrics
109
+ results_list.append(
110
+ {
111
+ "Run": i + 1,
112
+ "Seed": 12345+i+1,
113
+ "Score": score,
114
+ "Coeffs": coeffs,
115
+ "Attrs": attrs
116
+ }
117
+ )
118
+
119
+ # 5. Combine into a single DataFrame
120
+ results_df = pd.DataFrame(results_list)
121
+
122
+ # 6. Display results table
123
+ print(results_df.to_string(index=False))
124
+
125
+ # 7. Summary statistics
126
+ print("\n=== Summary Across 20 Runs ===")
127
+ print("Best Score Found: ", results_df["Score"].max())
128
+ print("Mean Score: ", results_df["Score"].mean())
129
+ print("Score Std Dev: ", results_df["Score"].std(ddof=1))
130
+ ```
131
+
132
+ ## Acknowledgements
133
+
134
+ I would like to thank Dr. **Alexandros Louka** and Professor **Yannis Bilias** for their valuable help and the shared ideas for the development of HITSpy. The development of the HITSpy software was supported by the Hellenic Foundation for Research and Innovation (H.F.R.I.) under the '2nd Call for H.F.R.I. Research Projects to support Post-Doctoral Researchers' (Project Number: 902).
135
+
136
+ ## Citation
137
+
138
+ If you use `hitspy` in your research, please cite the underlying methodology papers:
139
+
140
+ ```bibtex
141
+ @article{florios2025hits,
142
+ title={HITS: Hyperplanes intersection tabu search for maximum score estimation},
143
+ author={Florios, Kostas and Louka, Alexandros and Bilias, Yannis},
144
+ journal={SoftwareX},
145
+ volume={30},
146
+ pages={102164},
147
+ year={2025},
148
+ publisher={Elsevier}
149
+ }
150
+
151
+ @article{manski1975maximum,
152
+ title={Maximum score estimation of the stochastic utility model of choice},
153
+ author={Manski, Charles F},
154
+ journal={Journal of econometrics},
155
+ volume={3},
156
+ number={3},
157
+ pages={205--228},
158
+ year={1975},
159
+ publisher={Elsevier}
160
+ }
hitspy-0.1.0/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # hitspy
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+
5
+ **hitspy** is a high-performance Python package providing C++ implementation of discrete Tabu Search algorithm for maximum score estimation. Powered by **pybind11** and parallelized with **OpenMP**, `hitspy` accelerates Hyperplanes Intersection evaluation and search routines across large search spaces.
6
+
7
+ ---
8
+
9
+ ## Key Features
10
+
11
+ - ⚡ **High Performance**: Core optimization routines written in C++ for maximum throughput.
12
+ - 🚀 **Parallel Computing**: OpenMP multi-threading support for fast neighborhood evaluations.
13
+ - 🐍 **Seamless Python API**: Native `numpy` array integration using `pybind11`.
14
+
15
+ ---
16
+
17
+ ## Installation
18
+
19
+ ### From Source (Locally)
20
+
21
+ Download the main folder of the package, cd to it and provide
22
+ in your preferred environment:
23
+
24
+ ```bash
25
+ pip install -e .
26
+ ```
27
+ ---
28
+
29
+ ### Requirements
30
+ Python 3.8 and above, MS Visual Studio 2019 and above
31
+
32
+ ## API Reference
33
+
34
+ ### `hitspy.run_tabu_search`
35
+
36
+ Executes an OpenMP-accelerated discrete Tabu Search algorithm for maximum score estimator computation.
37
+
38
+ ```python
39
+ hitspy.run_tabu_search(
40
+ X: numpy.ndarray,
41
+ y: numpy.ndarray,
42
+ b0: float,
43
+ d: float,
44
+ iSeed: int
45
+ ) -> numpy.ndarray
46
+ ```
47
+
48
+ ## Complete Example: Multi-Seed Benchmark (20 Random Starts)
49
+
50
+ This benchmark demonstrates how to execute 20 distinct random restarts using `hitspy`, summarize overall solution quality, and compute selection frequency statistics for each attribute.
51
+
52
+ ```python
53
+ import numpy as np
54
+ import pandas as pd
55
+ from hitspy import run_tabu_search
56
+
57
+ # 1. Load and preprocess data
58
+ X_raw = np.loadtxt("X.txt")
59
+ y_raw = np.loadtxt("y.txt")
60
+
61
+ X = X_raw[:, 1:]
62
+ y = y_raw[:, 1].astype(int)
63
+ X=np.transpose(X)
64
+ X = np.ascontiguousarray(X, dtype=np.float64)
65
+
66
+ # 2. Parameters
67
+ b0 = -1.0
68
+ d = 10000
69
+ num_runs = 20
70
+
71
+ # Generate 20 distinct random seeds
72
+ np.random.seed(42) # For reproducible seed generation
73
+ seeds = np.random.randint(0, 1000000, size=num_runs)
74
+
75
+ # 3. Storage for results
76
+ results_list = []
77
+
78
+ print("Starting 20 runs...")
79
+
80
+ # 4. Run loop
81
+ for i in range(num_runs):
82
+ print('run...:',i+1)
83
+ current_seed = int(seeds[i])
84
+
85
+ # Execute Tabu Search
86
+ #attrs, coeffs, score = run_tabu_search(X=X, y=y, b0=b0, d=d, iSeed=current_seed)
87
+ attrs, coeffs, score = run_tabu_search(X=X, y=y, b0=b0, d=d, iSeed=12345+i+1)
88
+
89
+ # hitspy returns 'attributes' as a 1D numpy array/list and 'score' / 'coeffs' as keys in the dict
90
+
91
+ # Save metrics
92
+ results_list.append(
93
+ {
94
+ "Run": i + 1,
95
+ "Seed": 12345+i+1,
96
+ "Score": score,
97
+ "Coeffs": coeffs,
98
+ "Attrs": attrs
99
+ }
100
+ )
101
+
102
+ # 5. Combine into a single DataFrame
103
+ results_df = pd.DataFrame(results_list)
104
+
105
+ # 6. Display results table
106
+ print(results_df.to_string(index=False))
107
+
108
+ # 7. Summary statistics
109
+ print("\n=== Summary Across 20 Runs ===")
110
+ print("Best Score Found: ", results_df["Score"].max())
111
+ print("Mean Score: ", results_df["Score"].mean())
112
+ print("Score Std Dev: ", results_df["Score"].std(ddof=1))
113
+ ```
114
+
115
+ ## Acknowledgements
116
+
117
+ I would like to thank Dr. **Alexandros Louka** and Professor **Yannis Bilias** for their valuable help and the shared ideas for the development of HITSpy. The development of the HITSpy software was supported by the Hellenic Foundation for Research and Innovation (H.F.R.I.) under the '2nd Call for H.F.R.I. Research Projects to support Post-Doctoral Researchers' (Project Number: 902).
118
+
119
+ ## Citation
120
+
121
+ If you use `hitspy` in your research, please cite the underlying methodology papers:
122
+
123
+ ```bibtex
124
+ @article{florios2025hits,
125
+ title={HITS: Hyperplanes intersection tabu search for maximum score estimation},
126
+ author={Florios, Kostas and Louka, Alexandros and Bilias, Yannis},
127
+ journal={SoftwareX},
128
+ volume={30},
129
+ pages={102164},
130
+ year={2025},
131
+ publisher={Elsevier}
132
+ }
133
+
134
+ @article{manski1975maximum,
135
+ title={Maximum score estimation of the stochastic utility model of choice},
136
+ author={Manski, Charles F},
137
+ journal={Journal of econometrics},
138
+ volume={3},
139
+ number={3},
140
+ pages={205--228},
141
+ year={1975},
142
+ publisher={Elsevier}
143
+ }
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel", "pybind11>=2.10.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "hitspy"
7
+ version = "0.1.0"
8
+ description = "Fast OpenMP-parallelized discrete tabu search with pybind11 C++ bindings for maximum score estimator"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Kostas Florios", email = "cflorios@aueb.gr" }
14
+ ]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: C++",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ ]
21
+ dependencies = [
22
+ "numpy>=1.20.0"
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/kflorios/hitspy"
hitspy-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
hitspy-0.1.0/setup.py ADDED
@@ -0,0 +1,45 @@
1
+ import sys
2
+ from setuptools import setup, Extension
3
+ from setuptools.command.build_ext import build_ext
4
+
5
+ class BuildExt(build_ext):
6
+ """Custom build extension to handle compiler-specific flags."""
7
+ def build_extensions(self):
8
+ compiler = self.compiler.compiler_type
9
+ for ext in self.extensions:
10
+ if compiler == "msvc":
11
+ ext.extra_compile_args = ["/O2", "/openmp", "/std:c++14"]
12
+ else:
13
+ ext.extra_compile_args = ["-O3", "-fopenmp", "-std=c++14"]
14
+ ext.extra_link_args = ["-fopenmp"]
15
+ super().build_extensions()
16
+
17
+ class get_pybind_include:
18
+ """Helper class to delay importing pybind11 until build time."""
19
+ def __str__(self):
20
+ import pybind11
21
+ return pybind11.get_include()
22
+
23
+ ext_modules = [
24
+ Extension(
25
+ "hitspy._hitspy",
26
+ sources=[
27
+ "src/hitspy/tabu_bindings.cpp",
28
+ "src/hitspy/tabu_api.cpp",
29
+ "src/hitspy/bsort.cpp",
30
+ "src/hitspy/evaluate_neighbourhood.cpp",
31
+ "src/hitspy/EvaluateScore1.cpp",
32
+ "src/hitspy/gelim.cpp",
33
+ ],
34
+ include_dirs=[
35
+ get_pybind_include(),
36
+ "src/hitspy",
37
+ ],
38
+ language="c++",
39
+ )
40
+ ]
41
+
42
+ setup(
43
+ ext_modules=ext_modules,
44
+ cmdclass={"build_ext": BuildExt},
45
+ )
@@ -0,0 +1,43 @@
1
+ #include "tabu_search.hpp"
2
+
3
+ // -----------------------------------------------------------------------------
4
+ // EvaluateScore1
5
+ // sump(i) = sum_j beta(j)*Z(j+1,i) + b0*Z(1,i)
6
+ // firep(i) = sign(sump(i))
7
+ // sfire = sum_i data_y(i)*firep(i)
8
+ // h = (1 + sfire/T) / 2
9
+ // -----------------------------------------------------------------------------
10
+ void EvaluateScore1(
11
+ const double w[pmax],
12
+ int T,
13
+ int p,
14
+ double &h,
15
+ const double Z[pmax][Tmax],
16
+ double b0,
17
+ const int data_y[Tmax]
18
+ )
19
+ {
20
+ double beta[pmax];
21
+ int firep[Tmax];
22
+
23
+ for (int j = 0; j < p; ++j)
24
+ beta[j] = w[j];
25
+
26
+ int sfire = 0;
27
+
28
+ for (int i = 0; i < T; ++i)
29
+ {
30
+ double s = 0.0;
31
+
32
+ for (int j = 0; j < p; ++j)
33
+ s += beta[j] * Z[j + 1][i];
34
+
35
+ s += b0 * Z[0][i];
36
+
37
+ firep[i] = (s >= 0.0 ? 1 : -1);
38
+
39
+ sfire += data_y[i] * firep[i];
40
+ }
41
+
42
+ h = (1.0 + double(sfire) / double(T)) * 0.5;
43
+ }
@@ -0,0 +1,4 @@
1
+ from ._hitspy import run_tabu_search
2
+
3
+ __version__ = "0.1.0"
4
+ __all__ = ["run_tabu_search"]
@@ -0,0 +1,26 @@
1
+ #include "tabu_search.hpp"
2
+
3
+ // -----------------------------------------------------------------------------
4
+ // bsort: sort nat[0..np-1] into snat[0..np-1] in increasing order
5
+ // Fortran original:
6
+ // do i=1,np
7
+ // do i=1,np-1
8
+ // do j=i+1,np
9
+ // -----------------------------------------------------------------------------
10
+ void bsort(int np, const int nat[], int snat[])
11
+ {
12
+ // copy input
13
+ for (int i = 0; i < np; ++i)
14
+ snat[i] = nat[i];
15
+
16
+ // simple bubble/selection hybrid as in Fortran
17
+ for (int i = 0; i < np - 1; ++i) {
18
+ for (int j = i + 1; j < np; ++j) {
19
+ if (snat[i] > snat[j]) {
20
+ int tmp = snat[i];
21
+ snat[i] = snat[j];
22
+ snat[j] = tmp;
23
+ }
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,159 @@
1
+ #include "tabu_search.hpp"
2
+
3
+ // -----------------------------------------------------------------------------
4
+ // evaluate_neighbourhood
5
+ // 0-based C++ translation of the Fortran routine.
6
+ //
7
+ // This routine evaluates all possible substitutions of attribute i_idx
8
+ // with every candidate id[j], j = 0..n-1, except those already in natsol.
9
+ //
10
+ // It performs:
11
+ // - triviality check
12
+ // - tabu check
13
+ // - local system solve (gelim)
14
+ // - bounds check
15
+ // - score computation (EvaluateScore1)
16
+ // - tracking of best dz_i and MaxScoreEver_i
17
+ //
18
+ // This version is called inside an OpenMP parallel loop in main.cpp.
19
+ // -----------------------------------------------------------------------------
20
+ void evaluate_neighbourhood(
21
+ int i_idx,
22
+ int p,
23
+ int n,
24
+ const int natsol[pmax],
25
+ const int Mat[pmax][Itmax],
26
+ const double Z[pmax][Tmax],
27
+ const double y[Tmax],
28
+ const int id[Tmax],
29
+ const double xlbound[pmax],
30
+ const double xubound[pmax],
31
+ double b0,
32
+ double MaxScore,
33
+ int ntenure,
34
+ int csubstitutionstotal,
35
+ int T,
36
+ double &dz_i,
37
+ int natcur_i[pmax],
38
+ double &MaxScoreEver_i,
39
+ int natever_i[pmax],
40
+ int &row_i,
41
+ int &cbd_i,
42
+ int &cinfsd_i,
43
+ const int data_y[Tmax]
44
+ )
45
+ {
46
+ row_i = 0;
47
+ cbd_i = 0;
48
+ cinfsd_i = 0;
49
+ dz_i = -1.0;
50
+ MaxScoreEver_i = -1.0e99;
51
+
52
+ int nat_local[pmax];
53
+ int snat_local[pmax];
54
+ double b_local[pmax];
55
+ double A_local[pmax][pmax];
56
+ double bb_local[pmax];
57
+ double x_local[pmax];
58
+
59
+ for (int j = 0; j < n; ++j)
60
+ {
61
+ bool trivial = false;
62
+ for (int iii = 0; iii < p; ++iii)
63
+ {
64
+ if (natsol[iii] == id[j])
65
+ {
66
+ trivial = true;
67
+ break;
68
+ }
69
+ }
70
+ if (trivial)
71
+ continue;
72
+
73
+ row_i++;
74
+
75
+ for (int iii = 0; iii < p; ++iii)
76
+ nat_local[iii] = (iii == i_idx ? id[j] : natsol[iii]);
77
+
78
+ bsort(p, nat_local, snat_local);
79
+
80
+ bool tabu = false;
81
+ for (int k = 0; k < ntenure; ++k)
82
+ {
83
+ int ct = 0;
84
+ for (int iii = 0; iii < p; ++iii)
85
+ {
86
+ if (snat_local[iii] == Mat[iii][csubstitutionstotal + k])
87
+ ct++;
88
+ }
89
+ if (ct == p)
90
+ {
91
+ tabu = true;
92
+ break;
93
+ }
94
+ }
95
+ if (tabu)
96
+ continue;
97
+
98
+ for (int iii = 0; iii < p; ++iii)
99
+ b_local[iii] = (y[nat_local[iii]] >= 0.0 ? 1.0 : 0.0);
100
+
101
+ for (int iii = 0; iii < p; ++iii)
102
+ {
103
+ for (int jjj = 0; jjj < p; ++jjj)
104
+ {
105
+ A_local[jjj][iii] =
106
+ (1.0 - 2.0 * b_local[jjj]) *
107
+ Z[iii + 1][id[nat_local[jjj]]];
108
+ }
109
+ }
110
+
111
+ for (int iii = 0; iii < p; ++iii)
112
+ {
113
+ bb_local[iii] =
114
+ b0 * (-1.0 + 2.0 * b_local[iii]) *
115
+ Z[0][id[nat_local[iii]]];
116
+ }
117
+
118
+ gelim(p, A_local, bb_local, x_local);
119
+
120
+ bool infeasible = false;
121
+ for (int iii = 0; iii < p; ++iii)
122
+ {
123
+ if (x_local[iii] < xlbound[iii] ||
124
+ x_local[iii] > xubound[iii])
125
+ {
126
+ cinfsd_i++;
127
+ infeasible = true;
128
+ break;
129
+ }
130
+ }
131
+ if (infeasible)
132
+ continue;
133
+
134
+ double score1;
135
+ EvaluateScore1(
136
+ x_local, T, p, score1,
137
+ Z, b0, data_y
138
+ );
139
+
140
+ double dz = score1 - MaxScore;
141
+
142
+ if (dz > 0.0)
143
+ cbd_i++;
144
+
145
+ if (score1 > MaxScoreEver_i)
146
+ {
147
+ MaxScoreEver_i = score1;
148
+ for (int iii = 0; iii < p; ++iii)
149
+ natever_i[iii] = nat_local[iii];
150
+ }
151
+
152
+ if (dz > dz_i)
153
+ {
154
+ dz_i = dz;
155
+ for (int iii = 0; iii < p; ++iii)
156
+ natcur_i[iii] = nat_local[iii];
157
+ }
158
+ }
159
+ }
@@ -0,0 +1,60 @@
1
+ #include "tabu_search.hpp"
2
+
3
+ // -----------------------------------------------------------------------------
4
+ // gelim: Gaussian elimination (Fortran version preserved exactly)
5
+ // 0-based C++ translation of the Fortran routine:
6
+ //
7
+ // do i=1,np-1
8
+ // do j=i+1,np
9
+ // p = sk(j,i)/sk(i,i)
10
+ // r1(j) = r1(j) - p*r1(i)
11
+ // do k=1,np
12
+ // sk(j,k) = sk(j,k) - p*sk(i,k)
13
+ // enddo
14
+ // enddo
15
+ // enddo
16
+ //
17
+ // Back substitution, then restore original sk and r1
18
+ // -----------------------------------------------------------------------------
19
+ void gelim(int np,
20
+ double sk[pmax][pmax],
21
+ double r1[pmax],
22
+ double u[pmax])
23
+ {
24
+ double bsk[pmax][pmax];
25
+ double br1[pmax];
26
+
27
+ // Save original sk and r1 (Fortran behavior)
28
+ for (int i = 0; i < np; ++i) {
29
+ br1[i] = r1[i];
30
+ for (int j = 0; j < np; ++j)
31
+ bsk[i][j] = sk[i][j];
32
+ }
33
+
34
+ // Forward elimination
35
+ for (int i = 0; i < np - 1; ++i) {
36
+ for (int j = i + 1; j < np; ++j) {
37
+ double p = sk[j][i] / sk[i][i];
38
+ r1[j] -= p * r1[i];
39
+ for (int k = 0; k < np; ++k)
40
+ sk[j][k] -= p * sk[i][k];
41
+ }
42
+ }
43
+
44
+ // Back substitution
45
+ for (int l = np - 1; l >= 0; --l) {
46
+ u[l] = r1[l] / sk[l][l];
47
+ for (int j = l - 1; j >= 0; --j) {
48
+ double p1 = sk[j][l] / sk[l][l];
49
+ sk[j][l] = 0.0;
50
+ r1[j] -= p1 * r1[l];
51
+ }
52
+ }
53
+
54
+ // Restore original sk and r1 (Fortran semantics)
55
+ for (int i = 0; i < np; ++i) {
56
+ r1[i] = br1[i];
57
+ for (int j = 0; j < np; ++j)
58
+ sk[i][j] = bsk[i][j];
59
+ }
60
+ }
@@ -0,0 +1,274 @@
1
+ #include "tabu_search.hpp"
2
+
3
+ // Portable RNG
4
+ static inline double get_random() {
5
+ return double(rand()) / double(RAND_MAX);
6
+ }
7
+
8
+ // -----------------------------------------------------------------------------
9
+ // Public API entry point callable from Python (pybind11)
10
+ // -----------------------------------------------------------------------------
11
+ extern "C" int run_tabu_search(
12
+ double* X,
13
+ int T,
14
+ int p_in,
15
+ int* y_in,
16
+ double b0,
17
+ double d,
18
+ int iSeed, // <-- NEW ARGUMENT
19
+ int* out_attributes,
20
+ double* out_coeffs,
21
+ double* out_score
22
+ )
23
+ {
24
+ srand(iSeed);
25
+ //srand(12345); // or use time(NULL) if you want nondeterministic runs
26
+ //srand((unsigned)time(NULL)); //If you want randomness each run:
27
+
28
+ int p = p_in - 1; // number of attributes (excluding intercept)
29
+
30
+ static double Z[pmax][Tmax]; // now pmax and Tmax are known
31
+ static double y[Tmax];
32
+ static int data_y[Tmax];
33
+ static int id[Tmax];
34
+
35
+ if (p <= 0 || p > pmax)
36
+ return -10;
37
+ if (T <= 0 || T > Tmax)
38
+ return -11;
39
+
40
+ // Core data
41
+ /*double Z[pmax][Tmax];
42
+ double y[Tmax];
43
+ int data_y[Tmax];
44
+ int id[Tmax];*/
45
+
46
+ // Working arrays
47
+ double A[pmax][pmax], b[pmax], bb[pmax];
48
+ double mA[pmax][pmax], mb[pmax], bmb[pmax];
49
+ double x[pmax], score1, MaxScore0, MaxScore, MaxScoreEver;
50
+ double Af[pmax][pmax], bf[pmax], bbf[pmax];
51
+ double ArgMax[pmax];
52
+
53
+ int nat[pmax], natsol[pmax], natcur[pmax], natever[pmax];
54
+ int Mat[pmax][Itmax], snat[pmax], snatsol[pmax];
55
+
56
+ double xlbound[pmax], xubound[pmax];
57
+
58
+ int ntenure, niterover, niternotimproved;
59
+ int csubstitutionstotal, cbdsubseq, cbd, cinfsd;
60
+ double dzmax;
61
+ int row;
62
+
63
+ // -------------------------------------------------------------------------
64
+ // Copy inputs into local arrays
65
+ // -------------------------------------------------------------------------
66
+ for (int i = 0; i < T; ++i) {
67
+ id[i] = i;
68
+ data_y[i] = y_in[i];
69
+ y[i] = double(y_in[i]);
70
+ }
71
+
72
+ for (int j = 0; j < p_in; ++j) {
73
+ for (int i = 0; i < T; ++i) {
74
+ Z[j][i] = X[j*T + i];
75
+ }
76
+ }
77
+
78
+ // -------------------------------------------------------------------------
79
+ // Bounds (you can later tie these to d if desired)
80
+ // -------------------------------------------------------------------------
81
+ for (int j = 0; j < p; ++j) {
82
+ xlbound[j] = -d;
83
+ xubound[j] = d;
84
+ }
85
+
86
+ // -------------------------------------------------------------------------
87
+ // Initial nat (first p indices)
88
+ // -------------------------------------------------------------------------
89
+ /*for (int j = 0; j < p; ++j)
90
+ nat[j] = j;*/
91
+ for (int j = 0; j < p; ++j) {
92
+ double rr = get_random();
93
+ nat[j] = int(std::floor(rr * T));
94
+ }
95
+ printf("Initial nat: ");
96
+ for (int j = 0; j < p; ++j) printf("%d ", nat[j]);
97
+ printf("\n");
98
+
99
+
100
+ bsort(p, nat, snat);
101
+ for (int j = 0; j < p; ++j)
102
+ natsol[j] = snat[j];
103
+
104
+ // -------------------------------------------------------------------------
105
+ // Build mb
106
+ // -------------------------------------------------------------------------
107
+ for (int j = 0; j < p; ++j)
108
+ mb[j] = (y[id[natsol[j]]] >= 0.0 ? 1.0 : 0.0);
109
+
110
+ // -------------------------------------------------------------------------
111
+ // Build mA and bmb
112
+ // -------------------------------------------------------------------------
113
+ for (int j = 0; j < p; ++j)
114
+ for (int i = 0; i < p; ++i)
115
+ mA[i][j] = (1.0 - 2.0 * mb[i]) * Z[j + 1][ id[natsol[i]] ];
116
+
117
+ for (int i = 0; i < p; ++i)
118
+ bmb[i] = b0 * (-1.0 + 2.0 * mb[i]) * Z[0][ id[natsol[i]] ];
119
+
120
+ // -------------------------------------------------------------------------
121
+ // Solve initial system
122
+ // -------------------------------------------------------------------------
123
+ gelim(p, mA, bmb, x);
124
+
125
+ for (int i = 0; i < p; ++i) {
126
+ if (x[i] < xlbound[i] || x[i] > xubound[i]) {
127
+ return -2; // infeasible initial solution
128
+ }
129
+ }
130
+
131
+ // -------------------------------------------------------------------------
132
+ // Evaluate initial score
133
+ // -------------------------------------------------------------------------
134
+ EvaluateScore1(
135
+ x, T, p, score1,
136
+ Z, b0, data_y
137
+ );
138
+ MaxScore0 = score1;
139
+ MaxScore = MaxScore0;
140
+ MaxScoreEver = MaxScore0;
141
+
142
+ // -------------------------------------------------------------------------
143
+ // Tabu parameters
144
+ // -------------------------------------------------------------------------
145
+ ntenure = 200;
146
+ niterover = 200;
147
+ niternotimproved = 200;
148
+
149
+ for (int j = 0; j < p; ++j)
150
+ for (int i = 0; i < ntenure; ++i)
151
+ Mat[j][i] = -1;
152
+
153
+ csubstitutionstotal = 0;
154
+ cbdsubseq = 0;
155
+
156
+ // -------------------------------------------------------------------------
157
+ // Main tabu loop
158
+ // -------------------------------------------------------------------------
159
+ while (true) {
160
+
161
+ cbd = 0;
162
+ cinfsd = 0;
163
+ dzmax = -1.0;
164
+ row = 0;
165
+
166
+ bsort(p, natsol, snatsol);
167
+ for (int i = 0; i < p; ++i)
168
+ natsol[i] = snatsol[i];
169
+
170
+ for (int i = 0; i < p; ++i)
171
+ b[i] = (y[id[natsol[i]]] >= 0.0 ? 1.0 : 0.0);
172
+
173
+ for (int j = 0; j < p; ++j)
174
+ for (int i = 0; i < p; ++i)
175
+ A[i][j] = (1.0 - 2.0 * b[i]) * Z[j + 1][ id[natsol[i]] ];
176
+
177
+ for (int i = 0; i < p; ++i)
178
+ bb[i] = b0 * (-1.0 + 2.0 * b[i]) * Z[0][ id[natsol[i]] ];
179
+
180
+ int row_i_arr[pmax];
181
+ int cbd_i_arr[pmax];
182
+ int cinfsd_i_arr[pmax];
183
+ double dz_i_arr[pmax];
184
+ double MaxScoreEver_i_arr[pmax];
185
+ int natcur_i_arr[pmax][pmax];
186
+ int natever_i_arr[pmax][pmax];
187
+
188
+ #pragma omp parallel for default(shared)
189
+ for (int i = 0; i < p; ++i) {
190
+ evaluate_neighbourhood(
191
+ i, p, T,
192
+ natsol, Mat,
193
+ Z, y, id,
194
+ xlbound, xubound,
195
+ b0, MaxScore,
196
+ ntenure, csubstitutionstotal, T,
197
+ dz_i_arr[i], natcur_i_arr[i],
198
+ MaxScoreEver_i_arr[i], natever_i_arr[i],
199
+ row_i_arr[i], cbd_i_arr[i], cinfsd_i_arr[i],
200
+ data_y
201
+ );
202
+ }
203
+
204
+ for (int i = 0; i < p; ++i) {
205
+ row += row_i_arr[i];
206
+ cbd += cbd_i_arr[i];
207
+ cinfsd += cinfsd_i_arr[i];
208
+
209
+ if (MaxScoreEver_i_arr[i] > MaxScoreEver) {
210
+ MaxScoreEver = MaxScoreEver_i_arr[i];
211
+ for (int j = 0; j < p; ++j)
212
+ natever[j] = natever_i_arr[i][j];
213
+ }
214
+
215
+ if (dz_i_arr[i] > dzmax) {
216
+ dzmax = dz_i_arr[i];
217
+ for (int j = 0; j < p; ++j)
218
+ natcur[j] = natcur_i_arr[i][j];
219
+ }
220
+ }
221
+
222
+ csubstitutionstotal++;
223
+
224
+ if (cbd == 0)
225
+ cbdsubseq++;
226
+
227
+ if (csubstitutionstotal == niterover ||
228
+ cbdsubseq == niternotimproved)
229
+ break;
230
+
231
+ for (int j = 0; j < p; ++j)
232
+ natsol[j] = natcur[j];
233
+
234
+ MaxScore += dzmax;
235
+
236
+ bsort(p, natsol, snatsol);
237
+ for (int j = 0; j < p; ++j)
238
+ Mat[j][ntenure + csubstitutionstotal] = snatsol[j];
239
+
240
+ printf("iteration...%d...:",csubstitutionstotal);
241
+ printf("current natsol: ");
242
+ for (int j = 0; j < p; ++j) printf("%d ", natsol[j]);
243
+ printf("\n");
244
+
245
+ }
246
+
247
+ // -------------------------------------------------------------------------
248
+ // Final evaluation
249
+ // -------------------------------------------------------------------------
250
+ for (int j = 0; j < p; ++j)
251
+ bf[j] = (y[id[natever[j]]] >= 0.0 ? 1.0 : 0.0);
252
+
253
+ for (int j = 0; j < p; ++j)
254
+ for (int l = 0; l < p; ++l)
255
+ Af[l][j] = (1.0 - 2.0 * bf[l]) * Z[j + 1][ id[natever[l]] ];
256
+
257
+ for (int j = 0; j < p; ++j)
258
+ bbf[j] = b0 * (-1.0 + 2.0 * bf[j]) * Z[0][ id[natever[j]] ];
259
+
260
+ gelim(p, Af, bbf, x);
261
+ EvaluateScore1(
262
+ x, T, p, score1,
263
+ Z, b0, data_y
264
+ );
265
+
266
+ for (int j = 0; j < p; ++j) {
267
+ out_attributes[j] = natever[j];
268
+ out_coeffs[j] = x[j];
269
+ }
270
+
271
+ *out_score = score1;
272
+
273
+ return 0;
274
+ }
@@ -0,0 +1,86 @@
1
+ #include <pybind11/pybind11.h>
2
+ #include <pybind11/numpy.h>
3
+ #include "tabu_search.hpp"
4
+
5
+ extern "C" int run_tabu_search(
6
+ double* X,
7
+ int T,
8
+ int p_in,
9
+ int* y_in,
10
+ double b0,
11
+ double d,
12
+ int iSeed, // <-- NEW ARGUMENT
13
+ int* out_attributes,
14
+ double* out_coeffs,
15
+ double* out_score
16
+ );
17
+
18
+ namespace py = pybind11;
19
+
20
+ py::tuple py_run_tabu_search(py::array_t<double> X,
21
+ py::array_t<int> y,
22
+ double b0,
23
+ double d,
24
+ int iSeed)
25
+ {
26
+ if (X.ndim() != 2)
27
+ throw std::runtime_error("X must be 2D (p_in x T)");
28
+ if (y.ndim() != 1)
29
+ throw std::runtime_error("y must be 1D (T,)");
30
+
31
+ int p_in = static_cast<int>(X.shape(0));
32
+ int T = static_cast<int>(X.shape(1));
33
+
34
+ if (y.shape(0) != T)
35
+ throw std::runtime_error("y length must match X.shape[1]");
36
+
37
+ auto X_buf = X.request();
38
+ auto y_buf = y.request();
39
+
40
+ double* X_ptr = static_cast<double*>(X_buf.ptr);
41
+ int* y_ptr = static_cast<int*>(y_buf.ptr);
42
+
43
+ int p = p_in - 1;
44
+ if (p <= 0 || p > pmax)
45
+ throw std::runtime_error("p_in-1 must be in [1, pmax]");
46
+
47
+ py::array_t<int> attrs({p});
48
+ py::array_t<double> coeffs({p});
49
+ double score = 0.0;
50
+
51
+ auto attrs_buf = attrs.request();
52
+ auto coeffs_buf = coeffs.request();
53
+
54
+ int* attrs_ptr = static_cast<int*>(attrs_buf.ptr);
55
+ double* coeffs_ptr = static_cast<double*>(coeffs_buf.ptr);
56
+
57
+ int ret = run_tabu_search(
58
+ X_ptr,
59
+ T,
60
+ p_in,
61
+ y_ptr,
62
+ b0,
63
+ d,
64
+ iSeed, // <-- pass seed
65
+ attrs_ptr,
66
+ coeffs_ptr,
67
+ &score
68
+ );
69
+
70
+ if (ret != 0)
71
+ throw std::runtime_error("run_tabu_search failed with code " + std::to_string(ret));
72
+
73
+ return py::make_tuple(attrs, coeffs, score);
74
+ }
75
+
76
+ PYBIND11_MODULE(_hitspy, m) {
77
+ m.doc() = "Pybind11 bindings for hitspy discrete tabu search";
78
+
79
+ m.def("run_tabu_search", &py_run_tabu_search,
80
+ py::arg("X"),
81
+ py::arg("y"),
82
+ py::arg("b0"),
83
+ py::arg("d"),
84
+ py::arg("iSeed"),
85
+ "Run discrete tabu search with user-specified RNG seed.");
86
+ }
@@ -0,0 +1,69 @@
1
+ #ifndef TABU_SEARCH_HPP
2
+ #define TABU_SEARCH_HPP
3
+
4
+ #include <cstdio>
5
+ #include <cstdlib>
6
+ #include <cmath>
7
+ #include <cstring>
8
+ #include <algorithm>
9
+ #include <omp.h>
10
+
11
+ // -----------------------------------------------------------------------------
12
+ // Global constants (Fortran PARAMETER)
13
+ // -----------------------------------------------------------------------------
14
+ static const int Tmax = 50000;
15
+ static const int pmax = 20;
16
+ static const int Itmax = 1000;
17
+
18
+ // -----------------------------------------------------------------------------
19
+ // Function prototypes
20
+ // -----------------------------------------------------------------------------
21
+
22
+ // bsort: sort nat[0..np-1] into snat[0..np-1]
23
+ void bsort(int np, const int nat[], int snat[]);
24
+
25
+ // Gaussian elimination (Fortran gelim)
26
+ void gelim(int np,
27
+ double sk[pmax][pmax],
28
+ double r1[pmax],
29
+ double u[pmax]);
30
+
31
+ // EvaluateScore1: compute score h
32
+ void EvaluateScore1(
33
+ const double w[pmax],
34
+ int T,
35
+ int p,
36
+ double &h,
37
+ const double Z[pmax][Tmax],
38
+ double b0,
39
+ const int data_y[Tmax]
40
+ );
41
+
42
+ // evaluate_neighbourhood: OpenMP-parallelized neighborhood evaluation
43
+ void evaluate_neighbourhood(
44
+ int i_idx,
45
+ int p,
46
+ int n,
47
+ const int natsol[pmax],
48
+ const int Mat[pmax][Itmax],
49
+ const double Z[pmax][Tmax],
50
+ const double y[Tmax],
51
+ const int id[Tmax],
52
+ const double xlbound[pmax],
53
+ const double xubound[pmax],
54
+ double b0,
55
+ double MaxScore,
56
+ int ntenure,
57
+ int csubstitutionstotal,
58
+ int T,
59
+ double &dz_i,
60
+ int natcur_i[pmax],
61
+ double &MaxScoreEver_i,
62
+ int natever_i[pmax],
63
+ int &row_i,
64
+ int &cbd_i,
65
+ int &cinfsd_i,
66
+ const int data_y[Tmax]
67
+ );
68
+
69
+ #endif // TABU_SEARCH_HPP
@@ -0,0 +1,160 @@
1
+ Metadata-Version: 2.4
2
+ Name: hitspy
3
+ Version: 0.1.0
4
+ Summary: Fast OpenMP-parallelized discrete tabu search with pybind11 C++ bindings for maximum score estimator
5
+ Author-email: Kostas Florios <cflorios@aueb.gr>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/kflorios/hitspy
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: C++
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: numpy>=1.20.0
16
+ Dynamic: license-file
17
+
18
+ # hitspy
19
+
20
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
21
+
22
+ **hitspy** is a high-performance Python package providing C++ implementation of discrete Tabu Search algorithm for maximum score estimation. Powered by **pybind11** and parallelized with **OpenMP**, `hitspy` accelerates Hyperplanes Intersection evaluation and search routines across large search spaces.
23
+
24
+ ---
25
+
26
+ ## Key Features
27
+
28
+ - ⚡ **High Performance**: Core optimization routines written in C++ for maximum throughput.
29
+ - 🚀 **Parallel Computing**: OpenMP multi-threading support for fast neighborhood evaluations.
30
+ - 🐍 **Seamless Python API**: Native `numpy` array integration using `pybind11`.
31
+
32
+ ---
33
+
34
+ ## Installation
35
+
36
+ ### From Source (Locally)
37
+
38
+ Download the main folder of the package, cd to it and provide
39
+ in your preferred environment:
40
+
41
+ ```bash
42
+ pip install -e .
43
+ ```
44
+ ---
45
+
46
+ ### Requirements
47
+ Python 3.8 and above, MS Visual Studio 2019 and above
48
+
49
+ ## API Reference
50
+
51
+ ### `hitspy.run_tabu_search`
52
+
53
+ Executes an OpenMP-accelerated discrete Tabu Search algorithm for maximum score estimator computation.
54
+
55
+ ```python
56
+ hitspy.run_tabu_search(
57
+ X: numpy.ndarray,
58
+ y: numpy.ndarray,
59
+ b0: float,
60
+ d: float,
61
+ iSeed: int
62
+ ) -> numpy.ndarray
63
+ ```
64
+
65
+ ## Complete Example: Multi-Seed Benchmark (20 Random Starts)
66
+
67
+ This benchmark demonstrates how to execute 20 distinct random restarts using `hitspy`, summarize overall solution quality, and compute selection frequency statistics for each attribute.
68
+
69
+ ```python
70
+ import numpy as np
71
+ import pandas as pd
72
+ from hitspy import run_tabu_search
73
+
74
+ # 1. Load and preprocess data
75
+ X_raw = np.loadtxt("X.txt")
76
+ y_raw = np.loadtxt("y.txt")
77
+
78
+ X = X_raw[:, 1:]
79
+ y = y_raw[:, 1].astype(int)
80
+ X=np.transpose(X)
81
+ X = np.ascontiguousarray(X, dtype=np.float64)
82
+
83
+ # 2. Parameters
84
+ b0 = -1.0
85
+ d = 10000
86
+ num_runs = 20
87
+
88
+ # Generate 20 distinct random seeds
89
+ np.random.seed(42) # For reproducible seed generation
90
+ seeds = np.random.randint(0, 1000000, size=num_runs)
91
+
92
+ # 3. Storage for results
93
+ results_list = []
94
+
95
+ print("Starting 20 runs...")
96
+
97
+ # 4. Run loop
98
+ for i in range(num_runs):
99
+ print('run...:',i+1)
100
+ current_seed = int(seeds[i])
101
+
102
+ # Execute Tabu Search
103
+ #attrs, coeffs, score = run_tabu_search(X=X, y=y, b0=b0, d=d, iSeed=current_seed)
104
+ attrs, coeffs, score = run_tabu_search(X=X, y=y, b0=b0, d=d, iSeed=12345+i+1)
105
+
106
+ # hitspy returns 'attributes' as a 1D numpy array/list and 'score' / 'coeffs' as keys in the dict
107
+
108
+ # Save metrics
109
+ results_list.append(
110
+ {
111
+ "Run": i + 1,
112
+ "Seed": 12345+i+1,
113
+ "Score": score,
114
+ "Coeffs": coeffs,
115
+ "Attrs": attrs
116
+ }
117
+ )
118
+
119
+ # 5. Combine into a single DataFrame
120
+ results_df = pd.DataFrame(results_list)
121
+
122
+ # 6. Display results table
123
+ print(results_df.to_string(index=False))
124
+
125
+ # 7. Summary statistics
126
+ print("\n=== Summary Across 20 Runs ===")
127
+ print("Best Score Found: ", results_df["Score"].max())
128
+ print("Mean Score: ", results_df["Score"].mean())
129
+ print("Score Std Dev: ", results_df["Score"].std(ddof=1))
130
+ ```
131
+
132
+ ## Acknowledgements
133
+
134
+ I would like to thank Dr. **Alexandros Louka** and Professor **Yannis Bilias** for their valuable help and the shared ideas for the development of HITSpy. The development of the HITSpy software was supported by the Hellenic Foundation for Research and Innovation (H.F.R.I.) under the '2nd Call for H.F.R.I. Research Projects to support Post-Doctoral Researchers' (Project Number: 902).
135
+
136
+ ## Citation
137
+
138
+ If you use `hitspy` in your research, please cite the underlying methodology papers:
139
+
140
+ ```bibtex
141
+ @article{florios2025hits,
142
+ title={HITS: Hyperplanes intersection tabu search for maximum score estimation},
143
+ author={Florios, Kostas and Louka, Alexandros and Bilias, Yannis},
144
+ journal={SoftwareX},
145
+ volume={30},
146
+ pages={102164},
147
+ year={2025},
148
+ publisher={Elsevier}
149
+ }
150
+
151
+ @article{manski1975maximum,
152
+ title={Maximum score estimation of the stochastic utility model of choice},
153
+ author={Manski, Charles F},
154
+ journal={Journal of econometrics},
155
+ volume={3},
156
+ number={3},
157
+ pages={205--228},
158
+ year={1975},
159
+ publisher={Elsevier}
160
+ }
@@ -0,0 +1,25 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ setup.py
6
+ src/hitspy/EvaluateScore1.cpp
7
+ src/hitspy/bsort.cpp
8
+ src/hitspy/evaluate_neighbourhood.cpp
9
+ src/hitspy/gelim.cpp
10
+ src/hitspy/tabu_api.cpp
11
+ src/hitspy/tabu_bindings.cpp
12
+ src/hitspy/EvaluateScore1.cpp
13
+ src/hitspy/__init__.py
14
+ src/hitspy/bsort.cpp
15
+ src/hitspy/evaluate_neighbourhood.cpp
16
+ src/hitspy/gelim.cpp
17
+ src/hitspy/tabu_api.cpp
18
+ src/hitspy/tabu_bindings.cpp
19
+ src/hitspy/tabu_search.hpp
20
+ src/hitspy.egg-info/PKG-INFO
21
+ src/hitspy.egg-info/SOURCES.txt
22
+ src/hitspy.egg-info/dependency_links.txt
23
+ src/hitspy.egg-info/requires.txt
24
+ src/hitspy.egg-info/top_level.txt
25
+ tests/test_tabu.py
@@ -0,0 +1 @@
1
+ numpy>=1.20.0
@@ -0,0 +1 @@
1
+ hitspy
@@ -0,0 +1 @@
1
+