1 | package genius.core.logging;
|
---|
2 |
|
---|
3 | import static java.lang.String.format;
|
---|
4 |
|
---|
5 | import java.util.List;
|
---|
6 |
|
---|
7 | import java.util.ArrayList;
|
---|
8 | import java.util.Arrays;
|
---|
9 | import java.util.Collection;
|
---|
10 | import java.util.Collections;
|
---|
11 | import java.util.Iterator;
|
---|
12 |
|
---|
13 | import java.io.Closeable;
|
---|
14 | import java.io.File;
|
---|
15 | import java.io.FileNotFoundException;
|
---|
16 | import java.io.IOException;
|
---|
17 | import java.io.PrintStream;
|
---|
18 |
|
---|
19 | import genius.core.Bid;
|
---|
20 | import genius.core.analysis.MultilateralAnalysis;
|
---|
21 | import genius.core.parties.AbstractNegotiationParty;
|
---|
22 | import genius.core.parties.NegotiationParty;
|
---|
23 | import genius.core.parties.NegotiationPartyInternal;
|
---|
24 | import genius.core.protocol.MediatorProtocol;
|
---|
25 | import genius.core.protocol.MultilateralProtocol;
|
---|
26 | import genius.core.session.Session;
|
---|
27 | import genius.core.utility.AbstractUtilitySpace;
|
---|
28 |
|
---|
29 | /**
|
---|
30 | * Logger interface that writes the log to a comma separated value file (.csv
|
---|
31 | * file) File is created upon logger interface creation and logger class should
|
---|
32 | * be released (i.e. log.close() to release internal file handle when done with
|
---|
33 | * logger). *
|
---|
34 | *
|
---|
35 | * @author David Festen
|
---|
36 | */
|
---|
37 | public class CsvLogger implements Closeable {
|
---|
38 | // use "," for english and ";" for dutch excel readable output
|
---|
39 | public static final String DELIMITER = ";";
|
---|
40 |
|
---|
41 | // The internal print stream used for file writing
|
---|
42 | PrintStream ps;
|
---|
43 |
|
---|
44 | // The buffer of objects to write (we only do a write call per complete
|
---|
45 | // line)
|
---|
46 | List<Object> buffer;
|
---|
47 |
|
---|
48 | // Flag to indicate if header is already printed (we will print the header
|
---|
49 | // only once)
|
---|
50 | boolean printedHeader;
|
---|
51 |
|
---|
52 | /**
|
---|
53 | * Initializes a new instance of the CsvLogger class. Initializing this
|
---|
54 | * class opens a print stream, the user of the instance should take care to
|
---|
55 | * close (i.e. logger.close()) this instance when done.
|
---|
56 | *
|
---|
57 | * @param fileName
|
---|
58 | * The name of the file to log to (including the .csv extension)
|
---|
59 | * @throws FileNotFoundException
|
---|
60 | * Thrown by the PrintStream if the location is not writable.
|
---|
61 | */
|
---|
62 | public CsvLogger(String fileName) throws IOException {
|
---|
63 | File file = new File(fileName);
|
---|
64 | file.getParentFile().mkdirs();
|
---|
65 | ps = new PrintStream(file);
|
---|
66 | buffer = new ArrayList<Object>();
|
---|
67 |
|
---|
68 | // Used to tell excel to handle file correctly.
|
---|
69 | logLine("sep=" + DELIMITER);
|
---|
70 | }
|
---|
71 |
|
---|
72 | /**
|
---|
73 | * Helper method. Joins all the elements in the collection using the given
|
---|
74 | * delimiter. For each element the to string function is used to generate
|
---|
75 | * the string.
|
---|
76 | *
|
---|
77 | * @param s
|
---|
78 | * Collection of objects to create string of
|
---|
79 | * @param delimiter
|
---|
80 | * The delimiter used between object
|
---|
81 | * @return The string delimited with the given delimiter
|
---|
82 | */
|
---|
83 | public static String join(Collection<?> s, String delimiter) {
|
---|
84 | StringBuilder builder = new StringBuilder();
|
---|
85 | Iterator<?> iterator = s.iterator();
|
---|
86 | while (iterator.hasNext()) {
|
---|
87 | builder.append(iterator.next());
|
---|
88 | if (!iterator.hasNext()) {
|
---|
89 | break;
|
---|
90 | }
|
---|
91 | builder.append(delimiter);
|
---|
92 | }
|
---|
93 | return builder.toString();
|
---|
94 | }
|
---|
95 |
|
---|
96 | /**
|
---|
97 | * Log a given object. This actually just adds it to the buffer, to print to
|
---|
98 | * file, call logLine() afterwards.
|
---|
99 | *
|
---|
100 | * @param value
|
---|
101 | * The object to log
|
---|
102 | */
|
---|
103 | public void log(Object value) {
|
---|
104 | buffer.add(value);
|
---|
105 | }
|
---|
106 |
|
---|
107 | /**
|
---|
108 | * Logs a complete line to the file.
|
---|
109 | *
|
---|
110 | * @param values
|
---|
111 | * zero or more objects to log, using ; delimiter
|
---|
112 | */
|
---|
113 | public void logLine(Object... values) {
|
---|
114 | buffer.addAll(Arrays.asList(values));
|
---|
115 | String line = join(buffer, DELIMITER);
|
---|
116 | ps.println(line);
|
---|
117 | buffer.clear();
|
---|
118 | }
|
---|
119 |
|
---|
120 | /**
|
---|
121 | * Log default session information. Seems applicable only when an agreement
|
---|
122 | * was reached.
|
---|
123 | */
|
---|
124 | public static String getDefaultSessionLog(Session session,
|
---|
125 | MultilateralProtocol protocol,
|
---|
126 | List<NegotiationPartyInternal> partiesint, double runTime)
|
---|
127 | throws Exception {
|
---|
128 | List<String> values = new ArrayList<String>();
|
---|
129 | List<NegotiationParty> parties = new ArrayList<NegotiationParty>();
|
---|
130 | for (NegotiationPartyInternal p : partiesint) {
|
---|
131 | parties.add(p.getParty());
|
---|
132 | }
|
---|
133 |
|
---|
134 | try {
|
---|
135 | Bid agreement = protocol.getCurrentAgreement(session, parties);
|
---|
136 | values.add(format("%.3f", runTime));
|
---|
137 | values.add("" + (session.getRoundNumber() + 1));
|
---|
138 |
|
---|
139 | // round / time
|
---|
140 | values.add(session.getDeadlines().toString());
|
---|
141 |
|
---|
142 | // discounted and agreement
|
---|
143 | boolean isDiscounted = false;
|
---|
144 | for (NegotiationPartyInternal party : partiesint)
|
---|
145 | isDiscounted |= (party.getUtilitySpace().discount(1, 1) != 1);
|
---|
146 | values.add(agreement == null ? "No" : "Yes");
|
---|
147 | values.add(isDiscounted ? "Yes" : "No");
|
---|
148 |
|
---|
149 | // number of agreeing parties
|
---|
150 | List<NegotiationParty> agents = MediatorProtocol
|
---|
151 | .getNonMediators(parties);
|
---|
152 | values.add(
|
---|
153 | "" + protocol.getNumberOfAgreeingParties(session, agents));
|
---|
154 |
|
---|
155 | // discounted min and max utility
|
---|
156 | List<Double> utils = getUtils(partiesint, agreement, true);
|
---|
157 | values.add(format("%.5f", Collections.min(utils)));
|
---|
158 | values.add(format("%.5f", Collections.max(utils)));
|
---|
159 |
|
---|
160 | // analysis (distances, social welfare, etc)
|
---|
161 | MultilateralAnalysis analysis = new MultilateralAnalysis(partiesint,
|
---|
162 | protocol.getCurrentAgreement(session, parties),
|
---|
163 | session.getTimeline().getTime());
|
---|
164 | values.add(format("%.5f", analysis.getDistanceToPareto()));
|
---|
165 | values.add(format("%.5f", analysis.getDistanceToNash()));
|
---|
166 | values.add(format("%.5f", analysis.getSocialWelfare()));
|
---|
167 |
|
---|
168 | // enumerate agents names, utils, protocols
|
---|
169 | for (NegotiationPartyInternal agent : partiesint)
|
---|
170 | values.add("" + agent);
|
---|
171 | for (double util : utils)
|
---|
172 | values.add(format("%.5f", util));
|
---|
173 | for (NegotiationPartyInternal agent : partiesint) {
|
---|
174 | String name = "-";
|
---|
175 | if (agent.getUtilitySpace() instanceof AbstractUtilitySpace) {
|
---|
176 | name = stripPath(
|
---|
177 | ((AbstractUtilitySpace) agent.getUtilitySpace())
|
---|
178 | .getFileName());
|
---|
179 | }
|
---|
180 | values.add(name);
|
---|
181 |
|
---|
182 | }
|
---|
183 |
|
---|
184 | } catch (Exception e) {
|
---|
185 | values.add("EXCEPTION OCCURRED");
|
---|
186 | }
|
---|
187 |
|
---|
188 | return join(values, DELIMITER);
|
---|
189 | }
|
---|
190 |
|
---|
191 | /**
|
---|
192 | * @param parties
|
---|
193 | * the parties in the nego
|
---|
194 | * @param agreement
|
---|
195 | * the reached agreement, or null if there was no agreement.
|
---|
196 | * @param discount
|
---|
197 | * true iff you want the discounted utilities.
|
---|
198 | * @return list with (possibly discounted) utilities/res value of each of
|
---|
199 | * the parties. Res value is only used if agreement=null.
|
---|
200 | *
|
---|
201 | */
|
---|
202 | public static List<Double> getUtils(List<NegotiationPartyInternal> parties,
|
---|
203 | Bid agreement, boolean discount) {
|
---|
204 | List<Double> utils = new ArrayList<Double>();
|
---|
205 | double time = 0;
|
---|
206 |
|
---|
207 | if (parties.size() > 0) {
|
---|
208 | time = parties.get(0).getTimeLine().getTime();
|
---|
209 | }
|
---|
210 |
|
---|
211 | for (NegotiationPartyInternal agent : parties) {
|
---|
212 | double util = getUtil(agent, agreement);
|
---|
213 | if (discount) {
|
---|
214 | util = agent.getUtilitySpace().discount(util, time);
|
---|
215 | }
|
---|
216 | utils.add(util);
|
---|
217 | }
|
---|
218 | return utils;
|
---|
219 | }
|
---|
220 |
|
---|
221 | /**
|
---|
222 | * Asks the parties themselves for the perceived utilties; this may yield a
|
---|
223 | * different number from the real utilities specified by
|
---|
224 | * {@link NegotiationPartyInternal} in case of preference uncertainty.
|
---|
225 | */
|
---|
226 | public static List<Double> getPerceivedUtils(
|
---|
227 | List<NegotiationPartyInternal> parties, Bid agreement,
|
---|
228 | boolean discount) {
|
---|
229 | List<Double> utils = new ArrayList<Double>();
|
---|
230 | double time = 0;
|
---|
231 | if (parties.size() > 0) {
|
---|
232 | time = parties.get(0).getTimeLine().getTime();
|
---|
233 | }
|
---|
234 |
|
---|
235 | for (NegotiationPartyInternal agent : parties) {
|
---|
236 | NegotiationParty party = agent.getParty();
|
---|
237 | if (party instanceof AbstractNegotiationParty) {
|
---|
238 | double util = ((AbstractNegotiationParty) party)
|
---|
239 | .getUtility(agreement);
|
---|
240 | if (discount)
|
---|
241 | util = agent.getUtilitySpace().discount(util, time);
|
---|
242 | utils.add(util);
|
---|
243 | } else
|
---|
244 | utils.add(0.);
|
---|
245 | }
|
---|
246 | return utils;
|
---|
247 | }
|
---|
248 |
|
---|
249 | /**
|
---|
250 | * Returns a list of the user bothers
|
---|
251 | */
|
---|
252 |
|
---|
253 | public static List<Double> getUserBothers(List<NegotiationPartyInternal> parties) {
|
---|
254 | List<Double> bothers = new ArrayList<Double>();
|
---|
255 | for (NegotiationPartyInternal agent : parties) {
|
---|
256 | double userBother = (agent.getUser() != null) ? (agent.getUser().getTotalBother()) : 0.0;
|
---|
257 | bothers.add(userBother);
|
---|
258 | }
|
---|
259 | return bothers;
|
---|
260 |
|
---|
261 | }
|
---|
262 |
|
---|
263 | /**
|
---|
264 | * Returns a list of the user utilities, which are just the true utilities - user bothers
|
---|
265 | */
|
---|
266 |
|
---|
267 | public static List<Double> getUserUtilities(List<NegotiationPartyInternal> parties, Bid agreement, boolean discount) {
|
---|
268 |
|
---|
269 | List<Double> userUtils = new ArrayList<Double>();
|
---|
270 | List<Double> utils = getUtils(parties, agreement, discount);
|
---|
271 | List<Double> bothers = getUserBothers(parties);
|
---|
272 | for(int i = 0; i < utils.size(); i++) {
|
---|
273 | double userUtility = (utils.get(i)>bothers.get(i)) ? utils.get(i)-bothers.get(i) : 0.0;
|
---|
274 | userUtils.add(userUtility);
|
---|
275 | }
|
---|
276 | return userUtils;
|
---|
277 |
|
---|
278 | }
|
---|
279 |
|
---|
280 |
|
---|
281 | /**
|
---|
282 | *
|
---|
283 | * @param agent
|
---|
284 | * the agent for which to compute the utility
|
---|
285 | * @param agreement
|
---|
286 | * the agreement, or null if there is no agreement in which case
|
---|
287 | * the reservation value is used
|
---|
288 | * @return the undiscounted utility/reservation value of the given
|
---|
289 | * agreement.
|
---|
290 | */
|
---|
291 | private static double getUtil(NegotiationPartyInternal agent,
|
---|
292 | Bid agreement) {
|
---|
293 | if (agreement == null) {
|
---|
294 | return agent.getUtilitySpace().getReservationValue();
|
---|
295 | }
|
---|
296 | return agent.getUtility(agreement);
|
---|
297 | }
|
---|
298 |
|
---|
299 | /**
|
---|
300 | *
|
---|
301 | * @param session
|
---|
302 | * @param protocol
|
---|
303 | * @param partiesint
|
---|
304 | * @param runTime
|
---|
305 | * @return string for the log file
|
---|
306 | * @throws Exception
|
---|
307 | */
|
---|
308 | public static String logSingleSession(Session session,
|
---|
309 | MultilateralProtocol protocol,
|
---|
310 | List<NegotiationPartyInternal> partiesint, double runTime)
|
---|
311 | throws Exception {
|
---|
312 |
|
---|
313 | List<String> values = new ArrayList<String>();
|
---|
314 | List<NegotiationParty> parties = new ArrayList<NegotiationParty>();
|
---|
315 | for (NegotiationPartyInternal p : partiesint) {
|
---|
316 | parties.add(p.getParty());
|
---|
317 | }
|
---|
318 |
|
---|
319 | Bid agreement = protocol.getCurrentAgreement(session, parties);
|
---|
320 | List<Double> utils = getUtils(partiesint, agreement, true);
|
---|
321 |
|
---|
322 | double minUtil = Collections.min(utils);
|
---|
323 | double maxUtil = Collections.max(utils);
|
---|
324 |
|
---|
325 | MultilateralAnalysis analysis = new MultilateralAnalysis(partiesint,
|
---|
326 | protocol.getCurrentAgreement(session, parties),
|
---|
327 | session.getTimeline().getTime());
|
---|
328 |
|
---|
329 | // check if at least one of the util spaces is discounted.
|
---|
330 | boolean isDiscounted = false;
|
---|
331 | for (NegotiationPartyInternal party : partiesint) {
|
---|
332 | if (party.getUtilitySpace().discount(1, 1) != 1) {
|
---|
333 | isDiscounted = true;
|
---|
334 | }
|
---|
335 | }
|
---|
336 | values.add("Time (s):\t\t");
|
---|
337 | values.add("" + runTime + "\n");
|
---|
338 |
|
---|
339 | values.add("Rounds:\t\t");
|
---|
340 | values.add("" + (session.getRoundNumber()) + "\n");
|
---|
341 |
|
---|
342 | values.add("Agreement?:\t\t");
|
---|
343 | values.add(agreement == null ? "No\n" : "Yes\n");
|
---|
344 |
|
---|
345 | values.add("Discounted?:\t\t");
|
---|
346 | values.add(isDiscounted ? "Yes\n" : "No\n");
|
---|
347 |
|
---|
348 | values.add("Number of parties:\t\t" + protocol.getNumberOfAgreeingParties(session, parties)
|
---|
349 | + "\n");
|
---|
350 |
|
---|
351 | values.add("Min. utility:\t\t");
|
---|
352 | values.add(String.format("%.5f\n", minUtil));
|
---|
353 |
|
---|
354 | values.add("Max. utility:\t\t");
|
---|
355 | values.add(String.format("%.5f\n", maxUtil));
|
---|
356 |
|
---|
357 | values.add("Distance to pareto:\t");
|
---|
358 | values.add(String.format("%.5f\n", analysis.getDistanceToPareto()));
|
---|
359 |
|
---|
360 | values.add("Distance to Nash:\t");
|
---|
361 | values.add(String.format("%.5f\n", analysis.getDistanceToNash()));
|
---|
362 |
|
---|
363 | values.add("Social welfare:\t\t");
|
---|
364 | values.add(String.format("%.5f\n", analysis.getSocialWelfare()));
|
---|
365 |
|
---|
366 | values.add("Opposition:\t\t");
|
---|
367 | values.add(String.format("%.5f\n", analysis.getOpposition()));
|
---|
368 |
|
---|
369 | // If you need this, then you should also
|
---|
370 | // use buildSpace(false); in MultilateralAnalysis to get actual bid
|
---|
371 | // contents.
|
---|
372 |
|
---|
373 | // values.add("Nash point:\t\t");
|
---|
374 | // values.add(analysis.getNashPoint().toString() + "\n");
|
---|
375 | //
|
---|
376 | // values.add("Social Welfare point:\t");
|
---|
377 | // values.add(analysis.getSocialwelfarePoint().toString() + "\n");
|
---|
378 |
|
---|
379 | for (int i = 0; i < partiesint.size(); i++) {
|
---|
380 | values.add(String.format("Agent utility:\t\t%.5f (%s)\n",
|
---|
381 | utils.get(i), partiesint.get(i).getID()));
|
---|
382 | }
|
---|
383 |
|
---|
384 | double bother = 0;
|
---|
385 | for (int i = 0; i < partiesint.size(); i++) {
|
---|
386 | bother = (partiesint.get(i).getUser() != null)? partiesint.get(i).getUser().getTotalBother():0.0;
|
---|
387 | values.add(String.format("User bother:\t\t%.5f (%s)\n",
|
---|
388 | bother, partiesint.get(i).getID()));
|
---|
389 | }
|
---|
390 | double trueUtil = 0;
|
---|
391 | for (int i = 0; i < partiesint.size(); i++) {
|
---|
392 | trueUtil = (partiesint.get(i).getUser() != null)? utils.get(i)-partiesint.get(i).getUser().getTotalBother():utils.get(i);
|
---|
393 | trueUtil = (trueUtil>0) ? trueUtil:0.0;
|
---|
394 | values.add(String.format("User utility:\t\t%.5f (%s)\n",
|
---|
395 | trueUtil, partiesint.get(i).getID()));
|
---|
396 | }
|
---|
397 |
|
---|
398 |
|
---|
399 | return join(values, "");
|
---|
400 | }
|
---|
401 |
|
---|
402 | public static String stripPath(String filenameWithPath) {
|
---|
403 | String[] split = filenameWithPath.split("/");
|
---|
404 | if (split.length < 2)
|
---|
405 | return filenameWithPath;
|
---|
406 | else
|
---|
407 | return split[split.length - 2] + "/" + split[split.length - 1];
|
---|
408 | }
|
---|
409 |
|
---|
410 | /**
|
---|
411 | * Closes this stream and releases any system resources associated with it.
|
---|
412 | * If the stream is already closed then invoking this method has no effect.
|
---|
413 | *
|
---|
414 | * @throws java.io.IOException
|
---|
415 | * if an I/O error occurs
|
---|
416 | */
|
---|
417 | @Override
|
---|
418 | public void close() throws IOException {
|
---|
419 | ps.close();
|
---|
420 | }
|
---|
421 | }
|
---|