-
Notifications
You must be signed in to change notification settings - Fork 0
Merge implems #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8e1285c
18550e2
bd72b96
c73a3a5
577130b
a6eea42
8cf7855
2e51dfd
6660968
b57ec9d
a0c2de5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -172,3 +172,6 @@ cython_debug/ | |
|
|
||
| # PyPI configuration file | ||
| .pypirc | ||
|
|
||
| # Mac OS | ||
| .DS_Store | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import sys | ||
|
|
||
| print(sys.executable) | ||
| print("ole") | ||
| print(sys.version) | ||
| import matplotlib.pyplot as plt | ||
| import gurobipy as hp | ||
| sys.path.append("../") | ||
|
|
||
|
|
||
| import pickle | ||
| import numpy as np | ||
|
|
||
| from python.data_generation import SyntheticDataGenerator | ||
| from python.distances import TwoUTASpaceDiameter | ||
|
|
||
| alldist = {} | ||
| for ndata in [10, 20, 100, 1_000]: | ||
| print("Nb of data:", ndata) | ||
| generator = SyntheticDataGenerator( | ||
| n_dms=2, | ||
| n_criteria=4, | ||
| method_params={"n_pieces": 5}, | ||
| ) | ||
|
|
||
| X, Y, info = generator.generate_preferences(num_pairs=ndata, return_clusters=True) | ||
| dist = TwoUTASpaceDiameter(n_pieces=5) | ||
| dist.fit(X, Y) | ||
|
|
||
| alldist[ndata] = { | ||
| "s1": [[dist.marginal_coeffs["s1", i, k].x for k in range(6)] for i in range(4)], | ||
| "s2": [[dist.marginal_coeffs["s2", i, k].x for k in range(6)] for i in range(4)], | ||
| "d1": [[dist.marginal_coeffs["d1", i, k].x for k in range(6)] for i in range(4)], | ||
| "d2": [[dist.marginal_coeffs["d2", i, k].x for k in range(6)] for i in range(4)], | ||
| "objval": dist.solver.objVal | ||
| } | ||
|
|
||
| with open('filename_bis.pickle', 'wb') as handle: | ||
| pickle.dump(alldist, handle, protocol=pickle.HIGHEST_PROTOCOL) |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,171 @@ | ||||||||||||||||||
| import numpy as np | ||||||||||||||||||
|
|
||||||||||||||||||
| from .decision_maker import DecisionMaker | ||||||||||||||||||
|
|
||||||||||||||||||
| class SyntheticDataGenerator: | ||||||||||||||||||
| def __init__( | ||||||||||||||||||
| self, | ||||||||||||||||||
| n_dms, | ||||||||||||||||||
| n_criteria, | ||||||||||||||||||
| mix_decisions=False, | ||||||||||||||||||
| method_params={}, | ||||||||||||||||||
| noise=0.0, | ||||||||||||||||||
| gap=0.0, | ||||||||||||||||||
| decimals=6, | ||||||||||||||||||
| ): | ||||||||||||||||||
| self.n_dms = n_dms | ||||||||||||||||||
| self.n_criteria = n_criteria | ||||||||||||||||||
| self.mix_decisions = mix_decisions | ||||||||||||||||||
| self.method_params = method_params | ||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To avoid issues with mutable default arguments, initialize
Suggested change
|
||||||||||||||||||
| self.noise = noise # % of noise = % of pairs that will be reversed | ||||||||||||||||||
| self.gap = gap | ||||||||||||||||||
| self.decimals = decimals | ||||||||||||||||||
|
|
||||||||||||||||||
| self.instantiate() | ||||||||||||||||||
|
|
||||||||||||||||||
| def instantiate(self): | ||||||||||||||||||
| self.dms = [DecisionMaker(n_criteria=self.n_criteria, n_pieces=self.method_params.get("n_pieces", 5)) for _ in range(self.n_dms)] | ||||||||||||||||||
|
|
||||||||||||||||||
| self._marginal_utilities = lambda x: np.array([[dm.get_marginal_utility(criterion_index=i, criterion_value=x[i]) for i in range(len(x))] for dm in self.dms]) | ||||||||||||||||||
| self._utility = lambda x: np.array([dm.get_total_utility(criteria_vector=x) for dm in self.dms]) | ||||||||||||||||||
|
|
||||||||||||||||||
| def utility(self, X): | ||||||||||||||||||
| if len(X.shape) == 1: | ||||||||||||||||||
| return self._utility(X) | ||||||||||||||||||
| elif len(X.shape) == 2: | ||||||||||||||||||
| return np.array([self._utility(x) for x in X]) | ||||||||||||||||||
| else: | ||||||||||||||||||
| raise ValueError("Unsupported shape of X", X.shape) | ||||||||||||||||||
|
|
||||||||||||||||||
| def marginal_utilities(self, X): | ||||||||||||||||||
| if len(X.shape) == 1: | ||||||||||||||||||
| return self._marginal_utility(X) | ||||||||||||||||||
| elif len(X.shape) == 2: | ||||||||||||||||||
| return np.array([self._marginal_utility(x) for x in X]) | ||||||||||||||||||
| else: | ||||||||||||||||||
| raise ValueError("Unsupported shape of X", X.shape) | ||||||||||||||||||
|
|
||||||||||||||||||
| def generate_preferences( | ||||||||||||||||||
| self, num_pairs, return_utilities=False, return_clusters=False, verbose=0 | ||||||||||||||||||
| ): | ||||||||||||||||||
| X, Y = [], [] | ||||||||||||||||||
|
|
||||||||||||||||||
| utilities = [[], []] | ||||||||||||||||||
| clusters = [] | ||||||||||||||||||
| # Useless now that we have clusters | ||||||||||||||||||
| populations = [0] * self.n_dms | ||||||||||||||||||
| if not isinstance(num_pairs, list): | ||||||||||||||||||
| num_pairs = [np.ceil(num_pairs / self.n_dms)] * self.n_dms | ||||||||||||||||||
|
Comment on lines
+55
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a couple of issues here:
Suggested change
|
||||||||||||||||||
| while len(X) < sum(num_pairs): | ||||||||||||||||||
| if verbose > 0: | ||||||||||||||||||
| print(f"{len(X)} events have been created as of now", end="\r") | ||||||||||||||||||
|
|
||||||||||||||||||
| non_dominance = False | ||||||||||||||||||
| while not non_dominance: | ||||||||||||||||||
| x = np.around( | ||||||||||||||||||
| np.random.uniform(0, 1, self.n_criteria), decimals=self.decimals | ||||||||||||||||||
| ) | ||||||||||||||||||
| y = np.around( | ||||||||||||||||||
| np.random.uniform(0, 1, self.n_criteria), decimals=self.decimals | ||||||||||||||||||
| ) | ||||||||||||||||||
| non_dominance = (np.sum(x-y > 0) != len(x)) & (np.sum(x-y > 0) != 0) | ||||||||||||||||||
|
|
||||||||||||||||||
| ux = np.around(self.utility(x), decimals=self.decimals) | ||||||||||||||||||
| uy = np.around(self.utility(y), decimals=self.decimals) | ||||||||||||||||||
| if (ux - uy)[np.argmax(ux - uy)] > self.gap: | ||||||||||||||||||
| if np.sum(ux > uy) == 1 and not self.mix_decisions: | ||||||||||||||||||
| if populations[np.argmax(ux > uy)] < num_pairs[np.argmax(ux > uy)]: | ||||||||||||||||||
| if np.random.randint(1000) / 1000 >= self.noise: | ||||||||||||||||||
| X.append(x) | ||||||||||||||||||
| Y.append(y) | ||||||||||||||||||
| utilities[0].append(ux) | ||||||||||||||||||
| utilities[1].append(uy) | ||||||||||||||||||
| else: | ||||||||||||||||||
| X.append(y) | ||||||||||||||||||
| Y.append(x) | ||||||||||||||||||
| utilities[0].append(uy) | ||||||||||||||||||
| utilities[1].append(ux) | ||||||||||||||||||
|
|
||||||||||||||||||
| populations[np.argmax(ux - uy)] += 1 | ||||||||||||||||||
| clusters.append(np.argmax(ux - uy)) | ||||||||||||||||||
|
|
||||||||||||||||||
| elif np.sum(ux > uy) >= 1 and self.mix_decisions: | ||||||||||||||||||
| if populations[np.argmax(ux - uy)] < num_pairs[np.argmax(ux - uy)]: | ||||||||||||||||||
| if np.random.randint(1000) / 1000 >= self.noise: | ||||||||||||||||||
| X.append(x) | ||||||||||||||||||
| Y.append(y) | ||||||||||||||||||
| utilities[0].append(ux) | ||||||||||||||||||
| utilities[1].append(uy) | ||||||||||||||||||
| else: | ||||||||||||||||||
| X.append(y) | ||||||||||||||||||
| Y.append(x) | ||||||||||||||||||
| utilities[0].append(uy) | ||||||||||||||||||
| utilities[1].append(ux) | ||||||||||||||||||
|
|
||||||||||||||||||
| populations[np.argmax(ux - uy)] += 1 | ||||||||||||||||||
| clusters.append(np.argmax(ux - uy)) | ||||||||||||||||||
|
Comment on lines
+76
to
+106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code inside the Additionally, |
||||||||||||||||||
| if verbose > 0: | ||||||||||||||||||
| print("Clusters Populations", populations) | ||||||||||||||||||
| additional_info = {} | ||||||||||||||||||
| for i in range(self.n_dms): | ||||||||||||||||||
| additional_info[f"coefficients_{i}"] = self.dms[i].coefficients | ||||||||||||||||||
|
|
||||||||||||||||||
| if return_utilities: | ||||||||||||||||||
| additional_info["utilities_x"] = np.array(utilities)[0] | ||||||||||||||||||
| additional_info["utilities_y"] = np.array(utilities)[1] | ||||||||||||||||||
| if return_clusters: | ||||||||||||||||||
| additional_info["clusters"] = np.array(clusters) | ||||||||||||||||||
| return np.stack(X), np.stack(Y), additional_info | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def generate_indifferences( | ||||||||||||||||||
| self, num_pairs, return_utilities=False, return_clusters=False, verbose=0 | ||||||||||||||||||
| ): | ||||||||||||||||||
| X, Y = [], [] | ||||||||||||||||||
|
|
||||||||||||||||||
| utilities = [[], []] | ||||||||||||||||||
| clusters = [] | ||||||||||||||||||
| # Useless now that we have clusters | ||||||||||||||||||
| populations = [0] * self.n_dms | ||||||||||||||||||
| if not isinstance(num_pairs, list): | ||||||||||||||||||
| num_pairs = np.array([np.ceil(num_pairs / self.n_dms)] * self.n_dms).astype(int) | ||||||||||||||||||
|
|
||||||||||||||||||
| for i in range(self.n_dms): | ||||||||||||||||||
| for _ in range(num_pairs[i]): | ||||||||||||||||||
| if verbose > 0: | ||||||||||||||||||
| print(f"{len(X)} events have been created as of now", end="\r") | ||||||||||||||||||
| x = np.around( | ||||||||||||||||||
| np.random.uniform(0, 1, self.n_criteria), decimals=self.decimals | ||||||||||||||||||
| ) | ||||||||||||||||||
| ux = np.around(self.utility(x), decimals=self.decimals)[0] | ||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The utility
Suggested change
|
||||||||||||||||||
| y = x.copy() | ||||||||||||||||||
|
|
||||||||||||||||||
| uyi_1 = None | ||||||||||||||||||
| while uyi_1 is None: | ||||||||||||||||||
| indexes = np.random.permutation(np.arange(len(x)))[:2] | ||||||||||||||||||
|
|
||||||||||||||||||
| uyi_0 = np.random.uniform(0, 1) | ||||||||||||||||||
| uyi_1 = self.dms[i].get_indifference_on_two_criteria(criterion_i=indexes[0], criterion_j=indexes[1], | ||||||||||||||||||
| query_i=x[indexes[0]], p_i=x[indexes[1]], query_j=uyi_0) | ||||||||||||||||||
|
|
||||||||||||||||||
| y[indexes[0]] = uyi_0 | ||||||||||||||||||
| y[indexes[1]] = uyi_1 | ||||||||||||||||||
|
|
||||||||||||||||||
| X.append(x) | ||||||||||||||||||
| Y.append(y) | ||||||||||||||||||
| utilities[0].append(ux) | ||||||||||||||||||
| utilities[1].append(np.around(self.utility(y), decimals=self.decimals)[i]) | ||||||||||||||||||
| populations[i] += 1 | ||||||||||||||||||
| clusters.append(i) | ||||||||||||||||||
| if verbose > 0: | ||||||||||||||||||
| print("Clusters Populations", populations) | ||||||||||||||||||
| additional_info = {} | ||||||||||||||||||
| for i in range(self.n_dms): | ||||||||||||||||||
| additional_info[f"coefficients_{i}"] = self.dms[i].coefficients | ||||||||||||||||||
|
|
||||||||||||||||||
| if return_utilities: | ||||||||||||||||||
| additional_info["utilities_x"] = np.array(utilities)[0] | ||||||||||||||||||
| additional_info["utilities_y"] = np.array(utilities)[1] | ||||||||||||||||||
| if return_clusters: | ||||||||||||||||||
| additional_info["clusters"] = np.array(clusters) | ||||||||||||||||||
| return np.stack(X), np.stack(Y), additional_info | ||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a mutable default argument like
{}can lead to unexpected behavior where modifications to the dictionary in one instance affect all others. It's safer to useNoneas the default and then initializeself.method_paramstomethod_params if method_params is not None else {}inside__init__.