-
Notifications
You must be signed in to change notification settings - Fork 56
/
SimpleFastPreferenceData.java
262 lines (228 loc) · 9.26 KB
/
SimpleFastPreferenceData.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/*
* Copyright (C) 2015 Information Retrieval Group at Universidad Autónoma
* de Madrid, http://ir.ii.uam.es
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package es.uam.eps.ir.ranksys.fast.preference;
import es.uam.eps.ir.ranksys.core.preference.IdPref;
import es.uam.eps.ir.ranksys.fast.index.FastItemIndex;
import es.uam.eps.ir.ranksys.fast.index.FastUserIndex;
import java.io.Serializable;
import java.util.ArrayList;
import static java.util.Comparator.comparingInt;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.jooq.lambda.function.Function4;
import org.jooq.lambda.tuple.Tuple3;
import org.jooq.lambda.tuple.Tuple4;
import org.ranksys.fast.preference.FastPointWisePreferenceData;
import org.ranksys.fast.preference.StreamsAbstractFastPreferenceData;
/**
* Simple implementation of FastPreferenceData backed by nested lists.
*
* @param <U> type of the users
* @param <I> type of the items
* @author Saúl Vargas (saul.vargas@uam.es)
*/
public class SimpleFastPreferenceData<U, I> extends StreamsAbstractFastPreferenceData<U, I> implements FastPointWisePreferenceData<U, I>, Serializable {
private final int numPreferences;
private final List<List<IdxPref>> uidxList;
private final List<List<IdxPref>> iidxList;
/**
* Constructor with default IdxPref to IdPref converter.
*
* @param numPreferences number of total preferences
* @param uidxList list of lists of preferences by user index
* @param iidxList list of lists of preferences by item index
* @param uIndex user index
* @param iIndex item index
*/
protected SimpleFastPreferenceData(int numPreferences, List<List<IdxPref>> uidxList, List<List<IdxPref>> iidxList,
FastUserIndex<U> uIndex, FastItemIndex<I> iIndex) {
this(numPreferences, uidxList, iidxList, uIndex, iIndex,
(Function<IdxPref, IdPref<I>> & Serializable) p -> new IdPref<>(iIndex.iidx2item(p)),
(Function<IdxPref, IdPref<U>> & Serializable) p -> new IdPref<>(uIndex.uidx2user(p)));
}
/**
* Constructor with custom IdxPref to IdPref converter.
*
* @param numPreferences number of total preferences
* @param uidxList list of lists of preferences by user index
* @param iidxList list of lists of preferences by item index
* @param uIndex user index
* @param iIndex item index
* @param uPrefFun user IdxPref to IdPref converter
* @param iPrefFun item IdxPref to IdPref converter
*/
protected SimpleFastPreferenceData(int numPreferences, List<List<IdxPref>> uidxList, List<List<IdxPref>> iidxList,
FastUserIndex<U> uIndex, FastItemIndex<I> iIndex,
Function<IdxPref, IdPref<I>> uPrefFun, Function<IdxPref, IdPref<U>> iPrefFun) {
super(uIndex, iIndex, uPrefFun, iPrefFun);
this.numPreferences = numPreferences;
this.uidxList = uidxList;
this.iidxList = iidxList;
uidxList.parallelStream()
.filter(Objects::nonNull)
.forEach(l -> l.sort(comparingInt(IdxPref::v1)));
iidxList.parallelStream()
.filter(Objects::nonNull)
.forEach(l -> l.sort(comparingInt(IdxPref::v1)));
}
@Override
public int numUsers(int iidx) {
if (iidxList.get(iidx) == null) {
return 0;
}
return iidxList.get(iidx).size();
}
@Override
public int numItems(int uidx) {
if (uidxList.get(uidx) == null) {
return 0;
}
return uidxList.get(uidx).size();
}
@Override
public Stream<IdxPref> getUidxPreferences(int uidx) {
if (uidxList.get(uidx) == null) {
return Stream.empty();
} else {
return uidxList.get(uidx).stream();
}
}
@Override
public Stream<IdxPref> getIidxPreferences(int iidx) {
if (iidxList.get(iidx) == null) {
return Stream.empty();
} else {
return iidxList.get(iidx).stream();
}
}
@Override
public int numPreferences() {
return numPreferences;
}
@Override
public IntStream getUidxWithPreferences() {
return IntStream.range(0, numUsers())
.filter(uidx -> uidxList.get(uidx) != null);
}
@Override
public IntStream getIidxWithPreferences() {
return IntStream.range(0, numItems())
.filter(iidx -> iidxList.get(iidx) != null);
}
@Override
public int numUsersWithPreferences() {
return (int) uidxList.stream()
.filter(Objects::nonNull).count();
}
@Override
public int numItemsWithPreferences() {
return (int) iidxList.stream()
.filter(Objects::nonNull).count();
}
@Override
public Optional<IdxPref> getPreference(int uidx, int iidx) {
List<IdxPref> uList = uidxList.get(uidx);
int low = 0;
int high = uList.size() - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
IdxPref p = uList.get(mid);
int cmp = Integer.compare(p.v1, iidx);
if (cmp < 0) {
low = mid + 1;
} else if (cmp > 0) {
high = mid - 1;
} else {
return Optional.of(p);
}
}
return Optional.empty();
}
@Override
public Optional<? extends IdPref<I>> getPreference(U u, I i) {
Optional<? extends IdxPref> pref = getPreference(user2uidx(u), item2iidx(i));
if (!pref.isPresent()) {
return Optional.empty();
} else {
return Optional.of(uPrefFun.apply(pref.get()));
}
}
/**
* Loads a SimpleFastPreferenceData from a stream of user-item-value triples.
*
* @param <U> user type
* @param <I> item type
* @param tuples stream of user-item-value triples
* @param uIndex user index
* @param iIndex item index
* @return an instance of SimpleFastPreferenceData containing the data from the input stream
*/
public static <U, I> SimpleFastPreferenceData<U, I> load(Stream<Tuple3<U, I, Double>> tuples, FastUserIndex<U> uIndex, FastItemIndex<I> iIndex) {
return load(tuples.map(t -> t.concat((Void) null)),
(uidx, iidx, v, o) -> new IdxPref(iidx, v),
(uidx, iidx, v, o) -> new IdxPref(uidx, v),
uIndex, iIndex,
(Function<IdxPref, IdPref<I>> & Serializable) p -> new IdPref<>(iIndex.iidx2item(p)),
(Function<IdxPref, IdPref<U>> & Serializable) p -> new IdPref<>(uIndex.uidx2user(p)));
}
/**
* Loads a SimpleFastPreferenceData from a stream of user-item-value-other tuples. It can accomodate other information, thus you need to provide sub-classes of IdxPref IdPref accomodating for this new information.
*
* @param <U> user type
* @param <I> item type
* @param <O> additional information type
* @param tuples stream of user-item-value-other tuples
* @param uIdxPrefFun converts a tuple to a user IdxPref
* @param iIdxPrefFun converts a tuple to a item IdxPref
* @param uIndex user index
* @param iIndex item index
* @param uIdPrefFun user IdxPref to IdPref converter
* @param iIdPrefFun item IdxPref to IdPref converter
* @return an instance of SimpleFastPreferenceData containing the data from the input stream
*/
public static <U, I, O> SimpleFastPreferenceData<U, I> load(Stream<Tuple4<U, I, Double, O>> tuples,
Function4<Integer, Integer, Double, O, ? extends IdxPref> uIdxPrefFun,
Function4<Integer, Integer, Double, O, ? extends IdxPref> iIdxPrefFun,
FastUserIndex<U> uIndex, FastItemIndex<I> iIndex,
Function<IdxPref, IdPref<I>> uIdPrefFun,
Function<IdxPref, IdPref<U>> iIdPrefFun) {
AtomicInteger numPreferences = new AtomicInteger();
List<List<IdxPref>> uidxList = new ArrayList<>();
for (int uidx = 0; uidx < uIndex.numUsers(); uidx++) {
uidxList.add(null);
}
List<List<IdxPref>> iidxList = new ArrayList<>();
for (int iidx = 0; iidx < iIndex.numItems(); iidx++) {
iidxList.add(null);
}
tuples.forEach(t -> {
int uidx = uIndex.user2uidx(t.v1);
int iidx = iIndex.item2iidx(t.v2);
numPreferences.incrementAndGet();
List<IdxPref> uList = uidxList.get(uidx);
if (uList == null) {
uList = new ArrayList<>();
uidxList.set(uidx, uList);
}
uList.add(uIdxPrefFun.apply(uidx, iidx, t.v3, t.v4));
List<IdxPref> iList = iidxList.get(iidx);
if (iList == null) {
iList = new ArrayList<>();
iidxList.set(iidx, iList);
}
iList.add(iIdxPrefFun.apply(uidx, iidx, t.v3, t.v4));
});
return new SimpleFastPreferenceData<>(numPreferences.intValue(), uidxList, iidxList, uIndex, iIndex, uIdPrefFun, iIdPrefFun);
}
}