Party competition Ch10
Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)
WHAT IS IT?
This implements the model of party competition with endogenous party birth and death, and with party leaders who balance a desire to win votes with a desire to set party policy close to their own ideal policy position, specified in Chapter 10 of Michael Laver and Ernest Sergenti's book, "Party competition: an agent based model" (Princeton University Press, 2012). A full description and analysis of the model, which is a modified version of the model specified in Chapter 7, can be found in Chapter 10 of this book and is not repeated here.
Party positions and voter ideal points are defined in two dimensions of policy/ideology, directly analogous to the dimensions used in other "spatial" models of political competition. The horizontal dimension might, for example, be seen as describing left-right economic policy positions; the vertical dimension might be a liberal-conservative policy dimension on matters such as abortion, sexuality, euthanasia.
VOTERS always vote for the closest party. The preference structure of the voting population can be designed as if this is an aggregate of up to three subpopulations. (Only two are investigated by Laver and Sergenti and the default setting on the interface sets the size of the third subpopulation at zero). Voters in each subpopulation have normally distributed ideal points, and each subpopulation is characterized by: the number of voters it comprises; the standard deviation of the distribution of its voters' ideal points, and the means of this distribution on the x and y dimensions. All of these parameters can be set using the sliders in the _Population Designer_ panel near the bottom of the interface. Alternatively, the _random population_ button picks these at random.
PARTY LEADERS compete with each other by offering policies to potential supporters. They use one of five parameterized _species_ of decision rule to select a party policy position. The species are: Sticker, Aggregator, Hunter, Predator and Exporer. The rule parameters of speed of adaptation (speed) and exploration neighborhood (eta) are set to central values, while the comfort threshold (kappa) is set to its highest value so that leaders are always seeking more votes. What varies between leaders are the rule species they use and the trade-off (phi) between the leader's party vote shares and her ideal policy position. This trade-off requires a modification of each of the decision rules. These modifications are described fully in Chapter 10 of Laver and Sergenti (2012) and implemented in the _party dynamics_ section of the code.
DYNAMICS OF PARTY COMPETITION. The baseline dynamics of the model iterate forever. (1) Voters support their closest party. (2) Given profile of voter support for parties, leaders adapt party policy positions using their decision rule. (3) Go to 1.
The set of surviving political parties is fully endogenous to the model.
EXISTING PARTIES DIE if their updated fitness, denominated in vote share, falls below a system survival threshold. The party survival threshold, and the memory parameter in the fitness updating regime, can be set using the sliders in the _Environment_ panel near the top of the interface.
NEW PARTIES ARE BORN at the ideal points of disgruntled voters. Parameters of the party birth regime can be set using the sliders in the _Party birth_ panel near the top of the interface. There is a switch on the interface that turns off endogenous party birth and instead randomly generates new party births at random locations. (This was not investigated systematically by Laver and Sergenti.)
Model ticks are divided into CAMPAIGN TICKS and ELECTION TICKS. Party leaders adapt their positions during campaign ticks but receive no rewards or punishments. Parties can only die or be born on election ticks. The number of campaign ticks per election tick can be set using the slider in the _Environment_ panel near the top of the interface.
HOW TO USE IT
The set of available decision rules, and the set of available parameterizations of these rules, are hard coded in the "set-rule-list" procedure in the setup routine. (This is obviously less than elegant and it would be better to load this list from a file). Comments below this procedure explain the coding of a given rule parameterization, which is equivalent to a party leader's "decision-making DNA". Some parameters are redundant for some rules. The research design of the Laver-Sergenti experiments requires that all rule SPECIES have an equal chance of random selection, and "clones" of Sticker and Aggregator rules, all with the same parameterization, are included to enable this. The current, easily editable, rule list is the one used for the experiment reported in Chapter 10 of Laver and Sergenti.
SETUP sets up parties, supporters and system parameters as specified above. GO starts and stops the simulation using current parameters.
(Hitting SETUP while GO is still pressed very occasionally causes an error depending where precisely the program is when setup is hit; this easily retrieved by unpressing GO and pressing SETUP again.)
RUNNING EXPERIMENTS. Laver and Sergenti designed a large computational experiment, and report results of this, in Chapter 10 of their book. Although the _production_ run was executed on a high performance cluster, precisely equivalent smaller scale experiments can easily be run using Behavior Space. Sketch runs for all results reported in Laver and Sergenti were generated using Behavior Space on a normal laptop.
DATA OUTPUT. Standard data output is via Behavior Space experiments used in the normal way. There is a separate data channel that writes out information on party births and deaths only when these occur. This is activated by a switch and a file name on the interface.
WHAT TO PLAY WITH
The most obvious new source of potential experimentation concerns values of party leader phi, the parameter for the trade-off between party votes share and party policy. These can easily be changed by editing the rule-list in the setup procedure. Any number of parametizations can enter this list, which can be as large and varied as you like. The larger the rule list, however, the longer it will take to "map out" the steady state distribution of model outputs. The less systematically it is constructed, the harder it will be to make sense of model outputs.
The rule parameters of speed of adaptation (speed) and exploration neighborhood (eta) might be varied, while another interesting possibility is to add a valence compomnent to the model as specified in Chapter 9 of Laver and Sergenti (2012). An "all singing all dancing" version of the model (which will be very difficult if not impossible to analyze) can easily be created by combining all model features described in Chapters 7-10 of Laver and Sergenti (2012).
Laver and Sergenti report results from a carefully controlled computational experiment and only investigate electorate with two subpopulations. There are infinitely many alternative populations for you to explore using the population designer. There are also many parameterizations of the competitive environment, and the party birth regime, not explored by Laver and Sergenti. You may, for example, want to specify a parameterization of the model you feel corresponds to some real political system that interests you.
CREDITS AND REFERENCES
Programmed by:
Michael Laver, Department of Politics, New York University
ml127@nyu.edu
Ernest Sergenti, The World Bank
esergenti@gmail.com
Comments and Questions
;;____________________________ ; ;; SETUP AND HOUSEKEEPING ;;____________________________ breed [ parties party ] globals [ total-votes max-voteshare ; largest vote share on any patch mean-voterx ; mean voter x-coordinate mean-votery ; mean voter y-coordinate cycle election ; election number n-parties ; total number of parties that competed in the previous election voter-misery ; mean quadratic Euclidean voter distance from closest party mean-eccentricity ; mean Euclidean distance of parties from (mean-voterx, mean-votery) enp ; effective number of parties = 1/(sum of squared party vote shares) mean-party-policy-loss ; mean ideal policy point loss for all of the parties mean-party-policy-shift ; mean ideal policy shift for all of the parties mean-party-phi ; mean phi for all of the parties rule-number ; rule number rule-list ; list of available decision rules rule-voteshare ; list of voteshares won by the set of parties using each rule rule-pcount ; number of parties using rule rule-eccent ; list of the mean eccentricities of all parties using each rule rule-loss ; mean policy loss of party leaders using each rule rule-delta ; mean policy movement of party leaders using each rule ] parties-own [ rule ; party's parameterized decision rule species ; party's decision rule species ; decision rule parameters speed ; distance each party moves per tick comfort-kappa ; comfort threshold - implement rule iff fitness below this neighborhood-eta ; radius from party postion of local neighborhood phi ; mixing parameter that specifies the relative importance of the ideal-point loss ; indicators mysize ; current party size old-x ; x-coordinate of my position at previous election old-y ; y-coordinate of my position at previous election policy-shift ; policy distance moved since birth age ; number of elections survived since birth fitness ; party's evolutionary fitness eccentricity ; party's Euclidean distance from (mean-voterx, mean-votery) ideal-x ; party's ideal position in the x dimension ideal-y ; party's ideal position in the y dimension utiles ; party's utility, balancing vote share and policy preferences old-utiles ; utiles as of the most recent update ideal-point-loss ; quadratic distance between leader's ideal position and party position ; rule-specific variables prey ; for predators: party I am thinking of attacking best-x ; for explorers: explored x-coordinate with highest utility during campaign best-y ; for explorers: explored y-coordinate with highest utility during campaign best-utiles ; for explorers: highest-utility found during campaign ] patches-own [ votes ; number of voters on patch vote-share ; proportion of total voters on patch closest-party ; party with smallest Euclidean distance from patch misery ; quadratic distance from closest party, weighted by patch's vote share ] to setup clear-all file-close set rule-list (list "S10a" "S10b" "S10c" "S10d" "S10e" "S10f" "A00" "A02" "A04" "A06" "A08" "A10" "H00" "H02" "H04" "H06" "H08" "H10" "P00" "P02" "P04" "P06" "P08" "P10" "E00" "E02" "E04" "E06" "E08" "E10" ) ;; the 3-character rule names store the parameterization (genetic code) of each rule, as follows: ;; character 1 = rule species; chars 2-3 = phi set rule-number n-values length(rule-list) [?] set rule-voteshare n-values length(rule-list) [0] set rule-pcount n-values length(rule-list) [0] set rule-eccent n-values length(rule-list) [-1] set rule-loss n-values length(rule-list) [-1] set rule-delta n-values length(rule-list) [-1] create-parties 1 ask parties [set fitness 1 set heading random-float 360 jump random-float 30 set old-x xcor set old-y ycor set age 0 set size 2 random-pick load-rule-parameters color-myself set ideal-x xcor set ideal-y ycor set old-utiles 0 ] ;; every run starts with a single party, which has a random position and rule picked from the rule list ask patches [ let x1 (pxcor - x-mean1) / sd-1 let y1 (pycor - y-mean1) / sd-1 set votes votes1 * exp (-0.5 * ( x1 ^ 2 + y1 ^ 2)) / (2 * pi * sd-1 ^ 2) ;; votes1, x_mean1, y_mean1, sd_1 = votes, mean and standard deviation of subpopulation 1, read from interface ;; each patch's votes arising from subpopulation 1 = votes1 * bivariate normal density with mean1, sd_1, rho = 0 if (votes2 > 0)[ let x2 (pxcor - x-mean2) / sd-2 let y2 (pycor - y-mean2) / sd-2 set votes votes + votes2 * exp (-0.5 * ( x2 ^ 2 + y2 ^ 2)) / (2 * pi * sd-2 ^ 2)] ;; add votes to each patch from subpopulation 2, calculated as above if (votes3 > 0)[ let x3 (pxcor - x-mean3) / sd-3 let y3 (pycor - y-mean3) / sd-3 set votes votes + votes3 * exp (-0.5 * ( x3 ^ 2 + y3 ^ 2)) / (2 * pi * sd-3 ^ 2)] ;; add votes to each patch from subpopulation 3, calculated as above ] set total-votes sum [ votes ] of patches type "Total votes at all locations = " type round(total-votes) ;; add total of votes on all patches and output this to the command window ask patches [set vote-share votes / total-votes] ;calculate each patch's vote share set mean-voterx sum [ pxcor * vote-share ] of patches set mean-votery sum [ pycor * vote-share ] of patches type " Mean voter x = " type round(mean-voterx) type " Mean voter y = " print round(mean-votery) ;; calculate center (mean) of voter distribution on each dimension as sum of (patch positions weighted by vote share) ;; output this to the command window set max-voteshare max [ vote-share ] of patches ask patches [set pcolor scale-color gray vote-share 0 max-voteshare ] ;; color patches red with density proportional to vote shares update-support ;; ask voters to choose closest party and calculate relative success of different rules update-utility ;; ask party leaders to calculate their utility based on their position and vote share end to random-pop set sd-1 5 set sd-2 5 set y-mean1 0 set y-mean2 0 set x-mean2 precision (5 + random-float 10) 2 set x-mean1 0 - x-mean2 set votes1 1000000 set votes2 votes1 - random 500000 end ;;____________________________ ; ;; PARTY DYNAMICS ;;____________________________ to stick ;; do nothing end to aggregate if (mysize > 0) ; maintain current position if zero supporters, else ... [ let xbar (sum [votes * pxcor] of patches with [closest-party = myself] / mysize) let ybar (sum [votes * pycor] of patches with [closest-party = myself] / mysize) let xdest (phi * ideal-x) + (1 - phi) * xbar ; find a policy destination that balances your ideal point let ydest (phi * ideal-y) + (1 - phi) * ybar ; with the mean position of current party supporters facexy xdest ydest let dist distancexy xdest ydest ifelse (dist >= speed) [jump speed] [setxy xdest ydest]] ; if you will not overshoot (xbdest, ydest), jump "speed" towards this end to hunt ifelse (utiles > old-utiles) [jump speed] [set heading heading + 90 + random-float 180 jump speed] ; move "speed" in same heading if last move increased UTILITY, else browse set old-utiles utiles ; remember utility and size for next cycle end to predate set prey max-one-of parties [mysize] let xdest (phi * ideal-x) + (1 - phi) * [xcor] of prey let ydest (phi * ideal-y) + (1 - phi) * [ycor] of prey facexy xdest ydest ; find a policy destination that balances your ideal point let dist distancexy xdest ydest ; with the position of the largest party ifelse (dist >= speed) [jump speed] [setxy xdest ydest] ; if you will not overshoot (xdest, ydest), jump "speed" towards this end to explore if (utiles > best-utiles) [set best-x xcor set best-y ycor set best-utiles utiles] ;; if you found a position with more UTILES than your previous best-utiles, update your best position ifelse (remainder cycle campaign-ticks != 0) [setxy old-x old-y set heading random-float 360 jump random-float neighborhood-eta] [if (best-utiles > old-utiles) [setxy best-x best-y] set best-utiles 0 ] end ;;____________________________ ;; ;; MAIN CONTROL SUBROUTINES ;;____________________________ to update-support ask patches [set closest-party min-one-of parties [distance myself]] ;; patches find their closest party ask parties [set mysize sum [votes] of patches with [closest-party = myself]] ;; each party sums the votes on patches for which it is the closest party end to update-utility ask parties [ let scale 0.05 set ideal-point-loss 0 - (distancexy ideal-x ideal-y ^ 2) / 100 set utiles 1 + (phi * scale * ideal-point-loss) + ((1 - phi) * mysize / total-votes) ;;phi (<= 1) is like the alpha parameter, but balances votes and policy ;;phi = 1 implies ideal point loss is all important; ;;the scale parameter is an "exchange rate", needed because votes and policy are measured in different units. ] end to calculate-election-results set election election + 1 update-party-measures update-rule-measures measure-enp measure-eccentricity measure-misery party-death party-birth end to update-party-measures ask parties [ set fitness fitness-alpha * fitness + (1 - fitness-alpha) * mysize / total-votes ;; parties recursively update their fitness as: (alpha)*(previous fitness) + (1-alpha)*(current vote share) set policy-shift sqrt((xcor - old-x) ^ 2 + (ycor - old-y) ^ 2) set age age + 1 set old-x xcor set old-y ycor ] set n-parties count parties set mean-party-policy-loss sum [ideal-point-loss] of parties / n-parties set mean-party-policy-shift mean [policy-shift] of parties set mean-party-phi mean [phi] of parties end to update-rule-measures (foreach rule-number rule-list [ set rule-voteshare replace-item ?1 rule-voteshare sum [mysize / total-votes] of parties with [rule = ?2] ;; calculate the current support level of all parties using each rule set rule-pcount replace-item ?1 rule-pcount count parties with [rule = ?2] ;; count the number of parties using each rule ifelse (sum [mysize] of parties with [rule = ?2] > 0) [ set rule-eccent replace-item ?1 rule-eccent mean [eccentricity] of parties with [rule = ?2] set rule-loss replace-item ?1 rule-loss mean [ideal-point-loss] of parties with [rule = ?2] set rule-delta replace-item ?1 rule-delta mean [policy-shift] of parties with [rule = ?2] ] ;;calculate the mean of eccentricity, policy loss and policy shift of all parties using each rule [ set rule-eccent replace-item ?1 rule-eccent -1 set rule-loss replace-item ?1 rule-loss -1 set rule-delta replace-item ?1 rule-delta -1 ] ;;these measures have no meaning when no party uses a rule ]) end to measure-enp set enp (total-votes ^ 2) / (sum [mysize ^ 2] of parties) ;; calculate the enp of the system end to measure-eccentricity ask parties [set eccentricity sqrt ((xcor - mean-voterx) ^ 2 + (ycor - mean-votery) ^ 2) / 10] ;; calculate each party's eccentricity, its Euclidean distance from the center of the voter distribution set mean-eccentricity mean [eccentricity] of parties ;; calculate the mean eccentricity of all parties in the system end to measure-misery ask patches [set misery misery-alpha * misery + (1 - misery-alpha) * ((distance closest-party ^ 2) / 100) * vote-share] set voter-misery sum [misery] of patches ;; patch misery is misery at t-1, updated by mean quadratic Euclidean distance of patch from closest party, ;; weighted by patch vote share ;; mean voter "misery" is thus updated mean quadratic Euclidean distance of each voter from his/her closest party end to party-death ask parties [if (fitness < survival-threshold and count parties > 2) [ if (birth-death-file = true) [ file-write votes1 file-write x-mean1 file-write votes2 file-write x-mean2 file-write fitness-alpha file-write survival-threshold file-write campaign-ticks file-write misery-alpha file-write misery-beta file-write election file-write "death" file-write rule file-write who file-write precision xcor 4 file-write precision ycor 4 file-write "age" file-write age file-print "" ] die ask patches [set closest-party min-one-of parties [distance myself]] ]] ;; parties whose updated fitness falls below the survival threshold write out their data and die ;; as long as there are at least two parties end to party-birth ifelse (endogenous-birth = true) [ ask one-of patches with [distancexy 0 0 < 30] [ if (random-float 1 < (misery-beta * misery * 1000)) [sprout-parties 1 [initialize-party] ]]] ;; pick a random patch within three standard deviations of the origin. ;; the probability this patch sprouts a new party is proportional to (misery-beta)*(patch misery) ;; the greater patch misery, the higher the probability the patch sprouts a new party. ;; NB patch misery is scaled, in measure-misery above, to the share of all voters on the patch, ;; this share maxes at 0.00159 for the (0,0) patch in a unimodal (0,10) distribution and is 0.0002 at patch (20,0) in this distibution ;; this explains the scaling up of the patch misery score by 1000 and the units of beta are thus sui generis to the simulation ;; the greater beta, however, the more sensitive are voters on a patch to a given level of misery. ;; new-born parties intially locate at the position of the "sprouting" patch. [ create-parties 1 [set heading random-float 360 jump random-float 30 initialize-party] ] ;; non-endogenous initial party locations take a random walk within 30 from the origin end to initialize-party ifelse (count parties = 0) [set fitness 1] [set fitness 1 / count parties] set heading random-float 360 set old-x xcor set old-y ycor set age 0 set size 1.5 set ideal-x xcor set ideal-y ycor set old-utiles 0 ;;NB leader ideal points are founding party positions random-pick load-rule-parameters color-myself if (birth-death-file = true) [ file-write votes1 file-write x-mean1 file-write votes2 file-write x-mean2 file-write fitness-alpha file-write survival-threshold file-write campaign-ticks file-write misery-alpha file-write misery-beta file-write election file-write "birth" file-write rule file-write who file-write precision xcor 4 file-write precision ycor 4 file-print "" ] ;;Write out your starting data before handing control back to the observer end to random-pick set rule one-of rule-list ;; randomly pick a rule from the rule list end to load-rule-parameters ; set parameters of your decision rule by reading the relevant parts of your rule name set species first rule set phi (read-from-string substring rule 1 3) / 10 set speed 1 set neighborhood-eta 6 end to color-myself if (species = "S") [set color yellow] if (species = "A") [set color lime] if (species = "H") [set color violet] if (species = "P") [set color red] if (species = "E") [set color blue] end to adapt if (species = "S") [stick] if (species = "A") [aggregate] if (species = "H") [hunt] if (species = "P") [predate] if (species = "E") [explore] end ;;____________________________ ; ;; MAIN CONTROL ROUTINE ;;____________________________ to go repeat campaign-ticks [ set cycle cycle + 1 ask parties [adapt] update-support update-utility if (remainder cycle campaign-ticks = 0 and cycle != 0) [calculate-election-results] ] end
There is only one version of this model, created over 12 years ago by Michael Laver.
Attached files
No files
This model does not have any ancestors.
This model does not have any descendants.