1 | \documentclass{article}
|
---|
2 | \usepackage[margin=1in]{geometry}
|
---|
3 | \usepackage{authblk}
|
---|
4 |
|
---|
5 | \usepackage[utf8]{inputenc}
|
---|
6 | \pagestyle{plain}
|
---|
7 | \usepackage{eurosym}
|
---|
8 | \usepackage[table]{xcolor}
|
---|
9 | \usepackage{graphicx}
|
---|
10 | \usepackage{enumitem}\newsavebox\fnbox
|
---|
11 | \usepackage{hyperref}
|
---|
12 | \usepackage{placeins}
|
---|
13 | \usepackage{listings}
|
---|
14 |
|
---|
15 | \usepackage{booktabs}
|
---|
16 | \usepackage{caption}
|
---|
17 | \captionsetup[table]{skip=10pt}
|
---|
18 |
|
---|
19 | \newtheorem{exercise}{Exercise}
|
---|
20 |
|
---|
21 | \newcommand{\code}[1]{\texttt{#1}}
|
---|
22 |
|
---|
23 | \newcommand{\fbf}{\textbf}
|
---|
24 |
|
---|
25 | \newcommand{\gw}{\sc{{GeniusWeb}}}
|
---|
26 |
|
---|
27 | \title{ {\gw} Tutorial}
|
---|
28 | \author{Wouter Pasman}
|
---|
29 | \author{Pradeep K.~Murukannaiah}
|
---|
30 | \author{Catholijn M.~Jonker}
|
---|
31 |
|
---|
32 | \affil{Interactive Intelligence, Delft University of Technology}
|
---|
33 |
|
---|
34 | %define colors for various boxes
|
---|
35 | \definecolor{infocolor}{rgb}{0.8, 0.9, 0.99}
|
---|
36 | \definecolor{warningcolor}{rgb}{0.99, 0.9, 0.8}
|
---|
37 | \definecolor{tipcolor}{rgb}{0.9, 0.99, 0.8}
|
---|
38 |
|
---|
39 |
|
---|
40 |
|
---|
41 | \usepackage{courier} %% Sets font for listing as Courier.
|
---|
42 | \usepackage{listings, xcolor}
|
---|
43 | \lstset{
|
---|
44 | tabsize = 4, %% set tab space width
|
---|
45 | showstringspaces = false, %% prevent space marking in strings, string is defined as the text that is generally printed directly to the console
|
---|
46 | numbers = left, %% display line numbers on the left
|
---|
47 | commentstyle = \color{green}, %% set comment color
|
---|
48 | keywordstyle = \color{blue}, %% set keyword color
|
---|
49 | stringstyle = \color{red}, %% set string color
|
---|
50 | rulecolor = \color{black}, %% set frame color to avoid being affected by text color
|
---|
51 | basicstyle = \small \ttfamily , %% set listing font and size
|
---|
52 | breaklines = true, %% enable line breaking
|
---|
53 | numberstyle = \tiny,
|
---|
54 | }
|
---|
55 |
|
---|
56 | \begin{document}
|
---|
57 | \maketitle
|
---|
58 |
|
---|
59 | This tutorial will help you getting started on the Negotiation Assignment. It gives some exercises to test yourself, guides you through some steps like setting up parts of Eclipse, starting services, etc. This tutorial is optional, it is provided to help you, and not part of an assignment.
|
---|
60 |
|
---|
61 | Sections 1 to 4 introduce the concepts of domains, issues, preference profiles, and outcome spaces as well as the {\gw} platform. In Sections 5 to 9, you will create a simple negotiation party in Java\footnote{Other languages can be used, but we chose the reference language used for GeniusWeb} using the {\gw} platform and learn to debug your party. Sections 10 to 14 introduce a more advanced strategy using opponent modelling, and shows how to evaluate your party via different metrics.
|
---|
62 |
|
---|
63 | \fbf{A note on terminology:} We use the term negotiation \emph{party}. In other literature you may find the term \emph{agent}.
|
---|
64 |
|
---|
65 | %%%%%%%%%%%%% LAB 1 %%%%%%%%%%%%%%%%%%%%%%%
|
---|
66 |
|
---|
67 | \section{Understanding Preference Profiles}\label{ch:Profiles}
|
---|
68 | We start with a few simple exercises to make sure that you understand the basic concepts. Please refer to the Negotiating Agents handbook (available in Brightspace) to understood the theoretical concepts.
|
---|
69 |
|
---|
70 |
|
---|
71 | \begin{exercise}
|
---|
72 | \label{ex:utility}
|
---|
73 |
|
---|
74 | Given are the following linear-additive profiles for a buyer $A$ (Table~\ref{tab:buyerprofile}) and a seller $B$ (Table~\ref{tab:sellerprofile})), calculate the utility of outcome $o$=(Price: \euro 1200, HardDisk: 1TB, Monitor: 15) for Parties $A$ and $B$.
|
---|
75 |
|
---|
76 | \begin{table}[htb!]
|
---|
77 | \centering
|
---|
78 | \caption{Preference profile of Party $A$ (buyer)}
|
---|
79 | \begin{tabular}{|l|c|r|} % <-- Alignments: 1st column left, 2nd middle and 3rd right, with vertical lines in between
|
---|
80 | \hline
|
---|
81 | \textbf{Issue} & \textbf{Weight} & \textbf{Evaluations}\\
|
---|
82 | \hline
|
---|
83 | Price & 0.3 & eval(\euro 1000)=1, eval(\euro 1200)=0.5, eval(\euro 1400)=0\\
|
---|
84 | Hard disk & 0.4 & eval(256GB)=0, eval(512GB)=0.6, eval(1TB)=1\\
|
---|
85 | Monitor & 0.3 & eval(15)=0, eval(17)=1\\
|
---|
86 | \hline
|
---|
87 | \end{tabular}
|
---|
88 | \label{tab:buyerprofile}
|
---|
89 | \end{table}
|
---|
90 |
|
---|
91 | \begin{table}[htb!]
|
---|
92 | \centering
|
---|
93 | \caption{Preference profile of Party $B$ (seller)}
|
---|
94 | \begin{tabular}{|l|c|r|} % <-- Alignments: 1st column left, 2nd middle and 3rd right, with vertical lines in between
|
---|
95 | \hline
|
---|
96 | \textbf{Issue} & \textbf{Weight} & \textbf{Evaluations}\\
|
---|
97 | \hline
|
---|
98 | Price & 0.6 & eval(\euro 1000)=0, eval(\euro 1200)=0.5, eval(\euro 1400)=1\\
|
---|
99 | Hard disk & 0.3 & eval(256GB)=1, eval(512GB)=2/3, eval(1TB)=1/3\\
|
---|
100 | Monitor & 0.1 & eval(15)=1, eval(17)=0\\
|
---|
101 | \hline
|
---|
102 | \end{tabular}
|
---|
103 | \label{tab:sellerprofile}
|
---|
104 | \end{table}
|
---|
105 |
|
---|
106 | \end{exercise}
|
---|
107 |
|
---|
108 | \begin{exercise}
|
---|
109 | \label{ex:pareto}
|
---|
110 | Figure~\ref{fig:bidspace} plots the utilities of parties $A$ and $B$ corresponding to the outcomes from Example~\ref{ex:utility}. Which of these outcomes are Pareto optimal?
|
---|
111 |
|
---|
112 | \begin{figure}
|
---|
113 | \centering
|
---|
114 | \includegraphics[width=10cm]{images/laptopdomain.png}
|
---|
115 | \caption{Negotiation outcomes for the laptop domain.}
|
---|
116 | \label{fig:bidspace}
|
---|
117 | \end{figure}
|
---|
118 | \end{exercise}
|
---|
119 |
|
---|
120 | \section{Installing {\gw} }\label{ch:installation}
|
---|
121 | For creating a profile, you only need a text editor, possibly with json support. You can install or use an already available text editor on your machine.
|
---|
122 |
|
---|
123 | For creating a party and to run the geniusweb servers, you need to have java JDK (at least version 8) and maven to be installed on your machine.
|
---|
124 |
|
---|
125 | To run a negotiation, you need the GeniusWeb servers installed in a tomcat server running on your machine. Go to
|
---|
126 | \url{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb#Installation}and follow the instructions to install the latest version of the {\gw} servers on your machine. You need these servers to run your own parties and profies in a negotiation. There is also a video on the installation process,
|
---|
127 |
|
---|
128 | \url{https://ii.tudelft.nl/GeniusWeb/students.html}.
|
---|
129 |
|
---|
130 | Start the tomcat webserver that you have after the installation.
|
---|
131 | Try the \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebRunServer\#UsingtheGUI}{runserver GUI} to check that your {\gw} is working.
|
---|
132 |
|
---|
133 |
|
---|
134 | \section{Creating a Domain in {\gw}}
|
---|
135 | We are now going to create the laptop profiles as in Table~\ref{tab:buyerprofile} and Table~\ref{tab:sellerprofile} above.
|
---|
136 | If you go to the \href{https://tracinsy.ewi.tudelft.nl/trac/GeniusWebProfilesServer/wiki/WikiStart#Usage}{Profileserver GUI} you can check the already available profiles. You should get a table like in figure \ref{fig:defaultprofiles}.
|
---|
137 |
|
---|
138 | \begin{figure}[!htb]
|
---|
139 | \centering
|
---|
140 | \includegraphics[width=.8\textwidth]{images/defaultprofiles.png}
|
---|
141 | \caption{Default profiles on the profilesserver.}
|
---|
142 | \label{fig:defaultprofiles}
|
---|
143 | \end{figure}
|
---|
144 |
|
---|
145 | You can see that there already is a laptop domain available. We are going to create a new \textbf{laptop1} domain and leave the existing laptop domain intact.\\
|
---|
146 |
|
---|
147 |
|
---|
148 |
|
---|
149 | \fcolorbox{black}{tipcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
150 | \textbf{ProfilesServer messages:}
|
---|
151 | At this point it is useful to keep an eye on the tomcat log messages.
|
---|
152 | You can see those inside the logs/catalina.out file inside your tomcat installation directory.
|
---|
153 | }}
|
---|
154 |
|
---|
155 |
|
---|
156 |
|
---|
157 | \subsection{Copy laptop folder}
|
---|
158 | The quickest way to get started is to copy the existing laptop domain and modify it.
|
---|
159 |
|
---|
160 | \begin{enumerate}
|
---|
161 | \item locate and check the contents of the directory webapps/profilesserver-X.Y.Z/domainsrepo inside your tomcat application (NOTICE: this is created by tomcat from the profilesserver-X.Y.Z.war file when you start up tomcat).
|
---|
162 | \item copy the laptop folder including its contents to laptop1.
|
---|
163 | \item at this point, you may see a message on the profilesserver:
|
---|
164 | \code{WARNING domainsrepo/laptop1/laptop1.json is missing}.
|
---|
165 | \item to fix this change the new \code{laptop1/laptop} filename into \code{laptop1.json}. You will now get another error, which will be fixed in the next section.
|
---|
166 | \end{enumerate}
|
---|
167 |
|
---|
168 | \subsection{Fix domain description}
|
---|
169 |
|
---|
170 | We are now first going to fix the domain description. For reference, domain desciptions are documented on \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb#Domain}{{\gw} domains}.
|
---|
171 |
|
---|
172 | Open the file \code{laptop1.json} and make changes. Make sure that your text editor keeps the UTF8 text encoding and does not mess with the end-of-line markers. Do the following:
|
---|
173 | \begin{itemize}
|
---|
174 | \item the ``name" should be changed to ``laptop1".
|
---|
175 | \item the ``Brand" issue has to be changed into ``Price".
|
---|
176 | \item the ``Price" values have to be changed into the correct values "600", "900" and "1200". Keep the quotes, See \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb#ValueSet}{valueset documentation} for more details.
|
---|
177 | \item Fix the harddisk values.
|
---|
178 | \item fix the issue ``External Monitor" to ``Monitor".
|
---|
179 | \item fix the values of ``Monitor".
|
---|
180 | \item save the file.
|
---|
181 | \end{itemize}
|
---|
182 |
|
---|
183 | At this point, you still get warnings in the catalina log, but now errors like ``Profile has incorrect domain", indicating that domain desciption inside the laptopBuyer and laptopSeller files does not match the laptop1 description.
|
---|
184 |
|
---|
185 | \subsection{Fix the profiles}
|
---|
186 | We now are going to fix the two profiles. The following has to be repeated for both the laptopBuyer and the laptopSeller files inside the laptop1 domain.
|
---|
187 | \begin{enumerate}
|
---|
188 | \item Open the \code{laptopBuyer.json} file with the plain text editor.
|
---|
189 | \item Replace the entire contents of the ``domain" section with the full contents of the \code{laptop1.json} file that you just fixed.
|
---|
190 | \item If you would save the file at this point, you would get a warning that ``The issues in utilityspace and domain do not match" because the issues in the domain now do not match issues in the profile itself.
|
---|
191 | \item Now we are going to fix the actual profile inside the LinearAdditiveUtilitySpace element:
|
---|
192 | \begin{itemize}
|
---|
193 | \item as with the profile, fix ``Brand" to ``Price", and ``External Monitor" to ``Monitor".
|
---|
194 | \item in Price/valueUtilities change the values from Brand to Price values "600", "900", "1200". Make sure that your values match exactly the values that you set in the domain, including quotes. WARNING: if you do not copy the issue names correctly you will not get an error but your profile will get a 0 evaluation for the wrong-typed issue, leading to unexpected behaviour of your profile.
|
---|
195 | \item Set the correct evaluation values, as in the perference profile of this party.
|
---|
196 | \item repeat for the Harddisk issue. You can round 1/3 to 0.33 etc.
|
---|
197 | \item repeat for the Monitor issue.
|
---|
198 | \item fix the issue names in the issueWeights, ``Brand" to ``Price", etc.
|
---|
199 | \item Finally, fix the issueWeights, according to the weights of the issues in the table.
|
---|
200 | \end{itemize}
|
---|
201 | \item save the file.
|
---|
202 | \end{enumerate}
|
---|
203 |
|
---|
204 | Repeat this for the \code{laptopSeller.json} file. If you did all fixes correctly, there should be no new errors reported, and your profiles should now be visible on the profilesserver as in Figure \ref{fig:laptop1}.
|
---|
205 |
|
---|
206 |
|
---|
207 | \begin{figure}[!htb]
|
---|
208 | \centering
|
---|
209 | \includegraphics[width=.8\textwidth]{images/laptop1.png}
|
---|
210 | \caption{laptop1 domain and profiles available on profilesserver.}
|
---|
211 | \label{fig:laptop1}
|
---|
212 | \end{figure}
|
---|
213 |
|
---|
214 |
|
---|
215 |
|
---|
216 | \section{Running a Negotiation Session}\label{ch:runsession}
|
---|
217 | We are going to use the web interface to run some automated negotiation.
|
---|
218 | Browse to your local runserver (see \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebRunServer#UsingtheGUI}{the wiki pages}) and select `new session'.
|
---|
219 | \begin{itemize}
|
---|
220 | \item use the default SAOP protocol. You can read more on the other protocols on
|
---|
221 |
|
---|
222 | \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb#Protocol}{tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb\#Protocol}
|
---|
223 | \item set 60 rounds for the negotiation.
|
---|
224 | \item Select the laptop1 domain that you created.
|
---|
225 | \item Select e.g. boulware and laptopBuyer for the settings and click Add.
|
---|
226 | \item Select e.g. linear and laptopSeller for the settings and click Add.
|
---|
227 |
|
---|
228 | \end{itemize}
|
---|
229 |
|
---|
230 | Your setup should look like Figure \ref{fig:sessionsetup} now.
|
---|
231 |
|
---|
232 | \begin{figure}[!htb]
|
---|
233 | \centering
|
---|
234 | \includegraphics[width=.8\textwidth]{images/sessionsetup.png}
|
---|
235 | \caption{Session setup, ready to run}
|
---|
236 | \label{fig:sessionsetup}
|
---|
237 | \end{figure}
|
---|
238 |
|
---|
239 | \fcolorbox{black}{infocolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
240 | \textbf{Rounds vs Time:}
|
---|
241 | The Deadline can either be set to ROUNDS or TIME.
|
---|
242 | With TIME, the negotiation has a time deadline, at which point the negotiation will be terminated if no deal was yet reached. With ROUNDS, the negotiation is terminated if no deal is reached after the given number of rounds, or if
|
---|
243 | the negotiation lasts longer than a maximum duration (typically 10 seconds).
|
---|
244 | }}
|
---|
245 |
|
---|
246 |
|
---|
247 |
|
---|
248 |
|
---|
249 | Click on the ``Start Session" button. After a few seconds, the text appears below the ``Start Session" button.
|
---|
250 | You can click on ``view the log file" to see the raw log file results from the negotiation.
|
---|
251 | Alternatively you can render a graph of the results by clicking on ``render a utilities plot". Clicking the latter, you get a window as in Figure~\ref{fig:deal} with the bidding progress and informing that a deal has been reached.
|
---|
252 |
|
---|
253 |
|
---|
254 | \begin{figure}[!htb]
|
---|
255 | \centering
|
---|
256 | \includegraphics[width=.8\textwidth]{images/deal.png}
|
---|
257 | \caption{Session progress plot}
|
---|
258 | \label{fig:deal}
|
---|
259 | \end{figure}
|
---|
260 |
|
---|
261 |
|
---|
262 |
|
---|
263 | \fcolorbox{black}{infocolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
264 | In addition to the negotiation log visible in the {\gw} interface, this log can be found in the directory
|
---|
265 | \code{webapps/runserver/log} inside your tomcat application.
|
---|
266 | }}
|
---|
267 |
|
---|
268 |
|
---|
269 |
|
---|
270 | \FloatBarrier
|
---|
271 |
|
---|
272 |
|
---|
273 |
|
---|
274 |
|
---|
275 | %%%%%%%%%%%%%%%%%%%%%%% Labl2 %%%%%%%%%%%%%%%
|
---|
276 |
|
---|
277 | \section{Creating a party in Eclipse}
|
---|
278 | In this section, you are going to write and compile your own party in Java.
|
---|
279 |
|
---|
280 | Install Eclipse if you don't yet have it. You can download it from \href{https://www.eclipse.org/downloads/packages}{www.eclipse.org/downloads/packages}.
|
---|
281 | You need the IDE for java Developers.
|
---|
282 |
|
---|
283 | \fcolorbox{black}{warningcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
284 | \textbf{Java, Maven, Tomcat, Eclipse}:
|
---|
285 | It is assumed you are familiar with the Java programming language,
|
---|
286 | the Maven build system, Tomcat server and Eclipse.
|
---|
287 | The tutorial videos show how to do
|
---|
288 | a number of tricky steps and should help you to do
|
---|
289 | the tasks in this exercise.
|
---|
290 | }}
|
---|
291 |
|
---|
292 | \fcolorbox{black}{infocolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
293 | \textbf{Integrated Development Environment (IDE):}
|
---|
294 | It is highly recommended that you use a Java IDE, such as Eclipse or IntelliJ, for developing your party. If you are already experienced with Java, then you can use any IDE you are most comfortable with. Otherwise, it is recommended that you use Eclipse since this is the one used in most of the examples and the tutorial videos.
|
---|
295 |
|
---|
296 | With the Eclipse IDE, you get many tools, features and support, such as all code documentation immediately available, also from all third-party libraries, code completion suggestions, refactoring, immediate highlighting of syntax and typing erorrs, interactive debugging, automatic building, testing, quality assurance, etc. This is not the place to delve into these, but we recommend that you explore these features for yourself if you are not familiar with these.
|
---|
297 | }}
|
---|
298 | \newline
|
---|
299 |
|
---|
300 | We follow the instructions on the \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb#WritingapartyinJava}{{\gw} wiki}.
|
---|
301 | There are \href{https://ii.tudelft.nl/GeniusWeb/students.html}{Tutorial videos} available for this as well. Follow the one explaining how to build your party in Eclipse. We recommend following the video tutorial if you are not used to Eclipse.
|
---|
302 | :
|
---|
303 |
|
---|
304 | \begin{enumerate}
|
---|
305 | \item Create a new workspace in Eclipse (or use a space you already made).
|
---|
306 | \item Install an SVN client, eg subclipse, if you did not yet install it, using Eclipse Marketplace menu item.
|
---|
307 | \item Clone the randomparty from \href{https://tracinsy.ewi.tudelft.nl/pub/svn/GeniusWeb/exampleparties/randomparty/}{the {\gw} example parties repository}.
|
---|
308 | \item If Eclipse does not recognise it as Maven, configure the project as maven project in Eclipse.
|
---|
309 | \item Change the isGood function of RandomParty.java so that it accepts above utility 0.7 instead of 0.6.
|
---|
310 | \item Change the getDescription function accordingly.
|
---|
311 | \item run \code{maven clean package} and check that your party passes the tests and that a jar file is built in the target directory.
|
---|
312 | \end{enumerate}
|
---|
313 |
|
---|
314 | At this point, your compiled party will have the name \code{randomparty-X,Y.Z.jar}.
|
---|
315 |
|
---|
316 |
|
---|
317 |
|
---|
318 | \fcolorbox{red}{warningcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
319 | RandomParty supports several protocols.
|
---|
320 | If you are working on an assignment, typically you need to support only one protocol
|
---|
321 | and you can remove code for the other protocols.
|
---|
322 | }}
|
---|
323 |
|
---|
324 |
|
---|
325 | \section{Renaming your party}
|
---|
326 | Assignments typically require you to use specific package names,
|
---|
327 | and not use the packages already used by {\gw}. To rename your party:
|
---|
328 |
|
---|
329 | \begin{enumerate}
|
---|
330 | \item In the Package Explorer, Open src/main/java of the randomparty project.
|
---|
331 | \item Right-click the geniusweb.exampleparties.randomparty folder and select refactor/rename.
|
---|
332 | \item Enter the new name for your package, e.g., \code{collabai.group$N$} where $N$ is your group number. Check your assignment for the required name.
|
---|
333 | \item repeat for the src/test folder
|
---|
334 | \item Open the new package.
|
---|
335 | \item Right-click on RandomParty.java, select refactor/rename.
|
---|
336 | \item Enter your new partyname, e.g., \code{Group$N$Party} where $N$ is your group number.
|
---|
337 | \item repeat for the src/test folder
|
---|
338 | \item Open the pom.xml of the project.
|
---|
339 | \item At the top, change:
|
---|
340 | \begin{itemize}
|
---|
341 | \item \code{<artifactId>randomparty</artifactId>} into \code{ <artifactId>group$N$party</artifactId>}
|
---|
342 | \item \code{<groupId>geniusweb.exampleparties</groupId>
|
---|
343 | } into \code{ <artifactId>collabai.group$N$</artifactId>}
|
---|
344 | \end{itemize}
|
---|
345 | \item Fix the two occurences of \code{<mainClass>geniusweb....RandomParty</mainClass>}
|
---|
346 | into the correct full class path to your party.
|
---|
347 | \item Re-run the \code{mvn clean package}.
|
---|
348 | \end{enumerate}
|
---|
349 |
|
---|
350 | At this point, there should be a \code{group$N$party-X,Y,Z-jar-with-dependencies.jar} inside your target directory.
|
---|
351 | You may need to right click and refresh the directory.
|
---|
352 |
|
---|
353 | \section{Deploy your party}
|
---|
354 |
|
---|
355 | Deploying your new party is done by just copying your \code{...jar-with-dependencies.jar}
|
---|
356 | file to partiesserver.
|
---|
357 | \begin{enumerate}
|
---|
358 | \item Open \code{webapps/partiesserver-X.Y.Z/partiesrepo} on the tomcat server.
|
---|
359 | \item Copy your \code{group$N$party-X,Y,Z-jar-with-dependencies.jar} to this directory.
|
---|
360 | \item Assuming your server is still running, check if the party is available:
|
---|
361 | open the URL of the \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPartiesServer#GUI}{local partyserver} to see if your party is available. You should see your party in the list, as in Figure \ref{fig:myagentruns}.
|
---|
362 | \item If it is not available, check the catalina.out log file to see reported errors.
|
---|
363 | \end{enumerate}
|
---|
364 |
|
---|
365 |
|
---|
366 | \begin{figure}[!htb]
|
---|
367 | \centering
|
---|
368 | \includegraphics[width=.9\textwidth]{images/myagentruns.png}
|
---|
369 | \caption{My agent running successfully}
|
---|
370 | \label{fig:myagentruns}
|
---|
371 | \end{figure}
|
---|
372 |
|
---|
373 |
|
---|
374 | There is also a \href{https://ii.tudelft.nl/GeniusWeb/students.html}{Tutorial video} available for this. Your party is now ready for use. You can now use your party just as you used the boulware and linear parties in Section \ref{ch:runsession}.
|
---|
375 |
|
---|
376 |
|
---|
377 | \section{Debugging your party}
|
---|
378 | Sometimes your code is not behaving as you expected. One way to find the issue is to use a debugger.
|
---|
379 | To use the debugger directly on the party as it runs on the server is possible but a bit impractical, \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb/wiki/WikiStart#Debuggingintheserver}{check the wiki} if you really want to take this approach.
|
---|
380 |
|
---|
381 | Instead, here we use the recommended way using the standalone runner for debugging.
|
---|
382 |
|
---|
383 | Follow the steps outlined on \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb/wiki/WikiStart#Debuggingintheserver}{the wiki page}. A video on how to debug standalone is available on the \href{https://ii.tudelft.nl/GeniusWeb/students.html}{Tutorial page}.
|
---|
384 |
|
---|
385 | In short, you should do the following:
|
---|
386 | \begin{enumerate}
|
---|
387 | \item Install the standalone runner in your workspace.
|
---|
388 | \item Edit the run settings of the runner, so that it runs your party in the correct configuration. Also increase the deadline so that you have ample time to debug before the party is killed
|
---|
389 | \item Place breakpoint in your party.
|
---|
390 | \item Run the standalone runner in eclipse debug mode.
|
---|
391 | \item Eclipse will switch to the debugger and you can inspect all variables and step through your program.
|
---|
392 | \end{enumerate}
|
---|
393 |
|
---|
394 |
|
---|
395 | \fcolorbox{black}{warningcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
396 | The session run will usually show ``complete successfully" even if a party crashes.
|
---|
397 | This is because a crashing party is considered just a protocol error.
|
---|
398 | }}
|
---|
399 |
|
---|
400 | \fcolorbox{black}{tipcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
401 | You can also use the standalone runner to do quick standalone runs, avoiding
|
---|
402 | the need to set up a session or tournament for each run.
|
---|
403 | }}
|
---|
404 |
|
---|
405 | \fcolorbox{black}{infocolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
406 | If the run terminates, the final State (here, a \code{SAOPState}) is printed to stdout (usually the console). This state contains lots of details and may also contain a full stacktrace of failing parties.
|
---|
407 | }}
|
---|
408 |
|
---|
409 |
|
---|
410 |
|
---|
411 | \section{Modifying your Party}
|
---|
412 |
|
---|
413 | \fcolorbox{black}{infocolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
414 | \textbf{Documentations}
|
---|
415 | All classes in {\gw} are heavily documented to help using them.
|
---|
416 | An overview of {\gw} is given on the \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb}{main wiki page}.
|
---|
417 |
|
---|
418 | You can also download all javadocs from all modules. For example, to get
|
---|
419 | the 2.0.0 bidspace documentations, go to
|
---|
420 | \url{http://artifactory.ewi.tudelft.nl/artifactory/webapp/\#/artifacts/browse/simple/General/libs-release-local/geniusweb/bidspace/2.0.0/bidspace-2.0.0-javadoc.jar} and download and unzip the \code{javadoc.jar}. Open the index.html file inside.
|
---|
421 | If you use Eclipse, this is all done automatically Eclipse shows the javadoc
|
---|
422 | for anything you hover over.
|
---|
423 |
|
---|
424 | All source code from {\gw} is publicly available, check the information on the \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb}{main wiki page}.
|
---|
425 | }}
|
---|
426 |
|
---|
427 |
|
---|
428 |
|
---|
429 | \subsection{Using the Profile}
|
---|
430 | Your party receives the profile that it has to negotiate with in the Settings object. The settings object usually is the first object that your party receives.
|
---|
431 |
|
---|
432 | The Settings object contains only an URL from where the profile can be fetched. A simple method is to use the ProfileConnectionFactory for this. It gives you a ProfileInterface that does the fetching and offers you an instance of a \code{Profile}, ready for further use.
|
---|
433 |
|
---|
434 | Here we discuss an \code{LinearAdditiveUtilitySpace}, as you already made one in Section \ref{ch:Profiles}. This space can assign a number in $[0,1]$ to each bid,
|
---|
435 | and the higher the number the higher the preference for that bid. This space works
|
---|
436 | using linear, weighted functions as was explained in the handbook.
|
---|
437 |
|
---|
438 | The following code accesses the domain and displays the issues, etc. Try and understand each line of code, using the Javadoc as a reference for the various methods.
|
---|
439 |
|
---|
440 | Copy and paste the code snippet inside notifyChange(..), at the end of the if(info instanceof Settings) block. Run a single negotiation session with your agent and check the console for the output produced.
|
---|
441 |
|
---|
442 | \begin{lstlisting}[language = Java , frame = trBL , escapeinside={(*@}{@*)}]
|
---|
443 | LinearAdditiveUtilitySpace space =
|
---|
444 | (LinearAdditiveUtilitySpace) profileint.getProfile();
|
---|
445 | Map<String, ValueSetUtilities> valueutils = space.getUtilities();
|
---|
446 | for (String issue : space.getDomain().getIssues()) {
|
---|
447 | System.out.println(">> " + issue + " weight: "
|
---|
448 | + space.getWeight(issue));
|
---|
449 | ValueSetUtilities utils = valueutils.get(issue);
|
---|
450 | // ignore the non-discrete in this demo
|
---|
451 | if (!(utils instanceof DiscreteValueSetUtilities))
|
---|
452 | continue;
|
---|
453 | Map<DiscreteValue, BigDecimal> values =
|
---|
454 | ((DiscreteValueSetUtilities) utils).getUtilities();
|
---|
455 | for (DiscreteValue value : values.keySet()) {
|
---|
456 | System.out.println("utility( " + value + ")= "
|
---|
457 | + values.get(value));
|
---|
458 | }
|
---|
459 | }
|
---|
460 | \end{lstlisting}
|
---|
461 |
|
---|
462 | \fcolorbox{black}{tipcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
463 | You will get 'missing' and 'cannot be resolved' errors once you have copied the code since you need to import several classes. In Eclipse you can do this easily, by placing the cursor at the end of the class name where the error is, and then pressing CTRL+Space (you can use this for code completion). This will give you several options, and typically choose the first one. Pressing enter will import the corresponding class.
|
---|
464 | }}
|
---|
465 |
|
---|
466 | \fcolorbox{black}{tipcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
467 | it is recommended to delay access to the profile as long as possible,
|
---|
468 | to avoid waiting for server data to arrive while you don't yet need it.
|
---|
469 | }}
|
---|
470 |
|
---|
471 | \fcolorbox{black}{warningcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
472 | This code is placed inside the Settings handler only for demonstration purposes.
|
---|
473 | Avoid printing debug info in your assignment solutions.
|
---|
474 | }}
|
---|
475 |
|
---|
476 |
|
---|
477 | \subsection{A Simple Concession Strategy}
|
---|
478 |
|
---|
479 | Let's now modify the negotiation strategy. The SAOP protocol consists of 3 possible actions:
|
---|
480 |
|
---|
481 | \begin{itemize}
|
---|
482 | \item Accepting the opponent's offer;
|
---|
483 | \item Generating a (counter) offer (which automatically means rejecting an opponent offer);
|
---|
484 | \item Ending the negotiation.
|
---|
485 | \end{itemize}
|
---|
486 |
|
---|
487 | In the SAOP protocol, parties decide for an action when they receive a
|
---|
488 | \code{YourTurn}. Others' actions received through the \code{ActionDone} object can be stored for later use.
|
---|
489 |
|
---|
490 | If the party is the first to get the turn, then the first action should be to generate an \code{Offer}. Before proceeding any further, have a look at how these two methods are implemented in your party and make sure you understand the given code.
|
---|
491 |
|
---|
492 | Next, we introduce a simple concession strategy. The concession strategy determines the target utility level at which to produce and/or accept offers.
|
---|
493 |
|
---|
494 |
|
---|
495 | \begin{enumerate}
|
---|
496 | \item
|
---|
497 | Use a simple linear approach to set the target utility, which starts with the highest possible utility (i.e. the utility of the best possible offer), and then concedes up to a threshold when the time limit is reached. Set this threshold using a variable (e.g. it can be the average between the highest and lowest possible utility within the set of possible offers). Some code to help you is provided below:
|
---|
498 |
|
---|
499 | Getting a bid close to the maximum utility:
|
---|
500 |
|
---|
501 | \begin{lstlisting}[language = Java , frame = trBL , escapeinside={(*@}{@*)}]
|
---|
502 | private Bid getMaxUtilityBid() throws IOException {
|
---|
503 | BidsWithUtility bwu = new BidsWithUtility(
|
---|
504 | (LinearAdditive) this.profileint.getProfile());
|
---|
505 | BigDecimal maxutil = bwu.getRange().getMax();
|
---|
506 | ImmutableList<Bid> bids = bwu.getBids(new Interval(
|
---|
507 | maxutil.subtract(new BigDecimal("0.01")), maxutil));
|
---|
508 | if (bids.size().compareTo(BigInteger.ZERO) == 0)
|
---|
509 | return null;
|
---|
510 | return bids.get(BigInteger.ZERO);
|
---|
511 | }
|
---|
512 | \end{lstlisting}
|
---|
513 |
|
---|
514 | The above code uses the BidsWithUtility library to do the `heavy lifting' of processing the utility space. Of course you could naively iterate through the bids to find the exact maximum, but this might take longer than the available time for the negotiation.
|
---|
515 |
|
---|
516 | \fcolorbox{black}{tipcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
517 | We use the bidspace.BidsWithUtility library to demonstrate how to cope
|
---|
518 | with potentially huge spaces. Check the java documentations with BidsWithUtility for more details. Also check the other classes in bidspace.
|
---|
519 | }}
|
---|
520 |
|
---|
521 | Accessing the current progress of the negotiation, where 0 means the start and 1 the end of the negotiation:
|
---|
522 |
|
---|
523 | \code{ Double t=progress.get(System.currentTimeMillis()); }
|
---|
524 |
|
---|
525 | \item Accept any offers received by the opponent which have a utility equal or greater than the target utility.
|
---|
526 |
|
---|
527 | \item If no such offer was done, select a random offer which has a minimum utility threshold and offer it to the opponent.
|
---|
528 | \end{enumerate}
|
---|
529 |
|
---|
530 | Check the makeOffer code. Note that the used approach is not very efficient. Think about how this could be improved, also given that you now have a target utility..
|
---|
531 |
|
---|
532 | \subsection{Running Tournaments}
|
---|
533 | Running individual negotiation sessions for testing and evaluating your party can be a tedious task. There are two ways to easen running multiple sessions easier: running a tournament and using a script.
|
---|
534 |
|
---|
535 | A tournament consists of multiple negotiation sessions, involving multiple parties and multiple profiles. The {\gw} APP tournament protocol will automatically generate all possible configurations and you can even repeat tournaments multiple times, all with 1 click of a button. For example, suppose you want to compare 3 different strategies and 2 different profiles, then this will generate 3*3=9 individual negotiation sessions.
|
---|
536 |
|
---|
537 | To see how this works, first run a tournament using the {\gw} runserver interface by selecting \code{new tournament} (see \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebRunServer}{runserver wiki if you need more detailed instructions}). Select individual parties, e.g., Conceder, Boulware and your party. Furthermore, select at least 2 profiles from e.g. the Party domain. Run the tournament and see what happens.
|
---|
538 |
|
---|
539 | Also you can run sessions and tournaments from a script. The starting point is a json file with the sessionsettings or tournament settings. By providing this file to the simplerunner or sending it to the runserver, you can run these settings locally or on the server without having to navigate any GUIs.
|
---|
540 |
|
---|
541 | \fcolorbox{black}{infocolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
542 | Note that, when running a tournament, there is no record of the individual bids exchanged (unlike in the case of single negotiation sessions). This is to avoid large files, bogging the system and deterioration of the parties' performance.
|
---|
543 | }}
|
---|
544 |
|
---|
545 |
|
---|
546 | Now you are going to run a tournament using the simplerunner from Eclipse or from the command line, without using the GUI.
|
---|
547 |
|
---|
548 | \begin{enumerate}
|
---|
549 | \item Edit the simplerunner pom, and add dependencies for boulware and conceder, so that we can use those in the simplerunner.
|
---|
550 | boulware has this maven dependency:
|
---|
551 | \begin{lstlisting}[language = Java , frame = trBL , escapeinside={(*@}{@*)}]
|
---|
552 | <dependency>
|
---|
553 | <groupId>geniusweb.exampleparties</groupId>
|
---|
554 | <artifactId>boulware</artifactId>
|
---|
555 | <version>${geniusweb.version}</version>
|
---|
556 | </dependency>
|
---|
557 | \end{lstlisting}
|
---|
558 |
|
---|
559 | and conceder has this maven dependency:
|
---|
560 |
|
---|
561 | \begin{lstlisting}[language = Java , frame = trBL , escapeinside={(*@}{@*)}]
|
---|
562 | <dependency>
|
---|
563 | <groupId>geniusweb.exampleparties</groupId>
|
---|
564 | <artifactId>conceder</artifactId>
|
---|
565 | <version>${geniusweb.version}</version>
|
---|
566 | </dependency>
|
---|
567 | \end{lstlisting}
|
---|
568 |
|
---|
569 | \item Edit the file \code{src/test/resources/tournament.json}:
|
---|
570 | \begin{itemize}
|
---|
571 | \item Change the first partyref to \code{geniusweb.exampleparties.boulware.Boulware}.
|
---|
572 | \item Change the second partyref to \code{geniusweb.exampleparties.conceder.Conceder}.
|
---|
573 | \item Change the third partyref to the classpath of your team.
|
---|
574 | \end{itemize}
|
---|
575 | \item Edit the NegoRunner run settings and change the argument to \code{src/test/resources/tournament.json}.
|
---|
576 | \item Run the simplerunner to run your tournament.
|
---|
577 | \item Analyze the \code{AllPermutationsState} object that was finally printed to the console and check that there are 18 session results. Explain why 18. Check how many agreements were reached.
|
---|
578 | \end{enumerate}
|
---|
579 |
|
---|
580 |
|
---|
581 | Instead of running from Eclipse, you can run from the command line.
|
---|
582 | The above mostly applies, but instead of fixing Eclipse settings, do
|
---|
583 | \begin{enumerate}
|
---|
584 | \item run \verb|mvn clean package|
|
---|
585 | \item run (of course fix to the correct simplerunner version)
|
---|
586 | \begin{code}
|
---|
587 | java -jar target/simplerunner-2.0.0-jar-with-dependencies.jar
|
---|
588 | src/test/resources/tournament.json
|
---|
589 | \end{code}
|
---|
590 | \end{enumerate}
|
---|
591 |
|
---|
592 | \fcolorbox{black}{tipcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
593 | Many editors can re-format JSON text which makes it easier to read.
|
---|
594 | Even browsers like Firefox can do this: just drop an unformatted
|
---|
595 | json file into it and you will get a pretty-printed, collapsable presentation.
|
---|
596 | }}
|
---|
597 |
|
---|
598 |
|
---|
599 |
|
---|
600 | \fcolorbox{black}{tipcolor}{\parbox{\dimexpr \linewidth-2\fboxsep-2\fboxrule}{
|
---|
601 | It is recommended that you browse the details of the
|
---|
602 | \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb}{{\gw} wiki page} which contains much more
|
---|
603 | details that may come in handy for your following tasks.
|
---|
604 | }}
|
---|
605 | \newline
|
---|
606 |
|
---|
607 | \emph{ You are now ready to start creating your own negotiation strategy!}
|
---|
608 |
|
---|
609 | \FloatBarrier
|
---|
610 |
|
---|
611 | %%%%%%%%%%%%% LAB 3 %%%%%%%%%%%%%%%%%%%%%%%
|
---|
612 |
|
---|
613 |
|
---|
614 | \section{Opponent Modeling}
|
---|
615 |
|
---|
616 | The negotiation strategy described earlier is very simple and does not result in very Pareto efficient agreements. A typical approach to improve this, and which is used in many negotiation strategies, is to estimate the profile of the other party. This process is called ``Opponent Modeling". A properly estimated profile allows then for instance to generate closer to pareto-optimal bids and to determine the concession strategy used by the other(s).
|
---|
617 |
|
---|
618 | One way to do this is to use the bids received from the other parties.
|
---|
619 | The \code{FrequencyOpponentModel} assumes that others will use preferred issue values more often than less-preferred issue values. Thus, counting how often an opponent uses the issue values gives a way to assess these bids.
|
---|
620 |
|
---|
621 | To do just that, do the following steps:
|
---|
622 | \begin{enumerate}
|
---|
623 | \item Check the \href{https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWeb/browser/opponentmodel/src/main/java/geniusweb/opponentmodel/FrequencyOpponentModel.java}{FrequencyOpponentModel} already available in {\gw}.
|
---|
624 | \item Notice that this model is immutable. Explain how you can update this object to include a new offer received from a participant.
|
---|
625 | \item Add code to your party, that keeps an up-to-date FrequencyOpponentModel for all the participants (you don't need to model yourself).
|
---|
626 | \item Check that your approach works by running your party with another party and displaying the resulting opponent model.
|
---|
627 | \item The FrequencyOpponentModel is also a UtiltySpace and thus implements getUtility. Explain how this function works and if that matches the mechanism described above.
|
---|
628 | \item Add code to your party that prints received bids and the estimated opponent utility yourself for some bids. Explain the results.
|
---|
629 | \item Check whether the estimated opponent profile converges to the actual opponent's model. How is this affected by different types of parties (eg Conceder versus RandomParty versus your own party)?
|
---|
630 | \end{enumerate}
|
---|
631 |
|
---|
632 | \section{Improve the Offer Generating Strategy}
|
---|
633 | We are now going to improve the simple offer generating strategy from the previous lab by using our opponent model. The aim is to obtain Pareto efficient offers.
|
---|
634 | %How can we achieve this?
|
---|
635 |
|
---|
636 | \begin{figure}
|
---|
637 | \centering
|
---|
638 | \includegraphics[width=.7\textwidth]{images/pareto.png}
|
---|
639 | \caption{Finding a Pareto efficient point.}
|
---|
640 | \label{fig:paretofind}
|
---|
641 | \end{figure}
|
---|
642 |
|
---|
643 | Consider the setting in Figure \ref{fig:paretofind}. Suppose that Party 1 is making an offer, and the current target utility is 0.8. That is, it is willing to generate and accept offers with utility 0.8 or more. This is indicated by the black vertical line in Figure \ref{fig:paretofind}. Note that all the offers to the right of the line meet this threshold. Now, in order to find a Pareto efficient offer, all the party needs to do is to find an offer which meets the threshold and has the highest utility for Party 2. If the opponent model is perfect, this is necessarily a Pareto efficient offer. In the example, this would be the green point on the frontier. Obviously, in our case, the opponent model is not necessarily correct, and so the best one can hope for is that, by using this approach, the offer still is approximately Pareto efficient.
|
---|
644 |
|
---|
645 | To implement this approach, there are at least 2 options:
|
---|
646 |
|
---|
647 | \begin{enumerate}
|
---|
648 | \item Loop through all the bids, e.g. use bidspace.GenericPareto, to collect those with a utility equal or better than the target utility for the party, and then choose the one from that set which has highest utility for the opponent according to the opponent model. Realize that iterating through all the bids may already take longer than the total time available in the negotiation.
|
---|
649 |
|
---|
650 | % Looping through all the bids becomes very inefficient with large outcome spaces. It would speed up things a lot by generating a sorted list of offers when initialising the party. However, with large spaces you will quickly run out of time and memory with that approach.
|
---|
651 |
|
---|
652 | \item A quicker but heuristic approach is to test random bids until a bid is found above a threshold. But instead of selecting only 1 random bid, search multiple bids and choose the one with the highest opponent utility. Explain why this may still take very long to find a decent bid.
|
---|
653 | % This mechanism is very inefficient in even finding something with the target utility. Probabilyt that it hits on a utilty within 0.01 of the target is 0.01. In large spaces, it becomes very unlikely that anything near the pareto will be found in this way.
|
---|
654 |
|
---|
655 | \end{enumerate}
|
---|
656 |
|
---|
657 | You may find even better solutions to use for your own party.
|
---|
658 |
|
---|
659 |
|
---|
660 | \section*{Answers to a few exercises}
|
---|
661 | \begin{itemize}
|
---|
662 | \item Exercise~\ref{ex:utility}: Utilities in Outcome ID 8 in Table~\ref{tab:outcomes}.
|
---|
663 | \item Exercise~\ref{ex:pareto}: The Pareto optimal outcomes are are Outcome IDs 3, 12, 15, 18, 17, and 16.
|
---|
664 | \end{itemize}
|
---|
665 |
|
---|
666 | \begin{table}[!htb]
|
---|
667 | \centering
|
---|
668 | \caption{Negotiation outcomes and utilities for the laptop domain.}
|
---|
669 | \begin{tabular}{ |c|l|l|l|l|l|} % <-- Alignments: 1st column left, 2nd middle and 3rd right, with vertical lines in between
|
---|
670 | \hline
|
---|
671 | \textbf{Outcome ID} & \textbf{Price} & \textbf{HardDisk} & \textbf{Monitor} & \textbf{Utility A} & \textbf{Utility B} \\
|
---|
672 | \hline
|
---|
673 | 1 & 1000 & 256GB & 15 & 0.3 & 0.4 \\
|
---|
674 | 2 & 1200 & 256GB & 15 & 0.15 & 0.7 \\
|
---|
675 | 3 & 1400 & 256GB & 15 & 0 & 1 \\
|
---|
676 | 4 & 1000 & 512GB & 15 & 0.54 & 0.3 \\
|
---|
677 | 5 & 1200 & 512GB & 15 & 0.39 & 0.6 \\
|
---|
678 | 6 & 1400 & 512GB & 15 & 0.24 & 0.9 \\
|
---|
679 | 7 & 1000 & 1TB & 15 & 0.7 & 0.2 \\
|
---|
680 | 8 & 1200 & 1TB & 15 & 0.55 & 0.5 \\
|
---|
681 | 9 & 1400 & 1TB & 15 & 0.4 & 0.8 \\
|
---|
682 | 10 & 1000 & 256GB & 17 & 0.6 & 0.3 \\
|
---|
683 | 11 & 1200 & 256GB & 17 & 0.45 & 0.6 \\
|
---|
684 | 12 & 1400 & 256GB & 17 & 0.3 & 0.9 \\
|
---|
685 | 13 & 1000 & 512GB & 17 & 0.84 & 0.2 \\
|
---|
686 | 14 & 1200 & 512GB & 17 & 0.69 & 0.5 \\
|
---|
687 | 15 & 1400 & 512GB & 17 & 0.54 & 0.8 \\
|
---|
688 | 16 & 1000 & 1TB & 17 & 1 & 0.1 \\
|
---|
689 | 17 & 1200 & 1TB & 17 & 0.85 & 0.4 \\
|
---|
690 | 18 & 1400 & 1TB & 17 & 0.7 & 0.7 \\
|
---|
691 | \hline
|
---|
692 | \end{tabular}
|
---|
693 | \label{tab:outcomes}
|
---|
694 | \end{table}
|
---|
695 | \end{document} |
---|