LightVoting
 All Classes Namespaces Files Functions Variables Pages
CVotingAgent.java
Go to the documentation of this file.
1 
24 package org.lightvoting.simulation.agent;
25 
26 import cern.colt.Arrays;
27 import cern.colt.bitvector.BitVector;
28 import com.google.common.util.concurrent.AtomicDoubleArray;
29 import org.lightjason.agentspeak.action.binding.IAgentAction;
30 import org.lightjason.agentspeak.action.binding.IAgentActionFilter;
31 import org.lightjason.agentspeak.action.binding.IAgentActionName;
32 import org.lightjason.agentspeak.agent.IBaseAgent;
33 import org.lightjason.agentspeak.common.CCommon;
34 import org.lightjason.agentspeak.configuration.IAgentConfiguration;
35 import org.lightjason.agentspeak.generator.IBaseAgentGenerator;
36 import org.lightjason.agentspeak.language.CLiteral;
37 import org.lightjason.agentspeak.language.CRawTerm;
38 import org.lightjason.agentspeak.language.ILiteral;
39 import org.lightjason.agentspeak.language.instantiable.plan.trigger.CTrigger;
40 import org.lightjason.agentspeak.language.instantiable.plan.trigger.ITrigger;
41 import org.lightjason.agentspeak.language.score.IAggregation;
46 
47 import java.io.InputStream;
48 import java.text.MessageFormat;
49 import java.util.Collections;
50 import java.util.HashMap;
51 import java.util.LinkedHashMap;
52 import java.util.LinkedList;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Random;
56 import java.util.concurrent.atomic.AtomicIntegerArray;
57 import java.util.concurrent.atomic.AtomicLong;
58 import java.util.concurrent.atomic.AtomicReference;
59 import java.util.stream.Collectors;
60 import java.util.stream.Stream;
61 
62 
67 // annotation to mark the class that actions are inside
68 @IAgentAction
69 public final class CVotingAgent extends IBaseAgent<CVotingAgent>
70 {
71 
75  private final String m_name;
76 
80  private final CEnvironment m_environment;
81 
86 
91  private AtomicIntegerArray m_vote;
92 
96  private final int m_altNum;
97 
101  private final AtomicDoubleArray m_atomicPrefValues;
102 
106  private String m_grouping;
107 
112  private boolean m_voted;
113 
117  private Integer m_joinThreshold;
118  private final BitVector m_bitVote;
119  private final String m_fileName;
120 
132  public CVotingAgent( final String p_name, final IAgentConfiguration<CVotingAgent> p_configuration, final IBaseAgent<CChairAgent> p_chairagent,
133  final CEnvironment p_environment,
134  final int p_altNum,
135  final String p_grouping,
136  final String p_fileName
137  )
138  {
139  super( p_configuration );
140  m_name = p_name;
141  m_environment = p_environment;
142 
143  m_chair = (CChairAgent) p_chairagent;
144  m_storage.put( "chair", p_chairagent.raw() );
145 
146  m_beliefbase.add(
147  CLiteral.from(
148  "chair",
149  CRawTerm.from( p_chairagent )
150  )
151  );
152 
153  // sleep chair, Long.MAX_VALUE -> inf
154  p_chairagent.sleep( Long.MAX_VALUE );
155 
156  m_altNum = p_altNum;
157 
158  m_atomicPrefValues = this.generatePreferences( m_altNum );
159  m_vote = this.convertPreferences( m_atomicPrefValues );
160  m_bitVote = this.convertPreferencesToBits( m_atomicPrefValues );
161  m_voted = false;
162  m_joinThreshold = 5;
163  m_grouping = p_grouping;
164  m_fileName = p_fileName;
165  }
166 
167  // overload agent-cycle
168  @Override
169  public final CVotingAgent call() throws Exception
170  {
171  // run default cycle
172  return super.call();
173  }
174 
175  // public methods
176 
182  public final String name()
183  {
184  return m_name;
185  }
186 
193  {
194  return m_chair;
195  }
196 
197  public AtomicIntegerArray getVote()
198  {
199  return m_vote;
200  }
201 
202  public BitVector getBitVote()
203  {
204  return m_bitVote;
205  }
206 
207  // agent actions
208 
209  @IAgentActionFilter
210  @IAgentActionName( name = "perceive/env" )
211  private void perceiveEnv()
212  {
213  this.beliefbase().add( m_environment.literal( this ) );
214  System.out.println( this.name() + " perceived environment " );
215  }
216 
217  @IAgentActionFilter
218  @IAgentActionName( name = "join/group" )
219  private void joinGroup()
220  {
221  if ( "RANDOM".equals( m_grouping ) )
222  this.joinGroupRandom();
223 
224  if ( "COORDINATED".equals( m_grouping ) )
225  this.joinGroupCoordinated();
226  }
227 
228  @IAgentActionFilter
229  @IAgentActionName( name = "submit/vote" )
230  private void submitVote( final CChairAgent p_chairAgent )
231  {
232  if ( m_voted )
233  return;
234 
235  p_chairAgent.trigger(
236  CTrigger.from(
237  ITrigger.EType.ADDGOAL,
238  CLiteral.from(
239  "vote/received",
240  CRawTerm.from( this.name() ),
241  CRawTerm.from( this.getBitVote() )
242  )
243  )
244  );
245 
246  m_voted = true;
247 
248  }
249 
250  @IAgentActionFilter
251  @IAgentActionName( name = "submit/dissatisfaction" )
252  private void submitDiss( final CChairAgent p_chairAgent, final Integer p_iteration, final BitVector p_result ) throws InterruptedException
253  {
254  p_chairAgent.trigger(
255  CTrigger.from(
256  ITrigger.EType.ADDGOAL,
257  CLiteral.from(
258  "diss/received",
259  CRawTerm.from( this.name() ),
260  CRawTerm.from( this.computeDissBV( p_result ) ),
261  CRawTerm.from( p_iteration )
262  )
263  )
264  );
265  }
266 
267  private Double computeDissBV( final BitVector p_result )
268  {
269  double l_diss = 0;
270 
271  for ( int i = 0; i < p_result.size(); i++ )
272  {
273  if ( p_result.get( i ) )
274  l_diss = l_diss + ( 1 - m_atomicPrefValues.get( i ) );
275  }
276  return l_diss;
277  }
278 
279  // private methods
280 
281  private AtomicDoubleArray generatePreferences( final int p_altNum )
282  {
283  final Random l_random = new Random();
284  final double[] l_prefValues = new double[m_altNum];
285  for ( int i = 0; i < m_altNum; i++ )
286  l_prefValues[i] = this.sigmoidValue( l_random.nextDouble() - 0.5 );
287  System.out.println( "Preference Values: " + Arrays.toString( l_prefValues ) );
288  return new AtomicDoubleArray( l_prefValues );
289  }
290 
291  private double sigmoidValue( double p_var )
292  {
293  return 1 / ( 1 + Math.pow( Math.E, -1 * p_var ) );
294  }
295 
296  private AtomicIntegerArray convertPreferences( final AtomicDoubleArray p_atomicPrefValues )
297  {
298  final int[] l_voteValues = new int[m_altNum];
299  for ( int i = 0; i < m_altNum; i++ )
300  if ( p_atomicPrefValues.get( i ) > 0.5 )
301  l_voteValues[i] = 1;
302  else
303  l_voteValues[i] = 0;
304  System.out.println( "Vote: " + Arrays.toString( l_voteValues ) );
305  return new AtomicIntegerArray( l_voteValues );
306  }
307 
308  private BitVector convertPreferencesToBits( final AtomicDoubleArray p_atomicPrefValues )
309  {
310  final BitVector l_voteValues = new BitVector( m_altNum );
311  for ( int i = 0; i < m_altNum; i++ )
312  if ( p_atomicPrefValues.get( i ) > 0.5 )
313  l_voteValues.put( i, true );
314  else
315  l_voteValues.put( i, false );
316  System.out.println( "Vote as BitVector: " + l_voteValues );
317  return l_voteValues;
318  }
319 
320 
321  private List<CGroup> determineActiveGroups()
322  {
323  final AtomicReference<List<CGroup>> l_groupList = new AtomicReference<>();
324 
325  m_beliefbase.beliefbase().literal( "groups" ).stream().forEach( i ->
326  l_groupList.set( ( (ILiteral) i ).values().findFirst().get().raw() ) );
327 
328  final List<CGroup> l_activeGroups = new LinkedList<>();
329 
330  for ( int i = 0; i < l_groupList.get().size(); i++ )
331  if ( l_groupList.get().get( i ).open() )
332  {
333  l_activeGroups.add( l_groupList.get().get( i ) );
334  }
335 
336  return l_activeGroups;
337  }
338 
339  private void openNewGroup()
340  {
341  final CGroup l_group;
342 
343  if ( "RANDOM".equals( m_grouping ) )
344  l_group = m_environment.openNewGroupRandom( this );
345 
346  else
347  l_group = m_environment.openNewGroupCoordinated( this );
348 
349  this.beliefbase().add( l_group.literal( this ) );
350  System.out.println( "opened new group " + l_group );
351  }
352 
353  private void joinGroupRandom()
354  {
355 
356  final List<CGroup> l_activeGroups = this.determineActiveGroups();
357 
358  if ( l_activeGroups.isEmpty() )
359  {
360  this.openNewGroup();
361  return;
362  }
363 
364  final Random l_rand = new Random();
365 
366  final CGroup l_randomGroup = l_activeGroups.get( l_rand.nextInt( l_activeGroups.size() ) );
367  m_environment.addAgentRandom( l_randomGroup, this );
368  this.beliefbase().add( l_randomGroup.literal( this ) );
369 
370  }
371 
372  private void joinGroupCoordinated()
373  {
374  System.out.println( "join group according to coordinated grouping algorithm" );
375 
376  final List<CGroup> l_activeGroups = this.determineActiveGroups();
377 
378  if ( l_activeGroups.isEmpty() )
379  {
380  this.openNewGroup();
381  return;
382  }
383 
384  else
385  this.determineGroupCoordinated( l_activeGroups );
386  }
387 
388  private void determineGroupCoordinated( final List<CGroup> p_activeGroups )
389  {
390  final CGroup l_group;
391  // choose group to join
392  final Map<CGroup, Integer> l_groupDistances = new HashMap<>();
393  final BitVector l_vote = this.getBitVote();
394  System.out.println( "Vote: " + l_vote );
395  for ( int i = 0; i < p_activeGroups.size(); i++ )
396  {
397  final BitVector l_com = p_activeGroups.get( i ).result();
398  System.out.println( "Committee: " + l_com );
399 
400  System.out.println( "Vote: " + l_vote );
401  System.out.println( "Committee: " + l_com );
402  l_com.xor( l_vote );
403  final int l_HD = l_com.cardinality();
404  System.out.println( "Hamming distance: " + l_HD );
405  l_groupDistances.put( p_activeGroups.get( i ), l_HD );
406  }
407  final Map l_sortedDistances = this.sortMapDESC( l_groupDistances );
408  final Map.Entry<CGroup, Integer> l_entry = (Map.Entry<CGroup, Integer>) l_sortedDistances.entrySet().iterator().next();
409  l_group = l_entry.getKey();
410 
411  // if Hamming distance is above the threshold, do not join the chair but create a new group
412  if ( l_entry.getValue() > m_joinThreshold )
413  {
414  this.openNewGroup();
415  return;
416  }
417  m_environment.addAgentCoordinated( l_group, this );
418  this.beliefbase().add( l_group.literal( this ) );
419  System.out.println( this.name() + " joins group " + l_group );
420  }
421 
429  private double computeDiss( final int[] p_resultValues )
430  {
431  double l_diss = 0;
432 
433  for ( int i = 0; i < p_resultValues.length; i++ )
434  {
435  if ( p_resultValues[i] == 1 )
436  l_diss = l_diss + ( 1 - m_atomicPrefValues.get( i ) );
437  }
438  return l_diss;
439  }
440 
441 
442 
443  private Map sortMapDESC( final Map<CGroup, Integer> p_valuesMap )
444  {
445  final List<Map.Entry<CGroup, Integer>> l_list = new LinkedList<>( p_valuesMap.entrySet() );
446 
447  /* Sorting the list based on values in descending order */
448 
449  Collections.sort( l_list, ( p_first, p_second ) ->
450  p_second.getValue().compareTo( p_first.getValue() ) );
451 
452  /* Maintaining insertion order with the help of LinkedList */
453 
454  final Map<CGroup, Integer> l_sortedMap = new LinkedHashMap<>();
455  for ( final Map.Entry<CGroup, Integer> l_entry : l_list )
456  {
457  l_sortedMap.put( l_entry.getKey(), l_entry.getValue() );
458  }
459 
460  return l_sortedMap;
461  }
462 
466  public static final class CVotingAgentGenerator extends IBaseAgentGenerator<CVotingAgent>
467  {
468 
472  private final CSend m_send;
473 
477  private final AtomicLong m_agentcounter = new AtomicLong();
478 
483 
487  private final int m_altNum;
488  private final String m_grouping;
489  private final String m_fileName;
490 
499  public CVotingAgentGenerator( final CSend p_send, final InputStream p_stream, final CEnvironment p_environment, final int p_altNum,
500  final String p_grouping,
501  final String p_fileName
502  ) throws Exception
503  {
504 
505  super(
506  // input ASL stream
507  p_stream,
508 
509  // a set with all possible actions for the agent
510  Stream.concat(
511  // we use all build-in actions of LightJason
512  CCommon.actionsFromPackage(),
513  Stream.concat(
514  // use the actions which are defined inside the agent class
515  CCommon.actionsFromAgentClass( CVotingAgent.class ),
516  // add VotingAgent related external actions
517  Stream.of(
518  p_send
519  )
520  )
521  // build the set with a collector
522  ).collect( Collectors.toSet() ),
523 
524  // aggregation function for the optimization function, here
525  // we use an empty function
526  IAggregation.EMPTY,
527 
528  // variable builder
529  new CVariableBuilder( p_environment )
530  );
531 
532  m_send = p_send;
533  m_environment = p_environment;
534  m_altNum = p_altNum;
535  m_grouping = p_grouping;
536  m_fileName = p_fileName;
537  }
538 
539  // unregister an agent
540  // @param p_agent agent object
541  public final void unregister( final CVotingAgent p_agent )
542  {
543  m_send.unregister( p_agent );
544  }
545 
546  // generator method of the agent
547  // @param p_data any data which can be put from outside to the generator method
548  // @return returns an agent
549  @Override
550  public final CVotingAgent generatesingle( final Object... p_data )
551  {
552  // register a new agent object at the send action and the register
553  // method retruns the object reference
554 
555  final CVotingAgent l_votingAgent = new CVotingAgent(
556 
557  // create a string with the agent name "agent <number>"
558  // get the value of the counter first and increment, build the agent
559  // name with message format (see Java documentation)
560  MessageFormat.format( "agent {0}", m_agentcounter.getAndIncrement() ),
561 
562  // add the agent configuration
563  m_configuration,
564  // add the chair agent
565  ( (CChairAgent.CChairAgentGenerator) p_data[0] ).generatesingle(),
567  m_altNum,
568  m_grouping,
569  m_fileName
570  );
571 
572  l_votingAgent.sleep( Integer.MAX_VALUE );
573  m_environment.initialset( l_votingAgent );
574  return m_send.register( l_votingAgent );
575 
576  }
577  }
578 }
579 
580 
boolean m_voted
variable indicating if agent already submitted its vote
Map sortMapDESC(final Map< CGroup, Integer > p_valuesMap)
final AtomicLong m_agentcounter
Current free agent id, needs to be thread-safe, therefore using AtomicLong.
CChairAgent m_chair
associated chair agent;
void submitDiss(final CChairAgent p_chairAgent, final Integer p_iteration, final BitVector p_result)
final String name()
Get agent's name.
Double computeDissBV(final BitVector p_result)
ILiteral literal(final CVotingAgent p_votingAgent)
returns literal representation of existing groups
final int m_altNum
number of alternatives
BitVector convertPreferencesToBits(final AtomicDoubleArray p_atomicPrefValues)
void submitVote(final CChairAgent p_chairAgent)
AtomicDoubleArray generatePreferences(final int p_altNum)
void determineGroupCoordinated(final List< CGroup > p_activeGroups)
CChairAgent getChair()
get associated chair agent
AtomicIntegerArray m_vote
agent's vote
CVotingAgent(final String p_name, final IAgentConfiguration< CVotingAgent > p_configuration, final IBaseAgent< CChairAgent > p_chairagent, final CEnvironment p_environment, final int p_altNum, final String p_grouping, final String p_fileName)
constructor of the agent
Created by sophie on 21.02.17.
final CEnvironment m_environment
environment
final CSend m_send
Store reference to send action to registered agents upon creation.
Action to send messages for communication.
Definition: CSend.java:49
CVotingAgentGenerator(final CSend p_send, final InputStream p_stream, final CEnvironment p_environment, final int p_altNum, final String p_grouping, final String p_fileName)
constructor of the generator
final AtomicDoubleArray m_atomicPrefValues
agent's preferences
Integer m_joinThreshold
threshold for joining a group in the case of coordinated grouping
String m_grouping
grouping algorithm: "RANDOM" or "COORDINATED"
Created by sophie on 24.04.17.
Definition: CGroup.java:42
double computeDiss(final int[] p_resultValues)
compute dissatisfaction of voter with given committee
BDI agent with voting capabilities.
final String m_name
name of the agent
AtomicIntegerArray convertPreferences(final AtomicDoubleArray p_atomicPrefValues)