Skip to main content
Dryad logo

Honest signalling of cooperative intentions

Citation

Roberts, Gilbert (2020), Honest signalling of cooperative intentions, Dryad, Dataset, https://doi.org/10.5061/dryad.dfn2z34xd

Abstract

Trust can transform conflicting interests into cooperation. But how can individuals know when to trust others? Here, I develop the theory that reputation building may signal cooperative intent, or ‘trustworthiness’. I model a simple representation of this theory in which individuals (1) optionally invest in a reputation by performing costly helpful behaviour (‘signalling’); (2) optionally use others’ reputations when choosing a partner; and (3) optionally cooperate with that partner. In evolutionary simulations, high levels of reputation building; of choosing partners based on reputation; and of cooperation within partnerships emerged. Costly helping behaviour evolved into an honest signal of trustworthiness when it was adaptive for cooperators, relative to defectors, to invest in the long-term benefits of a reputation for helping. I show using game theory that this occurs when cooperators gain larger marginal benefits from investing in signalling than do defectors. This happens without the usual costly signalling assumption that individuals are of two ‘types’ which differ in quality. Signalling of trustworthiness may help explain phenomena such as philanthropy, pro-sociality, collective action, punishment, and advertising in humans and may be particularly applicable to courtship in other animals.

Methods

The data were produced using evolutionary simulation programs. Source code, data files and R code are described here.

Figure 3a,b

Run source code file RBPC.c (in Visual Studio 2017 or modify appropriately for other complier)

Check parameters are population size N = 100; generations = 1000; islands i = 10; cost c=1; benefit b = 10; meetings m = 50; rounds r = 25; signal cost s = 10; cost of choice h = 0.01; baseline payoff = 1000; mutation rate µ= 0.02 (parameters are explained in Table S1).

This outputs a simulation data file named dynamics.dat. For clarity I upload this as Fig3ab.dat

To produce Figures 3a and b, read this data file into Rstudio and name the data frame “dynamics”, as follows:

>library(ggplot2)

>attach(dynamics)

>mdynamics <- aggregate(dynamics, by=list(V2, V3, V4), FUN=mean)

> mdynamicsrole1 <- subset(mdynamics, V3==1, select=c("V2", "V4", "V5", "V6"))

> ggplot(mdynamicsrole1g100, aes(x=V2, y=V5, colour=as.factor(V4)))+geom_line() + theme(plot.background = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.border = element_blank(), panel.background = element_blank(), axis.line = element_line(colour = 'black'), axis.title.x=element_text(size=19, colour='black'), axis.title.y = element_text(size=19, colour='black'), axis.text.x = element_text(size=12, colour='black'), axis.text.y = element_text(size=12, colour='black'), legend.text = element_text(size=12), legend.title=element_blank(), legend.background = element_rect(colour='black', size=.5, linetype="solid"), legend.position=c(.75,.5)) +labs(x='Generation', y='Strategy (%)') + scale_colour_discrete(breaks=c("0", "1", "2", "3"), labels=c("No signal, D", "No signal, C", "Signal, D", "Signal, C" ))

>mdynamicsrole0 <- subset(mdynamics, V3==0, select=c("V2", "V4", "V5", "V6"))

>mdynamicsrole0g100 <- subset(mdynamicsrole0, V2 < 100, select=c("V2", "V4", "V5", "V6"))

> ggplot(mdynamicsrole0g100, aes(x=V2, y=V5, colour=as.factor(V4)))+geom_line() + theme(plot.background = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.border = element_blank(), panel.background = element_blank(), axis.line = element_line(colour = 'black'), axis.title.x=element_text(size=19, colour='black'), axis.title.y = element_text(size=19, colour='black'), axis.text.x = element_text(size=12, colour='black'), axis.text.y = element_text(size=12, colour='black'), legend.text = element_text(size=12), legend.title=element_blank(), legend.background = element_rect(colour='black', size=.5, linetype="solid"), legend.position=c(.75,.5)) +labs(x='Generation', y='Strategy (%)') + scale_colour_discrete(breaks=c("0", "1", "2", "3"), labels=c("Any, D", "Any, C", "Choose, D", "Choose, C" ))

 

Figure 3c

To produce this figure, run the RBPC.c source code file modified to write separate lines for the total proportions of Signalers, Choosers and Cooperators in each generation. This file is uploaded as Read this file into RStudio as dynamicsschc.dat. Load this into Rstudio and use the following to obtain the Figure:

>mschc <- aggregate(dynamicsschc, by=list(V2, V4), FUN=mean)

>mschc100 <- subset(mschc, V2 < 100, select=c("V2", "V3", "V4"))

>ggplot(mschc100, aes(x=V2, y=V3, colour=as.factor(V4)))+geom_line() + theme(plot.background = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.border = element_blank(), panel.background = element_blank(), axis.line = element_line(colour = 'black'), axis.title.x=element_text(size=19, colour='black'), axis.title.y = element_text(size=19, colour='black'), axis.text.x = element_text(size=12, colour='black'), axis.text.y = element_text(size=12, colour='black'), legend.text = element_text(size=12), legend.title=element_blank(), legend.background = element_rect(colour='black', size=.5, linetype="solid"), legend.position=c(.75,.5)) + labs(x='Generation', y='Strategy (%)') + scale_colour_discrete(breaks=c("1", "2", "3"), labels=c("Signal", "Choose", "Cooperate"))

 

Figure 4

Run source code RBPC.c modified to output the probability of signalling given that an agent is a co-operator [p(S|C] versus a defector [p(S|D)]. Import the data file pscpsd.dat into RStudio and aggregate it across simulations by generation and probabilities then plot as follows:

>mpscpsd <- aggregate(ppscpsd, by=list(v2, V4), FUN=mean)

>ggplot(mpscpsd, aes(x=V2, y=V3, colour=as.factor(V4)))+geom_line() + theme(plot.background = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.border = element_blank(), panel.background = element_blank(), axis.line = element_line(colour = 'black'), axis.title.x=element_text(size=19, colour='black'), axis.title.y = element_text(size=19, colour='black'), axis.text.x = element_text(size=12, colour='black'), axis.text.y = element_text(size=12, colour='black'), legend.text = element_text(size=12), legend.title=element_blank(), legend.background = element_rect(colour='black', size=.5, linetype="solid"), legend.position=c(.75,.75)) + labs(x='Generation', y='Probability') + scale_colour_discrete(breaks=c("1", "2"), labels=c("p(S|C)", "p(S|D)"))

Similarly, for p(C|S) and p(C|no-S), read in file pcspcns, aggregate and plot:

>mpcspcns <- aggregate(pcspcns, by=list(v2, V4), FUN=mean)

>ggplot(mpcspcns, aes(x=V2, y=V3, colour=as.factor(V4)))+geom_line(size=1.2) + theme(plot.background = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.border = element_blank(), panel.background = element_blank(), axis.line = element_line(colour = 'black'), axis.title.x=element_text(size=20, colour='black'), axis.title.y = element_text(size=20, colour='black'), axis.text.x = element_text(size=14, colour='black'), axis.text.y = element_text(size=14, colour='black'), legend.text = element_text(size=14), legend.title=element_blank(), legend.background = element_rect(colour='black', size=.5, linetype="solid"), legend.position=c(.75,.25)) + labs(x='Generation', y='Probability') + scale_colour_discrete(breaks=c("1", "2"), labels=c("p(C|S)", "p(C|no S)"))

 

Figure 6

Run RBPC.c source code with output as in Figure 3c of proportions displaying ‘Choose’ strategies; the proportion signalling; and the proportion cooperating. In each case these are calculated as means with standard errors of the given variable value in generation 100, computed across 10 simulations

For Figure6a-c, vary signal cost whilst keeping other parameters constant (see Figure 3 Legend for parameters) on the. They are therefore intended to indicate the average value at equilibrium (see Figure 3 for how the evolutionary dynamics reach equilibrium value within 100 generations). Similarly panels d-f show average percentages of Signallers, Choosers and Cooperators keeping other parameters constant (as in Figure 3) while varying the number of plays as the primary means of increasing the benefits of a relationship with a partner. These data are summarized in the files parastats.xls and playstats.xls. Use the following to plot:

>ggplot(parastats, aes(x=signalcost, y=coopm)) + geom_point(stat="identity") + geom_errorbar(aes(ymin=coopselo, ymax=coopsehi), width = 2, size = 1) + labs(x='Signal Cost', y='Cooperators (%)') + ylim(0,100)+ theme(plot.background=element_blank(), panel.background=element_blank(), panel.grid.major=element_blank(), panel.grid.minor=element_blank(), axis.line=element_line(colour='black'), axis.title.x=element_text(size=22, colour='black'), axis.title.y = element_text(size=22, colour='black'), axis.text.x = element_text(size=14, colour='black'), axis.text.y = element_text(size=14, colour='black'))

> ggplot(parastats, aes(x=signalcost, y=signal)) + geom_point(stat="identity") + geom_errorbar(aes(ymin=signallo, ymax=signalhi), width = 2, size = 1) + labs(x='Signal Cost', y='Signallers (%)') + ylim(0,100)+ theme(plot.background=element_blank(), panel.background=element_blank(), panel.grid.major=element_blank(), panel.grid.minor=element_blank(), axis.line=element_line(colour='black'), axis.title.x=element_text(size=22, colour='black'), axis.title.y = element_text(size=22, colour='black'), axis.text.x = element_text(size=14, colour='black'), axis.text.y = element_text(size=14, colour='black'))

> ggplot(parastats, aes(x=signalcost, y=choosem)) + geom_point(stat="identity") + geom_errorbar(aes(ymin=chooseselo, ymax=choosesehi), width = 2, size = 1) + labs(x='Signal Cost', y='Choosers (%)') + ylim(0,100)+ theme(plot.background=element_blank(), panel.background=element_blank(), panel.grid.major=element_blank(), panel.grid.minor=element_blank(), axis.line=element_line(colour='black'), axis.title.x=element_text(size=22, colour='black'), axis.title.y = element_text(size=22, colour='black'), axis.text.x = element_text(size=14, colour='black'), axis.text.y = element_text(size=14, colour='black'))