Banner
Mate Choice


The KH-Model

Spring 2012

Psychology 120


Introduction

Using models to understand systems and processes such as human mate choice involves working from the simple to the more complex.  The reason we must do this is that if we start with a model that is too complex, it can be almost as hard to analyze and understand as mate choice involving people.  So, we will start by re-creating Kalick & Hamilton’s (1986) original model.  Once we have the original model, we can explore it and add more realistic features piece by piece and determine how different features change the behavior of the family of models we build.

Interface

Let’s start by creating the basic interface using our flocking model (note that we have changed the Particles class to a class named Agent). We will add some control variables (Note: Here is the version we were up to on last Thursday, May 17):

      /**
       * Mate Choice Variables
       */

	public final static int NO_MATE_CHOICE = 0; // a constant
	public final static int KH = 1;  // a constant

	public int simulationType = NO_MATE_CHOICE; //default value

Next, we are going to add new types of get and set methods, which will allow us to build a series of models and decide which we will use. We will start with the either no model or the KH model. Here is the new code:


 

	public int getsimulationType(){	return simulationType;}

	public void setsimulationType(int b){
		simulationType = b;
		switch(b) {
		case 0: simulationType = NO_MATE_CHOICE; break;
                                // no mate choice model model
		case 1: simulationType = KH; break; // Sexual
		default: simulationType = KH;
		}
	}

	public Object domsimulationType()
	{
		return new String[] {"No mate choice", "KH Mate Choice"};
	}

 


Let’s run the model to see what the new interface looks like.

Now, we can pass the variable simulationType to each agent upon creation and modify the step method to run either no model or run the mate choice model we will build. If we run the program and choose “KH Mate Choice”, we see that all our simulation does is to place the agents into space, but they do not do anything.

Attractiveness

The next element of the model we can add is the attractiveness of agents. There are many ways to do this, but Kalick & Hamilton’s (1986) gave each agent a score between 1 and 10 and these scores were assigned randomly. We can do this at the time each agent is created:


 

public Bag makeKHAgents(int n, int sex){
		Bag b = new Bag(n);
		for(int i=0;i<n;i++){
			Agent a = placeAgents();
			int xdir = random.nextInt(3)-1;
			int ydir = random.nextInt(3)-1;
			a.setXYdir(xdir, ydir);
			a.setavoidCollisions(avoidCollisions);
			a.setboundaries(boundaries);
			a.setprobabilityOfChange(probabilityOfChange);
			a.setunoccupied(unoccupied);
			a.setaggregae(aggregate);
			a.setsearchRadius(searchRadius);
			a.setflock(flock);
			a.setsimulationType(simulationType);
			a.setsex(sex);
			double attractiveness = Math.floor(random.nextDouble()*maxAttractiveness)+1;
			a.setattractiveness(attractiveness);
			a.setdMax(dMax);
			a.setaMax(maxAttractiveness);
			a.setchoosiness(choosiness);
			b.add(a);
		}

		return b;
	}

 


We can then re-write our start method as follows:


 

	public void start(){	
		super.start();
		particleSpace = new SparseGrid2D(gridWidth, gridHeight); //create a 2D
		//space for our agents.
		if(simulationType != NO_MATE_CHOICE){
			n = males + females; //set n to the correct number to be tested
		}
		if(unoccupied && n > gridWidth*gridWidth){ //to many agents for	
			System.err.println("To many agents!");
		} 
		else {
			
			if(simulationType == NO_MATE_CHOICE){
				noModelAgents();
			}
			else if(simulationType == KH){
				
				malePopulation = makeKHAgents(males, MALE);
				femalePopulation = makeKHAgents(females, FEMALE);
				Observer o = new Observer(this);
				o.event = schedule.scheduleRepeating(o);//make sure it is last
				
			}
		}
	}

 


and lets write attractiveness into the color of agents:


	public void setupPortrayals() {
		SimulationEnvironment se = (SimulationEnvironment)state;
		particlesPortrayal.setField(se.particleSpace);
		if(se.simulationType == se.NO_MATE_CHOICE){
			OvalPortrayal2D o = new OvalPortrayal2D(Color.red);
			particlesPortrayal.setPortrayalForAll(o);
		}
		else if(se.simulationType == se.KH){
			Bag males = se.malePopulation;
			for(int i = 0; i<males.numObjs;i++){
				Agent a = (Agent)males.objs[i];
				float attractiveness = (float)a.attractiveness;
				Color m = new Color((float)0,(float)0,(float)1,
                                      (float)(attractiveness/se.maxAttractiveness));
				OvalPortrayal2D p = new OvalPortrayal2D(m);
				particlesPortrayal.setPortrayalForObject(a, p);
			}

			Bag females = se.femalePopulation;
			for(int i = 0; i<females.numObjs;i++){
				Agent a = (Agent)females.objs[i];
				float attractiveness = (float)a.attractiveness;
				Color f = new Color((float)1,(float)0,(float)0,
                                    (float)(attractiveness/se.maxAttractiveness));
				OvalPortrayal2D p = new OvalPortrayal2D(f);
				particlesPortrayal.setPortrayalForObject(a, p);
			}
		}
		display.reset();
		display.repaint();
	}


Decision Rules

Kalick & Hamilton’s (1986) had two main decision rule that we will implement (they had a third, but it is odd to say the least). In their general form we have an equation for choosing the most attractive partner and one for choosing the most similar.

 

 

 

 

and

 

 

 

where A is attractiveness with other, self, and maximum attractiveness are indicated by subscripts and d is the number of dates with the maximum and the number of actual dates are indicated by subscripts.

Let’s implement these rules in our model.

These two rule can be written in Java as:


 

	double dMax;
	double d = 0;
	double aMax;

		public double chooseTheBest(Agent other){

		double pBest = Math.pow(other.attractiveness/aMax,(dMax - d)/dMax*choosiness);
		if(pBest > 1){
			pBest = 1;
		}
		if(pBest < 0){
			pBest = 0;
		}
		return pBest;
	}

	public double chooseTheMostSimilar(Agent other){

		double pSimilar = Math.pow(((aMax - Math.abs(other.attractiveness- this.attractiveness))/aMax ),
                                 (dMax - d)/dMax*choosiness);
		if(pSimilar > 1){
			pSimilar = 1;
		}
		if(pSimilar < 0){
			pSimilar = 0;
		}
		return pSimilar;
	}

 


Observer

How do we match agents?  Let’s create a new class called the “Observer”, which is an agent the does the matches and records the data. One way to build an observer is this:


 

package agents;

import sim.engine.*;
import sim.util.Bag;

public class Observer implements Steppable{

	int matches = 0; //the number of matches.
	int maxMatches;
	double[] malesA;
	double[] femalesA;
	Bag males;
	Bag females;
	Stoppable event;

	public void step(SimState state) {
		SimulationEnvironment se = (SimulationEnvironment)state;
		int n;
		if(males.numObjs > females.numObjs){
			n = males.numObjs;
		}
		else {
			n = females.numObjs;
		}
		if(n > 0){
			for(int i=0;i<n;i++){
				int randomMale = state.random.nextInt(males.numObjs);
				Agent m =(Agent) males.objs[randomMale];
				int randomFemale = state.random.nextInt(males.numObjs);
				Agent f =(Agent) females.objs[randomFemale];
				double pm;
				double pf;

				if(se.chooseTheBest){
					pm = m.chooseTheBest(f);
					pf = f.chooseTheBest(m);
				}
				else {
					pm = m.chooseTheMostSimilar(f);
					pf = f.chooseTheMostSimilar(m);
				}

				if(state.random.nextBoolean(pm) && state.random.nextBoolean(pf)){
					malesA[matches] = m.attractiveness;
					femalesA[matches]=f.attractiveness;
					matches++;
					m.removeSelf(state);
					f.removeSelf(state);
					males.remove(m);
					females.remove(f);
				}
				else {
					m.updated();
					f.updated();
				}
			}
		}
		if(malesA.length>2){
			System.out.println("matches = "+ matches+" r = "+
                         PearsonCorrelation.correlation(malesA, femalesA));
		}

		if(males.numObjs == 0 || females.numObjs == 0){
			event.stop();
		}

	}

	public Observer(SimState state){
		SimulationEnvironment se = (SimulationEnvironment)state;
		if(se.males>se.females){
			maxMatches = se.males;
		}
		else {
			maxMatches = se.females;
		}
		malesA = new double[maxMatches];
		femalesA = new double[maxMatches];
		males = se.malePopulation;
		females = se.femalePopulation;
	}

}

 


We also have to schedule it in the simulation environment.

Correlation

Kalick & Hamilton’s (1986) compared their simulation results to correlations reported in the literature. So we need to calculate correlations for the simulation results.

 

 

 

 

To create a class that can calculate Pearson correlations, we could implement the following class:


 

package agents;

public class PearsonCorrelation {

	/**
	 * This will take two arrays of doubles and compute the Pearson correlation coefficient
	 */

	public static double correlation(double[] arrayX, double[] arrayY){
		double meanX = mean(arrayX);
		double meanY = mean (arrayY);
		double ssqX = sumDiffSQ(arrayX, meanX);
		double ssqY = sumDiffSQ(arrayY, meanY);
		double sXY = sumDiffXY(arrayX, meanX, arrayY, meanY);
		double r = sXY/(Math.sqrt(ssqX)*Math.sqrt(ssqY));

		return r;
	}

	public static double mean(double[] array){
		double total = 0;
		for(int i=0; i<array.length;i++){
			total += array[i];
		}
		return total/(double)array.length;
	}

	public static double sumDiffSQ(double[] array, double mean){
		double total = 0;
		for(int i=0; i<array.length;i++){
			double x = array[i]-mean;
			total +=x*x;
		}
		return total;
	}

	public static double sumDiffXY(double[] arrayX, double meanX, double[] arrayY, double meanY){
		double total = 0;
		for(int i=0; i<arrayX.length;i++){
			double x = arrayX[i]-meanX;
			double y = arrayY[i]-meanY;
			total +=x*y;
		}
		return total;
	}

}