Version 192 (modified by wouter, 5 years ago) ( diff )

--

GENIUS 2

Genius2 is an open architecture for heterogeneous negotiating parties via the internet. It provides the basis for an implementation of a testbed for negotiating parties that includes a set of negotiation problems for benchmarking parties, a library of negotiation strategies, and analytical tools to evaluate an party's performance and their strategies allows user.

Genius2 overview

Genius2 contains a number of components

name description more information
the core the data structures for issues, values, bids, profiles, events and actions, parties etc.here
profilesservera web server that provides profiles and domain descriptionsprofiles server
partiesserverA web server that provides instances of running parties to use for negotiationparties server
runserverA web server that can run sessions and tournamentsrun server

You can either run these servers locally on either your computer or on your web server, or use a public server somewhere else.

For creating your own profile or party, you only need the core.

If you want to get a quick top-down idea how Genius2 works, we suggest to install the servers and contact the runserver to run a session.

Installation

The Genius2 core code does not need to be installed. Check the profilesserver, partiesserver and runserver wiki pages (see above) for installation details.

Server choice, privacy and security

You can choose to either run services privately, on your intranet, or completely open on the internet.

privacy need configuration
completely private, no sharing with others All services run on tomcat server(s) on your computer or intranet. Outside network access is blocked. You need copies of the profiles and parties (jar files) you want to run.
dedicated business negotiation, competition : share your party with the world, but not its code You host your own partiesserver that is connected to the web*.
shared profile profilesserver on own server or on an existing profilesserver on the web*
public party with built-in private profileHard-code the profile in the party. Party runs as in competition.
semi-private sessions/tournamentsRun your own runserver, possibly even from behind a firewall or on your local computer. Existing parties- and profilesservers are used in your runs. Negotiation results are handled only on your computer and not visible outside. Others might be able to see parties act on your behalf, but it's not trivial to determine that they are negotiating against each other, especially if these parties are on different partiesservers.
run your own protocolsas with running semi-private sessions/tournaments
  • All firewalls above the server have to be configured appropriately, most corporations have firewalls between your computer and inside the company there is only connection between company computers.

Core overview

This section gives an overview of the core functionalties.

The image below gives an overview class diagram with the genius2 core modules and their functionalities. The figure also shows the 3 servers and the functions inside those. Click on the figure to zoom in.

source:design/classdiagram.svg

All classes are documented in detail in the javadoc with the code. Also the serialization is annotated with the classes. Here we just give a brief overview. Later in this document we explain the serialization of a domain and profile. Also check the examples to see how this works and what typical values look like.

issuevalue

This module contains the basic objects that make up a bid: issues, values, domains, and bids. The issues are just String objects. There are 2 types of values: numeric and discrete. DiscreteValueSet contains the list of DiscreteValue which is a basically a possible (string) values for the issue. NumberValueSet contains a Range which is a set of numbers defined by the minimum, maximum and stepsize (upwards from the minimum). A Domain contains a map, with each key the issue (string) and the value a ValueSet. Finally, a Bid is a Map where the key is the issue and the value either a DiscreteValue or NumberValue that is valid for that issue.

Your party can create any bid that it likes but the protocol will check if your bid fits in the current negotiation and may kick you out of the negotiation if you don't behave properly.

All number values are computed as BigDecimal to avoid rounding errors.

profile

A profile is a function that can tell if a bid is preferred over another bid. There are a number of ways to do this:

  • FullOrdering: this provides a function isPreferredOrEqual() that can tell if a bid is preferred over another
  • PartialOrdering: as FullyOrderedSpace, but may not know the answer for part of the bids
  • UtilitySpace: as FullOrdering, but additionally this provides a function getUtility(bid) that maps the bid into a BigDecimal in [0,1]. The higher the value, the more preferred is that bid. The Linear Additive Space is the most commonly used UtilitySpace. If the accuracy of BigDecimal is not needed, you can just call BigDecimal#doubleValue() to get a standard java double.

Party

The party module contains basic interfaces for implementing your negotiation party. The main class is Party, which defines the basic functionality required for every negotiation party. The heart of the party is that your Party implements Connectable<Inform,Action>. Connectable contains 2 main functions: connect and disconnect. When connect is called, your party receives a Connection over which it receives Inform objects and can send Action objects. What exactly is sent and received is determined by the protocol (see the protocol section below). For example if the SAOP protocol is used, your party will receive a YourTurn message, after which it decides on its action and sends it into the Connection. See also the example party discussed below.

The Inform objects are all available inside the inform package inside the party module.

Timeline

The timeline contains a deadline and a progress object. The deadline indicates how much time a negotiation session can take. The progress indicates where currently running session is towards the deadline.

References

Parties, domains, profiles and protocols are stored and used on remote machines. We use IRI's (internationalized resource identifier, which looks similar to the well known URLs you use in your web browser) to refer to them. These IRI's are packed inside objects like the PartyRef, ProtocolRef, ProfileRef, DomainRef so that it is clear what type of object the IRI is referring to. For example, when your party is initialized, it usually receives a Settings object that contains a ProfileRef. The intention is that the party fetches the actual profile from the web, using the IRI in the ProtocolRef. See the example party below for an example.

There are a number of schemes used for references:

schemeused withexamplecomments
http:partyhttp://localhost:8080/partiesserver/run/randompyparty-1.0.0
ws:profilews://localhost:8080/profilesserver/websocket/get/jobs/jobs1.json
file:profilefile:src/test/settings.json gives file relative to local current working dir
classpath:partyclasspath:genius2.exampleparties.randomparty.RandomPartymust be in classpath

BidSpace

The bidspace module contains functionality to support building a negotiation party. We currently have

  • OpponentModel: this is a category of classes that can estimate the opponent's profile from the bids that he places.
  • Pareto: this is a category of classes that can compute the pareto frontier from a set of profiles. Pareto optimality is an important mechanism to place optimal bids.
  • AllBidsList: this can be used to craete a list containing all possible bids in a domain. This list is created in a lazy way, and competely avoids storing the whole list in memory (which might not even fit)

Protocol

The protocol module contains the functionality to define and execute a negotiation protocol. There are session protocols and tournament protocols.

The basic classes defining a protocol are:

  • The NegoSettings: these define the settings for the protocol, such as the deadline, the participants and the profile
  • The NegoState: this contains the current state of execution of the protocol. To give some example states: "waiting for bid from party 2", "ended with agreement", "ended because party 1 broke the protocol". A state also has various with() functions defining the new state from an old state and a party doing an action.
  • The NegoProtocol: this defines what happens when a negotiation starts, when a participant enters halfway the session, what are the allowed actions, what happens if a participant breaks the protocol, etc.

Writing a party in Java

Example parties can be found here. A party is compiled with maven. After compilation (mvn package) you get a target/yourparty-X.Y.Z-jar-with-dependencies.jar that can be copied into the parties server for deployment.

The basic structure of an party looks like this

public class RandomParty extends DefaultParty {
	@Override
	public void notifyChange(Inform info) {
		if (info instanceof Settings) {
			Settings settings = (Settings) info;
			profileint = getProfile(settings.getProfile().getURI());
			this.me = settings.getID();
			this.progress = settings.getProgress();
		} else if (info instanceof ActionDone) {
			Action otheract = ((ActionDone) info).getAction();
			if (otheract instanceof Offer) {
				lastReceivedBid = ((Offer) otheract).getBid();
			}
		} else if (info instanceof YourTurn) {
			myTurn();
			if (progress instanceof ProgressRounds) {
				progress = ((ProgressRounds) progress).advance();
			}
		} else if (info instanceof Finished) {
			getReporter().log(Level.INFO, "Final ourcome:" + info);
		}
	}


	private void myTurn() {
		if (lastReceivedBid != null && profileint.getProfile()
				.getUtility(lastReceivedBid).doubleValue() > 0.6) {
			action = new Accept(me, lastReceivedBid);
		} else {
			// for demo. Obviously full bids have higher util in general
			AllPartialBidsList bidspace = new AllPartialBidsList(
					profileint.getProfile().getDomain());
			long i = random.nextInt(bidspace.size().intValue());
			action = new Offer(me, bidspace.get(BigInteger.valueOf(i)));
		}
		getConnection().send(action);

	}

	@Override
	public Capabilities getCapabilities() {
			return new Capabilities(new HashSet<>(
					Arrays.asList(new ProtocolRef(new URI("SAOP")))));
	}

	@Override
	public String getDescription() {
		return "places random bids until it can accept an offer with utility >0.6";
	}

}

Preparing the jar file

In order to put your party on the partiesserver for running, you need a jar file of your party.

Normally the party includes all dependencies. The jar files are loaded with an isolated jar class loader that should avoid collisions with possibly identically named but possibly different packages in other jar files.

Party jar files must have a Main-Class set in the MANIFEST.MF file. This main-class must implement Party and have a no-arg constructor.

The example randomparty does this from the maven build script.

We recommend to do initialization of the party only in the init() and not in the constructor or static code. This because instances of your class can be made both for extracting general info as getDescription(), or to really run your class.

Writing a party in Python

We provide a python-to-java adapter so that you can easily write your party in python instead of Java. You can check the full working example code here. A python-based party looks like this:

class RandomParty (DefaultParty):
	def notifyChange(self, info):
		if isinstance(info, Settings) :
			self.profile = ProfileConnectionFactory.create(info.getProfile().getURI(), self.getReporter());
			self.me = info.getID()
			self.progress = info.getProgress()
		elif isinstance(info , ActionDone): 
			self.lastActor = info.getAction().getActor()
			otheract = info.getAction()
			if isinstance(otheract, Offer):
				self.lastReceivedBid = otheract.getBid()
		elif isinstance(info , YourTurn):
			self._myTurn()
			if isinstance(self.progress, ProgressRounds) :
				self.progress = self.progress.advance();

	def getCapabilities(self): # -> Capabilities
		return Capabilities(HashSet([ ProtocolRef(URI("SAOP"))]))

	def getDescription(self):
		return "places random bids until it can accept an offer with utility >0.6. Python version"
	
	def terminate(self):
		self.profile.disconnect()

	def _myTurn(self):
		if self.lastReceivedBid != None and self.profile.getProfile().getUtility(self.lastReceivedBid).doubleValue() > 0.6:
			action = Accept(self.me, self.lastReceivedBid)
		else:
			bidspace = AllBidsList(self._getProfile().getDomain())
			i = self.random.nextInt(bidspace.size()) # warning: jython implicitly converts BigInteger to long.
			action = Offer(self.me, bidspace.get(BigInteger.valueOf(i)))
		self.getConnection().send(action)

   ...

You need to wrap your python party into a jar wrapper to get it accepted by the parties server. To do this, check the javadoc with the PythonPartyAdapter.

Writing a party in other languages

If you want to use another language than java or python2 to write your parties, you have a number of options

  • Make your own adapter that runs your language from Java. Check our pythonadapter for an example how this can be done.
  • Write your own partiesserver that correctly implements the partiesserver interface. This boils down to creating a webserver that correctly can handle calls to a number of prescribed URLs and websockets.

JSON layout

This section discusses how domains and profiles are looking in JSON. JSON is the communication format, java instances of Domain, Profile and Bid can easily be converted back and forth to JSON using the jackson objectmapper (see the test and example codes)

Create a domain

There is not yet a domain editor available so you have to create domains by manually editing a file with JSON code.

A domain looks like this

{"name":"jobs",
 "issuesValues":{
  "lease car":{"values":["yes","no"]},
  "permanent contract":{"values":["yes","no"]},
  "career development opportunities":{"values":["low","medium","high"]},
  "fte":{"values":["0.6","0.8","1.0"]},
  "salary":{"values":["2000","2500","3000","3500","4000"]},
  "work from home":{"values":["0","1","2"]}
 }
}
  • The name is just a string. It must match the filename and directory name when placed on the profiles server. So your directory must be domainsrepo/jobs and the filename must be jobs.json.
  • The issueValues contains a dictionary with issues. Each issue is indicated by a name (a string), followed by a column (:) and then a dictionary. The dictionary can contain either a discrete valueset or a number valueset
    • a discrete valueset looks like "values", a column and then a list of discrete values (all strings)
    • a number valueset contains a range of numbers and looks like {"range":["12.2","12.6","0.3"]} so "range:" followed by a list of 3 values. The first value is the minimum value in the range, the second the maximum value in the range, and the third the step value. This example range thus contains 12.2 and 12.5 (the next one would be 12.8 but that is already outside the range).

Create a profile

A profile is usually created after the domain. An example linear additive profile looks like this

{
	"LinearAdditiveUtilitySpace": {
		"issueUtilities": {
			"salary": {
				"discreteutils": {
					"valueUtilities": {
						"2000": 0,
						"2500": 0.25,
						"3000": 0.3,
						"3500": 0.75,
						"4000": 1.0
					}
				}
			},
			"fte": {
				"discreteutils": {
					"valueUtilities": {
						"0.6": 0.25,
						"0.8": 0.5,
						"1.0": 0.75
					}
				}
			},
			"work from home": {
.....
					}
				}
			}
		},
		"issueWeights": {
			"salary": 0.24,
			"fte": 0.32,
			"work from home": 0.18,
			"lease car": 0.06,
			"permanent contract": 0.16,
			"career development opportunities": 0.04
		},
		"domain": {
			"name": "jobs", .....
		},
		"name": "jobs1"
	}
}

This is an "LinearAdditiveYtilitySpace", other utilityspaces would have different contents. It contains a number of components:

  • issueUtilities. Here, each issue value from the domain gets a utility assigned. The two types of issue values each having their matching utility function:
Issue Value class Utility class json serialization example

DiscreteValue

DiscreteValueSetUtilities

"discreteutils": {

"valueUtilities": {

"2000": 0, "2500": 0.25, "3000": 0.3, "3500": 0.75, "4000": 1.0

}

}

NumberValue

NumberValueSetUtilities

numberutils": {

"lowValue":12, "lowUtility":0.3, "highValue":18, "highUtility":0.6

}

  • issueWeights: this is a map, for each issue there is a value in [0,1] and the sum of the weights in this map must be exactly 1.
  • domain: identical to above. We replaced some issueValues with "..." in this example .
  • name: a simple string with the name of the profile. The name must match the filename of the profile.

Bids

A Bid is a map with the issue names as keys (strings) and the values being an element from the valueset as in the domain description.

A bid typically looks like

{ "salary":"2000", "fte":"0.8", "work from home","0.6" ...}

Value workaround

This example also illustrates a problem with the json parser. In our jobs example domain, fte is a discrete value, so "0.8" is just a string, not a number. However if fte would be a numeric issue, we naively would write the 0.8 without quotes: fte:0.8. However due to the way the json parser works, we would get 0.8 parsed as s double. This introduces rounding errors. And these cause all kinds of problems.

To avoid these rounding errors, the solution is to store the decimal value also in a string. We can then later convert it properly.

However if we store it in a string, we would be unable to distinguish it from a discrete value. To work around this, we put it in a string and prepend it with a "=".

  • "0.8": the DiscreteValue
  • "=0.8": the NumberValue.

This workaround is only needed for issue values like in bids. In other places where we can *only* have double values, we can work around the issue without needing the string trick.

Stand-alone Running

For stand-alone running you need to have the following available in your project space (this includes the maven dependencies you have set for your project)

  • The parties you want to run (in compiled form) must be in your classpath
  • The profiles you want to provide to the parties (alternatively you can refer to a profile on a running profile server)
  • A settings.json file containing the SessionSettings eg SAOP Settings. view example file.
  • a simple stand-alone runner, eg download from genius2 artifactory select latest version simplerunner-<latestversion>-jar-with-dependencies.jar.

A complete example is available simplerunner here

Running

Make sure your parties are in the java classpath.

A stand-alone runner can now be started as follows (using the example, set your working directory to the root of the simplerunner project )

java -jar simplerunner...with-dependencies.jar src/test/resources/settings.json

When the protocol is completed, the runner prints out the final state which usually contains all actions that were done as well.

Debugging

Debugging can be done in several ways

  • From Eclipse EE:
    • Place your party source code in the Eclipse workspace
    • Place a breakpoint in your party's code
    • Run the partiesserver directly from Eclipse EE , in Debug mode.
    • Run a session or tournament in which you want to debug your party.
    • Eclipse will halt server execution and switch to debugging when it hits your breakpoint
    • Be aware of the automatic time-outs that will still be enforced while you are debugging. This includes tomcat session time-outs.
  • Using a stand-alone runner (normal Eclipse for Java developers, no EE needed)
    • Have your party's source code in the Eclipse workspace
    • Run the stand-alone command directly in your eclipse workspace
    • Eclipse will halt server execution and switch to debugging when it hits your breakpoint
    • time-outs may still be enforced while you are debugging but you avoid the Tomcat session time-out.
    • With stand-alone runner, your parties are run together in a single classloader. This is different from running in the partiesserver.

Genius2 sources

downloading source code

You can browse the genius2 core sources directly using the browse button at the right top of this page.

You can download the source code of this component from

https://tracinsy.ewi.tudelft.nl/svn/genius2/

Normal developers that write new parties do not need to install the genius2 source code. This is only needed if you want to debug/trace into the genius2 code for instance for debugging or understanding the inner workings of genius2.

Import all sources in Eclipse

Install Subclipse using "help/Eclipse MarketPlace" and search for subclipse. Disable the JavaHL native DLLs and install. NOTE: due to a bug in Eclipse Photon the marketplace may not work. We suggest to upgrade...

You may get some errors on JavaHL library. To get rid of those, go to preferences/Team/SVN/

  • disable General SVN settings / JavaHL
  • SVN interface/Client: select SVNKit instead of JavaHL.

Right click in Package Explorer in Eclipse, select "Import/SVN/Checkout Projects from SVN". Select the root of the project, finish (selecting sub-projects will result in a stupid loop in the checkout procedure in Eclipse and won't lead anywhere...)

Richt click on the checked-out project that you want eclipse to recognise as Maven project (the project are maven but Eclipse does not recognise this after check-out). Select import/maven/existing maven projects. Finish.

Note. Eclipse does not automatically recognise this as a maven project because Eclipse supports this only with GIT repositories while we use SVN.

Attachments (6)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.