├── LICENSE.txt ├── README.md ├── data ├── commondata_model1.mat ├── commondata_model2.mat └── commondata_model3.mat ├── figs ├── model1.fig ├── model1.pdf ├── model1MCMCse.fig ├── model1MCMCse.pdf ├── model1_mcmcJAGS_infer_chains.fig ├── model1_mcmcJAGS_infer_chains.pdf ├── model1_mcmc_infer_chains.fig ├── model1mcmc.fig ├── model1mcmcConvergence.fig ├── model1mcmcConvergence.pdf ├── model1mcmcCustom.fig ├── model1mcmcCustom.pdf ├── model1mcmcJAGS.fig ├── model1mcmcJAGS.pdf ├── model1mcmc_rapid_convergence.pdf ├── model2.fig ├── model2.pdf ├── model2_infer_chains.fig ├── model2_infer_chains.pdf ├── model2autocorrelations.fig ├── model2autocorrelations.pdf ├── model2paramMatrix.fig ├── model2paramMatrix.pdf ├── model2psychometric.fig ├── model3.fig ├── model3.pdf ├── model3_infer_chains.fig ├── model3autocorrelations.fig ├── model3psychometric.fig ├── model3psychometric.pdf ├── param_estimation.fig ├── param_estimation.pdf ├── psychometric_funcs.fig ├── psychometric_funcs.pdf └── testAFCplot.pdf ├── funcs ├── .ipynb_checkpoints │ └── fig2AfcDiagram-checkpoint.ipynb ├── ColorBand │ ├── ColorBand.m │ └── license.txt ├── HDIofGrid.m ├── acf │ ├── acf.m │ └── license.txt ├── bensPlotFunctions │ ├── MCMCdiagnoticsPlot.m │ ├── addDistributionSummaryText.m │ ├── add_text_to_figure.m │ ├── bens2Dmarginalplot.m │ ├── hline.m │ ├── log_plot_with_background.m │ ├── myDensityMatrix.m │ ├── my_2d_hist.m │ ├── my_errorbarsUL.m │ ├── my_shaded_errorbar_zone_UL.m │ ├── plotChainAutocorrelationsModel2.m │ ├── plotPost.m │ └── plot_formatting_setup.m ├── bensUtils │ ├── argmax.m │ ├── min_sec.m │ └── vec.m ├── define_experiment_params.m ├── define_mcmcparams.m ├── export_fig │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── append_pdfs.m │ ├── copyfig.m │ ├── eps2pdf.m │ ├── export_fig.m │ ├── fix_lines.m │ ├── ghostscript.m │ ├── im2gif.m │ ├── isolate_axes.m │ ├── pdf2eps.m │ ├── pdftops.m │ ├── print2array.m │ ├── print2eps.m │ ├── user_string.m │ └── using_hg2.m ├── generate_common_data.m ├── latex_fig │ ├── latex_fig.m │ └── license.txt ├── m1InferGridApprox.m ├── m1generateJAGS.m ├── m1inferJAGS.m ├── m1jointPosterior.m ├── m1posteriorPrediction.m ├── makeExplanatoryFigures │ ├── fig2AfcDiagram.ipynb │ ├── generateGridVsMCMCschematic.m │ └── generate_psychometric_functions.m ├── matjags.m ├── mhAlgorithm.m ├── model1MCMCconvergence.m ├── model1MCMCse.m ├── model1plot.m ├── model2generate.m ├── model2infer.m ├── model2modelfit.m ├── model2modelpredictions2.m ├── model2plot.m ├── model3generate.m ├── model3infer.m ├── model3nonMCMC.m ├── model3plot.m ├── model3psychometric.m ├── pcfunc.m ├── sampleStats.m └── sanityChecks │ └── sanityCheck2AFC.m ├── jagsmodels ├── model1JAGS.txt ├── model2JAGS.txt └── model3JAGS.txt ├── model1runme.m ├── model2runme.m ├── model3runme.m ├── output ├── m1MAPestimate.mat └── m3MAPestimate.mat ├── runAllModels.m └── setup.m /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Benjamin T. Vincent 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A tutorial on Bayesian models of perception 2 | 3 | This repository contains Matlab code associated with the paper: 4 | 5 | > Vincent (2015) [A tutorial on Bayesian models of Perception](http://www.sciencedirect.com/science/article/pii/S0022249615000061), Journal of Mathematical Psychology. do:10.1016/j.jmp.2015.02.001 6 | 7 | If you find the paper and/or code useful, the best way to thank me is by citing me :) 8 | 9 | **This code is published under the MIT licence, see LICENSE.txt.** 10 | 11 | 12 | ## If... then... 13 | * **You can't get the code to work**, then feel free to email and I'll try my best to respond to clarify things. 14 | 15 | * **You want to use the code for learning alongside the paper**, then feel free to clone or fork this repository, or download a .zip of the code. 16 | 17 | * **If you find a bug**, then: 18 | * create an Issue, or... 19 | * create a pull request through GitHub with fixed code :) 20 | 21 | * If you find the paper useful in your academic work, or adapt any of the code for your own work, please to share the love and cite the paper. 22 | 23 | ## Subdirectories 24 | 25 | * `~` the root directory contains the main Matlab scripts and functions for producing the figures, see the section below. 26 | * `figs/` output of the code saved in .fig and .pdf format. 27 | * `funcs/` contains the majority of the Matlab functions and JAGS model files. You don't need to run any of these files as they are called by the main scripts in the root directory. 28 | * `output/` data generated by the code is saved here. 29 | 30 | ## Reproducing the figures 31 | 32 | * `generate_common_data.m` generates and saves a set of common data which can be used with all of the models. There is no need to run this as example data is already included in the files `commondata_model*.mat`. 33 | * `model1runme.m` By changing the input variable `{'gridApprox', 'mcmcJAGS', 'mcmcCustom'}` this file will run the 3 inference steps using the different respective methods: 34 | * `gridApprox` will conduct parameter estimation via grid approximation. 35 | * `mcmcCustom` will conduct parameter estimation with an implementation of the Metropolis-Hastings sampling algorithm (see `mhAlgorithm.m`). 36 | * `mcmcJAGS` will use the JAGS inference software to conduct parameter estimation using MCMC methods. 37 | * `model1MCMCse.m` runs a large simulation to study the variability in MCMC-derived estimates of an observers internal variance. Warning: this function can take days to run even on a fast multi-core machine with lots of RAM. 38 | * `model1MCMCconvergence` 39 | * `model2runme.m` 40 | * `model2psychometric` 41 | * `model3runme.m` 42 | 43 | 44 | ## Options when running the code 45 | * If you have a computer with multiple CPU cores, then you can run the MCMC chains in parallel by setting `mcmcparams.doparallel = 1` in the file `/funcs/define_mcmcparams.m`. 46 | * You can elect to make inferences based upon the common dataset generated by the file `/generate_common_data.m` by setting `DATAMODE='load'`. Otherwise, new simulated observer behaviour can be generated by `DATAMODE='generate'`. 47 | 48 | ## Dependencies 49 | * This code was generated and tested with **Matlab 2014a**. 50 | * The **Matlab Statistics Toolbox** is required for Stage 3 of Model 1, when using grid approximation. 51 | * Models using JAGS to make inferences require: 52 | * **JAGS**, which can be downloaded from [http://mcmc-jags.sourceforge.net](http://mcmc-jags.sourceforge.net) 53 | * **matjags.m** is included in `/funcs` and works with the code here. Updated versions may be found in the repository [https://github.com/msteyvers/matjags](https://github.com/msteyvers/matjags) but of course future changes may introduce incompatibilities. 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /data/commondata_model1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/data/commondata_model1.mat -------------------------------------------------------------------------------- /data/commondata_model2.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/data/commondata_model2.mat -------------------------------------------------------------------------------- /data/commondata_model3.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/data/commondata_model3.mat -------------------------------------------------------------------------------- /figs/model1.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1.fig -------------------------------------------------------------------------------- /figs/model1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1.pdf -------------------------------------------------------------------------------- /figs/model1MCMCse.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1MCMCse.fig -------------------------------------------------------------------------------- /figs/model1MCMCse.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1MCMCse.pdf -------------------------------------------------------------------------------- /figs/model1_mcmcJAGS_infer_chains.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1_mcmcJAGS_infer_chains.fig -------------------------------------------------------------------------------- /figs/model1_mcmcJAGS_infer_chains.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1_mcmcJAGS_infer_chains.pdf -------------------------------------------------------------------------------- /figs/model1_mcmc_infer_chains.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1_mcmc_infer_chains.fig -------------------------------------------------------------------------------- /figs/model1mcmc.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1mcmc.fig -------------------------------------------------------------------------------- /figs/model1mcmcConvergence.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1mcmcConvergence.fig -------------------------------------------------------------------------------- /figs/model1mcmcConvergence.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1mcmcConvergence.pdf -------------------------------------------------------------------------------- /figs/model1mcmcCustom.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1mcmcCustom.fig -------------------------------------------------------------------------------- /figs/model1mcmcCustom.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1mcmcCustom.pdf -------------------------------------------------------------------------------- /figs/model1mcmcJAGS.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1mcmcJAGS.fig -------------------------------------------------------------------------------- /figs/model1mcmcJAGS.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1mcmcJAGS.pdf -------------------------------------------------------------------------------- /figs/model1mcmc_rapid_convergence.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model1mcmc_rapid_convergence.pdf -------------------------------------------------------------------------------- /figs/model2.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model2.fig -------------------------------------------------------------------------------- /figs/model2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model2.pdf -------------------------------------------------------------------------------- /figs/model2_infer_chains.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model2_infer_chains.fig -------------------------------------------------------------------------------- /figs/model2_infer_chains.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model2_infer_chains.pdf -------------------------------------------------------------------------------- /figs/model2autocorrelations.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model2autocorrelations.fig -------------------------------------------------------------------------------- /figs/model2autocorrelations.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model2autocorrelations.pdf -------------------------------------------------------------------------------- /figs/model2paramMatrix.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model2paramMatrix.fig -------------------------------------------------------------------------------- /figs/model2paramMatrix.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model2paramMatrix.pdf -------------------------------------------------------------------------------- /figs/model2psychometric.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model2psychometric.fig -------------------------------------------------------------------------------- /figs/model3.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model3.fig -------------------------------------------------------------------------------- /figs/model3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model3.pdf -------------------------------------------------------------------------------- /figs/model3_infer_chains.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model3_infer_chains.fig -------------------------------------------------------------------------------- /figs/model3autocorrelations.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model3autocorrelations.fig -------------------------------------------------------------------------------- /figs/model3psychometric.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model3psychometric.fig -------------------------------------------------------------------------------- /figs/model3psychometric.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/model3psychometric.pdf -------------------------------------------------------------------------------- /figs/param_estimation.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/param_estimation.fig -------------------------------------------------------------------------------- /figs/param_estimation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/param_estimation.pdf -------------------------------------------------------------------------------- /figs/psychometric_funcs.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/psychometric_funcs.fig -------------------------------------------------------------------------------- /figs/psychometric_funcs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/psychometric_funcs.pdf -------------------------------------------------------------------------------- /figs/testAFCplot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/figs/testAFCplot.pdf -------------------------------------------------------------------------------- /funcs/.ipynb_checkpoints/fig2AfcDiagram-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:a54628ffde146a3677b4921cd771f020bf5d25048536579eeb371d1e88767ee7" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "Produce 2AFC explanatory diagram" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "This requires you to not use inline mode. To do this, launch IPython from the terminal with the command \n", 24 | "\n", 25 | "`ipython notebook --pylab`" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "collapsed": false, 31 | "input": [ 32 | "import numpy as np\n", 33 | "import matplotlib.pyplot as plt\n", 34 | "\n", 35 | "# for normal distribution\n", 36 | "from scipy.stats import norm " 37 | ], 38 | "language": "python", 39 | "metadata": {}, 40 | "outputs": [], 41 | "prompt_number": 1 42 | }, 43 | { 44 | "cell_type": "heading", 45 | "level": 2, 46 | "metadata": {}, 47 | "source": [ 48 | "Formatting issues" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "The following allows latex rendering of text" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "collapsed": false, 61 | "input": [ 62 | "from matplotlib import rc\n", 63 | "rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})\n", 64 | "## for Palatino and other serif fonts use:\n", 65 | "#rc('font',**{'family':'serif','serif':['Palatino']})\n", 66 | "rc('text', usetex=True)" 67 | ], 68 | "language": "python", 69 | "metadata": {}, 70 | "outputs": [], 71 | "prompt_number": 2 72 | }, 73 | { 74 | "cell_type": "code", 75 | "collapsed": false, 76 | "input": [], 77 | "language": "python", 78 | "metadata": {}, 79 | "outputs": [], 80 | "prompt_number": 2 81 | }, 82 | { 83 | "cell_type": "code", 84 | "collapsed": false, 85 | "input": [], 86 | "language": "python", 87 | "metadata": {}, 88 | "outputs": [], 89 | "prompt_number": 2 90 | }, 91 | { 92 | "cell_type": "code", 93 | "collapsed": false, 94 | "input": [ 95 | "N=1000\n", 96 | "muS=1\n", 97 | "muN=0\n", 98 | "sigma=1\n", 99 | "SNx1 = np.random.normal(muS, sigma, 1000)\n", 100 | "SNx2 = np.random.normal(muN, sigma, 1000)\n", 101 | "\n", 102 | "NSx1 = np.random.normal(muN, sigma, 1000)\n", 103 | "NSx2 = np.random.normal(muS, sigma, 1000)" 104 | ], 105 | "language": "python", 106 | "metadata": {}, 107 | "outputs": [], 108 | "prompt_number": 3 109 | }, 110 | { 111 | "cell_type": "heading", 112 | "level": 2, 113 | "metadata": {}, 114 | "source": [ 115 | "Create a figure" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "collapsed": false, 121 | "input": [ 122 | "clf() \n", 123 | "f = plt.figure(num=1, figsize=(8, 6), dpi=600, facecolor='none', edgecolor='k')" 124 | ], 125 | "language": "python", 126 | "metadata": {}, 127 | "outputs": [], 128 | "prompt_number": 123 129 | }, 130 | { 131 | "cell_type": "heading", 132 | "level": 2, 133 | "metadata": {}, 134 | "source": [ 135 | "First panel" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "collapsed": false, 141 | "input": [ 142 | "ax1=plt.subplot(1,2,1, aspect='equal',axisbg='none')\n", 143 | "plt.rc('text', usetex=True)\n", 144 | "plt.scatter(SNx1, SNx2, s=15, c=[1,0.3,0.3], alpha=0.4, edgecolors='none')\n", 145 | "plt.scatter(NSx1, NSx2, s=15, c=[0.3,0.3,1], alpha=0.4, edgecolors='none')\n", 146 | "padding=sigma*3\n", 147 | "axis([muN-padding,muS+padding,muN-padding,muS+padding])\n", 148 | "\n", 149 | "\n", 150 | "\n", 151 | "\n", 152 | "ax1.tick_params(direction='out')\n", 153 | "\n", 154 | "# Move left and bottom spines outward by 10 points\n", 155 | "#ax1.spines['left'].set_position(('outward', 10))\n", 156 | "#ax1.spines['bottom'].set_position(('outward', 10))\n", 157 | "# Hide the right and top spines\n", 158 | "ax1.spines['right'].set_visible(False)\n", 159 | "ax1.spines['top'].set_visible(False)\n", 160 | "# Only show ticks on the left and bottom spines\n", 161 | "ax1.yaxis.set_ticks_position('left')\n", 162 | "ax1.xaxis.set_ticks_position('bottom')\n", 163 | "\n", 164 | "xlabel('$x_1$')\n", 165 | "ylabel('$x_2$')\n", 166 | "\n", 167 | "ax1.set_xticks([0,muS])\n", 168 | "ax1.set_yticks([0,muS])\n", 169 | "\n", 170 | "ax1.set_xticklabels(['$0$','$\\mu_S$'])\n", 171 | "ax1.set_yticklabels(['$0$','$\\mu_S$'])\n", 172 | "\n", 173 | "\n", 174 | "#plt.axis([-6, -6, 10, 10])\n", 175 | "\n", 176 | "# plot decision line\n", 177 | "plt.plot((-8,10),(-8,10),'k:')\n", 178 | "\n", 179 | "\n", 180 | "# Plot distance between centres\n", 181 | "plt.annotate ('', (muS,0), (0,muS), arrowprops={'arrowstyle':'<->'})\n", 182 | "#plt.text( muS,muS, '$\\sqrt{2\\mu_S^2}$', rotation=45)\n", 183 | "\n", 184 | "\n", 185 | "#plt.annotate(\n", 186 | "# 'D = 1', xy=(1, 9), xycoords = 'data',\n", 187 | "# xytext = (5, 0), textcoords = 'offset points')\n", 188 | "\n", 189 | "\n", 190 | "\n", 191 | "plt.text( 3*muS,-2*muS, '$SN$')\n", 192 | "\n", 193 | "plt.text( -2*muS,3*muS, '$NS$')\n", 194 | "\n", 195 | "\n", 196 | "\n" 197 | ], 198 | "language": "python", 199 | "metadata": {}, 200 | "outputs": [ 201 | { 202 | "metadata": {}, 203 | "output_type": "pyout", 204 | "prompt_number": 124, 205 | "text": [ 206 | "" 207 | ] 208 | } 209 | ], 210 | "prompt_number": 124 211 | }, 212 | { 213 | "cell_type": "heading", 214 | "level": 2, 215 | "metadata": {}, 216 | "source": [ 217 | "Create second panel" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "collapsed": false, 223 | "input": [ 224 | "\n", 225 | "ax2 = plt.subplot(1,2,2, aspect=20,axisbg='none')\n", 226 | "plt.rc('text', usetex=True)\n", 227 | "\n", 228 | "xmin=-muS-5\n", 229 | "xmax=muS+5\n", 230 | "# vector of x values\n", 231 | "x=np.linspace(xmin,xmax,501)\n", 232 | "# create a corresponding vector of zeros for use later\n", 233 | "theFloor = np.zeros(dsn.size)\n", 234 | "# Calculate the y-values of the distributions\n", 235 | "dsn = norm.pdf(x,muS,sqrt(2)*sigma)\n", 236 | "dns = norm.pdf(x,-muS,sqrt(2)*sigma)\n", 237 | "maxy=np.max(dsn)\n", 238 | "plt.plot( x, dns,'b-')\n", 239 | "plt.plot( x, dsn,'r-')\n", 240 | "\n", 241 | "# Fill error regions\n", 242 | "ax2.fill_between(x, 0, dsn, where=x<=0, facecolor='r', alpha=0.5, edgecolor='none')\n", 243 | "ax2.fill_between(x, 0, dns, where=x>=0, facecolor='b', alpha=0.5, edgecolor='none')\n", 244 | "\n", 245 | "\n", 246 | "# Move left and bottom spines outward by 10 points\n", 247 | "#ax.spines['left'].set_position(('outward', 10))\n", 248 | "#ax.spines['bottom'].set_position(('outward', 10))\n", 249 | "# Hide the right and top spines\n", 250 | "ax2.spines['right'].set_visible(False)\n", 251 | "ax2.spines['top'].set_visible(False)\n", 252 | "ax2.spines['left'].set_visible(False)\n", 253 | "# Only show ticks on the left and bottom spines\n", 254 | "#ax2.yaxis.set_ticks_position('left')\n", 255 | "ax2.xaxis.set_ticks_position('bottom')\n", 256 | "\n", 257 | "ax2.tick_params(direction='out')\n", 258 | "\n", 259 | "xlabel('$d = x_1-x_2$')\n", 260 | "\n", 261 | "ax2.set_xticks([-muS,0,muS])\n", 262 | "ax2.set_yticks([])\n", 263 | "\n", 264 | "ax2.set_xticklabels(['$-\\mu_S$','$0$','$\\mu_S$'])\n", 265 | "\n", 266 | "# draw decision boundary\n", 267 | "plt.axvline(x=0, linestyle=':', color='k')\n", 268 | "\n", 269 | "\n", 270 | "# distribution labels\n", 271 | "plt.text( muS*2.7,maxy*0.5, '$d_{SN}$', \n", 272 | " horizontalalignment='left',\n", 273 | " color ='r')\n", 274 | "\n", 275 | "plt.text( -muS*2.7,maxy*0.5, '$d_{NS} $', \n", 276 | " horizontalalignment='right',\n", 277 | " color ='b')\n", 278 | "\n", 279 | "# Decision test\n", 280 | "plt.text(0.5, maxy*1.05, 'say ${SN}$', \n", 281 | " horizontalalignment='left')\n", 282 | "\n", 283 | "plt.text(-0.5, maxy*1.05, 'say ${NS}$',\n", 284 | " horizontalalignment='right')\n", 285 | "\n" 286 | ], 287 | "language": "python", 288 | "metadata": {}, 289 | "outputs": [ 290 | { 291 | "metadata": {}, 292 | "output_type": "pyout", 293 | "prompt_number": 125, 294 | "text": [ 295 | "" 296 | ] 297 | } 298 | ], 299 | "prompt_number": 125 300 | }, 301 | { 302 | "cell_type": "code", 303 | "collapsed": false, 304 | "input": [], 305 | "language": "python", 306 | "metadata": {}, 307 | "outputs": [], 308 | "prompt_number": 125 309 | }, 310 | { 311 | "cell_type": "code", 312 | "collapsed": false, 313 | "input": [], 314 | "language": "python", 315 | "metadata": {}, 316 | "outputs": [], 317 | "prompt_number": 125 318 | }, 319 | { 320 | "cell_type": "heading", 321 | "level": 2, 322 | "metadata": {}, 323 | "source": [ 324 | "Save the figure as a pdf" 325 | ] 326 | }, 327 | { 328 | "cell_type": "code", 329 | "collapsed": false, 330 | "input": [ 331 | "# save it\n", 332 | "plt.savefig('testAFCplot.pdf',dpi=300, bbox_inches='tight', transparent=True)" 333 | ], 334 | "language": "python", 335 | "metadata": {}, 336 | "outputs": [], 337 | "prompt_number": 126 338 | }, 339 | { 340 | "cell_type": "code", 341 | "collapsed": false, 342 | "input": [], 343 | "language": "python", 344 | "metadata": {}, 345 | "outputs": [], 346 | "prompt_number": 122 347 | }, 348 | { 349 | "cell_type": "code", 350 | "collapsed": false, 351 | "input": [ 352 | "?fill_between" 353 | ], 354 | "language": "python", 355 | "metadata": {}, 356 | "outputs": [], 357 | "prompt_number": 129 358 | }, 359 | { 360 | "cell_type": "code", 361 | "collapsed": false, 362 | "input": [], 363 | "language": "python", 364 | "metadata": {}, 365 | "outputs": [] 366 | } 367 | ], 368 | "metadata": {} 369 | } 370 | ] 371 | } -------------------------------------------------------------------------------- /funcs/ColorBand/ColorBand.m: -------------------------------------------------------------------------------- 1 | function C = ColorBand( N ) 2 | %COLORBAND Summary of this function goes here 3 | % Detailed explanation goes here 4 | 5 | n = (1:N)'; 6 | R = exp(-(2*n/N).^2); 7 | G = exp(-(2*(n-N/2)/N).^2); 8 | B = exp(-(2*(n-N)/N).^2); 9 | C = [R,G,B]; 10 | 11 | 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /funcs/ColorBand/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Giorgos 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /funcs/HDIofGrid.m: -------------------------------------------------------------------------------- 1 | % 2 | % written by: Benjamin T Vincent 3 | % Based on p.627 of Kruschke, Doing Bayesian Data Analysis 4 | 5 | function [HDI] = HDIofGrid(x,y, credibiltyMass) 6 | 7 | % expecting sum of probability density to sum to 1. In other words, we are 8 | % expecting normalised probability distributions. 9 | % We could just see if sum(y)==1, but sometimes there are rounding errors 10 | % and it's not quite. So we will compute if the difference between sum(y) 11 | % and 1 is sufficiently low 12 | if abs(1-sum(y))>10^-10 13 | error('Expecting probability density (y) to sum to 1') 14 | end 15 | 16 | sortedMass = sort(y,'descend'); 17 | HDIheightIdx = find(cumsum(sortedMass) >= credibiltyMass,1,'first'); 18 | HDIheight = sortedMass(HDIheightIdx); 19 | HDImass = sum( (y(y>=HDIheight) )); 20 | 21 | lowerIndex = find( y>=HDIheight ,1,'first'); 22 | upperIndex = find( y>=HDIheight ,1,'last'); 23 | 24 | HDI.lower = x(lowerIndex); 25 | HDI.upper = x(upperIndex); 26 | 27 | return 28 | -------------------------------------------------------------------------------- /funcs/acf/acf.m: -------------------------------------------------------------------------------- 1 | function ta = acf(y,p) 2 | % ACF - Compute Autocorrelations Through p Lags 3 | % >> myacf = acf(y,p) 4 | % 5 | % Inputs: 6 | % y - series to compute acf for, nx1 column vector 7 | % p - total number of lags, 1x1 integer 8 | % 9 | % Output: 10 | % myacf - px1 vector containing autocorrelations 11 | % (First lag computed is lag 1. Lag 0 not computed) 12 | % 13 | % 14 | % A bar graph of the autocorrelations is also produced, with 15 | % rejection region bands for testing individual autocorrelations = 0. 16 | % 17 | % Note that lag 0 autocorelation is not computed, 18 | % and is not shown on this graph. 19 | % 20 | % Example: 21 | % >> acf(randn(100,1), 10) 22 | % 23 | 24 | 25 | % -------------------------- 26 | % USER INPUT CHECKS 27 | % -------------------------- 28 | 29 | [n1, n2] = size(y) ; 30 | if n2 ~=1 31 | error('Input series y must be an nx1 column vector') 32 | end 33 | 34 | [a1, a2] = size(p) ; 35 | if ~((a1==1 & a2==1) & (p abs(bar_hi)) % if rejection lines might not appear on graph 80 | axis([0 p+.60 line_lo line_hi]) 81 | else 82 | axis([0 p+.60 bar_lo bar_hi]) 83 | end 84 | title({' ','Sample Autocorrelations',' '}) 85 | xlabel('Lag Length') 86 | set(gca,'YTick',[-1:.20:1]) 87 | % set number of lag labels shown 88 | if (p<28 & p>4) 89 | set(gca,'XTick',floor(linspace(1,p,4))) 90 | elseif (p>=28) 91 | set(gca,'XTick',floor(linspace(1,p,8))) 92 | end 93 | set(gca,'TickLength',[0 0]) 94 | 95 | 96 | 97 | 98 | % --------------- 99 | % SUB FUNCTION 100 | % --------------- 101 | function ta2 = acf_k(y,k) 102 | % ACF_K - Autocorrelation at Lag k 103 | % acf(y,k) 104 | % 105 | % Inputs: 106 | % y - series to compute acf for 107 | % k - which lag to compute acf 108 | % 109 | global ybar 110 | global N 111 | cross_sum = zeros(N-k,1) ; 112 | 113 | % Numerator, unscaled covariance 114 | for i = (k+1):N 115 | cross_sum(i) = (y(i)-ybar)*(y(i-k)-ybar) ; 116 | end 117 | 118 | % Denominator, unscaled variance 119 | yvar = (y-ybar)'*(y-ybar) ; 120 | 121 | ta2 = sum(cross_sum) / yvar ; 122 | 123 | -------------------------------------------------------------------------------- /funcs/acf/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Calvin Price 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/MCMCdiagnoticsPlot.m: -------------------------------------------------------------------------------- 1 | function MCMCdiagnoticsPlot(samples,stats,fields) 2 | % 3 | % MCMCdiagnoticsPlot(samples,stats,{'a','b','c'}) 4 | % 5 | % MCMCdiagnoticsPlot(samples,stats,{'varint','lr','b'}) 6 | % 7 | % This function plots chains and posterior distributions of MCMC samples. 8 | % All the MCMC samples are assumed to be in a structure such as: 9 | % samples.a 10 | % samples.b 11 | % samples.c 12 | % 13 | 14 | 15 | nSamplesDisplayLimit=10^4; 16 | 17 | figure(1), clf 18 | %names = fieldnames(samples) 19 | 20 | rows = numel(fields); 21 | cols = 5; % chains, posterior 22 | 23 | % plot MCMC chains 24 | col=1; 25 | for r=1:rows 26 | % select the right subplot 27 | %ind = sub2ind([cols rows],[1:5],r); 28 | subplot(rows,cols,[(cols*r)-cols+1 : (cols*r)-1]) 29 | 30 | % plot MCMC chains, but only do so if there are fewer than 10^6 chains 31 | % as matlab can stall when plotting large numbers of data points. 32 | mcmcsamples = getfield(samples, fields{r}); 33 | 34 | % if there are more than 100,000 samples (per chain), then just plot the first 35 | % 100,000 because the plot just looks insane otherwise 36 | samplesPerChain = size(mcmcsamples,2); 37 | if samplesPerChain>nSamplesDisplayLimit 38 | mcmcsamples=mcmcsamples(:,[1:nSamplesDisplayLimit]); 39 | end 40 | samplesPerChainDisplayed = size(mcmcsamples,2); 41 | 42 | % pick colours for each line 43 | 44 | % CREATE COLOURS 45 | 46 | nchains = size(mcmcsamples,1); 47 | ColorSet = ColorBand(nchains); 48 | hold all 49 | set(gca, 'ColorOrder', ColorSet); 50 | 51 | plot(mcmcsamples',... 52 | 'LineWidth',0.2) 53 | 54 | % ylabel 55 | ylabel(fields(r)) 56 | set(gca,'XTick',[0:1000:samplesPerChainDisplayed]) 57 | 58 | %% 59 | % print Rhat statistic 60 | add_text_to_figure('T',... 61 | ['$\hat{R}$ = ' num2str(getfield(stats.Rhat, fields{r}))],... 62 | 20, 'latex') 63 | 64 | box off 65 | 66 | annotation('textbox', [0 0.9 1 0.1], ... 67 | 'String', 'up to the first 10,000 MCMC samples', ... 68 | 'EdgeColor', 'none', ... 69 | 'HorizontalAlignment', 'center') 70 | 71 | if r==rows 72 | xlabel('MCMC sample') 73 | end 74 | 75 | end 76 | 77 | 78 | %% 79 | % plot distributions 80 | col=2; 81 | for r=1:rows 82 | % select the right subplot 83 | subplot(rows,cols,(cols*r)) 84 | 85 | % plot MCMC chains 86 | mcmcsamples = getfield(samples, fields{r}); 87 | plotPost(mcmcsamples(:)); 88 | 89 | xlabel(fields(r)) 90 | end 91 | 92 | drawnow 93 | 94 | return 95 | -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/addDistributionSummaryText.m: -------------------------------------------------------------------------------- 1 | function addDistributionSummaryText(map, CI, position, fontsize) 2 | % Adds MAP and 95% CI info to a figure 3 | % 4 | % eg. 5 | % addDistributionSummaryText(0.9, [0.85 0.93], 'TR', 12) 6 | 7 | text_to_write=sprintf('%2.3f (%2.3f-%2.3f)',... 8 | map, CI(1), CI(2)); 9 | 10 | add_text_to_figure(position,text_to_write, fontsize) 11 | 12 | return -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/add_text_to_figure.m: -------------------------------------------------------------------------------- 1 | function add_text_to_figure(position,txt, fs, varargin) 2 | % 3 | % add_text_to_figure('TL','hello', 15) 4 | % 5 | % written by: Benjamin T Vincent 6 | 7 | 8 | if numel(varargin)==0 9 | interpreter='none'; 10 | else 11 | interpreter=varargin{1}; 12 | end 13 | 14 | a=axis; 15 | 16 | switch position 17 | case{'TL'} 18 | pos = [a(1) a(4)]; 19 | VerticalAlignment = 'top'; 20 | HorizontalAlignment = 'Left'; 21 | case{'TR'} 22 | pos = [a(2) a(4)]; 23 | VerticalAlignment = 'top'; 24 | HorizontalAlignment = 'Right'; 25 | 26 | case{'T'} 27 | pos = [(a(2)-a(1))/2 a(4)]; 28 | VerticalAlignment = 'top'; 29 | HorizontalAlignment = 'Center'; 30 | 31 | case{'BL'} 32 | pos = [a(1) a(3)]; 33 | VerticalAlignment = 'bottom'; 34 | HorizontalAlignment = 'Left'; 35 | case{'BR'} 36 | pos = [a(2) a(3)]; 37 | VerticalAlignment = 'bottom'; 38 | HorizontalAlignment = 'Right'; 39 | 40 | end 41 | 42 | text(pos(1),pos(2),[txt],... 43 | 'VerticalAlignment',VerticalAlignment,... 44 | 'HorizontalAlignment',HorizontalAlignment,... 45 | 'Color',[0 0 0],... 46 | 'FontSize', fs,... 47 | 'interpreter',interpreter); 48 | 49 | end -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/bens2Dmarginalplot.m: -------------------------------------------------------------------------------- 1 | 2 | 3 | clf 4 | % MAIN HISTOGRAM ================================================== 5 | subplot(2,2,2) 6 | 7 | 8 | xmin=0; xmax = max(xd(:)); 9 | ymin=0; ymax = max(yd(:)); 10 | 11 | xd = samples.varint(:); 12 | yd = samples.lapserate(:); 13 | n=40; 14 | xi = linspace(0,xmax,n); % varint 15 | yi = linspace(0,ymax,n); % lapse rate 16 | xr = interp1(xi,1:numel(xi),xd,'nearest')'; 17 | yr = interp1(yi,1:numel(yi),yd,'nearest')'; 18 | z = accumarray([xr' yr'], 1, [n n]); 19 | imagesc(xi,yi,z') 20 | 21 | 22 | 23 | xlabel('inferred variance') 24 | ylabel('inferred lapse rate') 25 | 26 | %set(gca,'PlotBoxAspectRatio',[1 1 1]) 27 | hline([],truevarint) 28 | hline(truelapserate) 29 | axis xy 30 | colormap(flipud(gray)) 31 | box off 32 | 33 | h1 = gca; 34 | 35 | 36 | % Y- MARGINAL ================================================== 37 | subplot(2,2,1); 38 | [n,yi] = hist(yd,31); 39 | barh(yi,n,1,... 40 | 'BarWidth',1,... 41 | 'FaceColor',[146 205 233]/255,... 42 | 'EdgeColor',[1 1 1]) 43 | box off 44 | hold on 45 | axis tight 46 | ylim([ymin ymax]) 47 | h2 = gca; 48 | axis('off'); 49 | hline(truelapserate) 50 | set(gca,'XDir','reverse') 51 | 52 | % Plot 95% CI 53 | a =axis; 54 | top =a(2); 55 | Y = prctile(yd,[5 95]); 56 | k=0.05; % the y-location of the line in terms of percent of the y-range 57 | 58 | % plot the 95% CI line 59 | plot([top*k top*k],Y,'k-',... 60 | 'LineWidth',3) 61 | 62 | 63 | % X-MARGINAL ================================================== 64 | subplot(2,2,4); 65 | 66 | [n,xi] = hist(xd,31); 67 | bar(xi,n,1,... 68 | 'BarWidth',1,... 69 | 'FaceColor',[146 205 233]/255,... 70 | 'EdgeColor',[1 1 1]) 71 | box off 72 | hold on 73 | axis tight 74 | xlim([xmin xmax]) 75 | h3 = gca; 76 | axis('off'); 77 | hline([],truevarint) 78 | 79 | % Plot 95% CI 80 | a =axis; 81 | top =a(4); 82 | Y = prctile(xd,[5 95]); 83 | k=0.05; % the y-location of the line in terms of percent of the y-range 84 | % plot the 95% CI line 85 | plot(Y,[top*k top*k],'k-',... 86 | 'LineWidth',3) 87 | 88 | 89 | 90 | % ============================================================= 91 | 92 | set(h1,'Position',[0.35 0.35 0.55 0.55]); 93 | set(h3,'Position',[.35 .1 .55 .15]); 94 | set(h2,'Position',[.1 .35 .15 .55]); -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/hline.m: -------------------------------------------------------------------------------- 1 | function out=hline(h,v) 2 | %HLINE Adds a horizontal/vertical line to current figure 3 | % hline(h) adds horizontal line at h 4 | % hline([],v) adds vertical line at v 5 | 6 | % Marko Laine 7 | % $Revision: 1.4 $ $Date: 2012/09/27 11:47:36 $ 8 | 9 | ax=get(gcf,'CurrentAxes'); 10 | ylim=get(ax,'YLim'); 11 | xlim=get(ax,'Xlim'); 12 | 13 | if isempty(h) 14 | hl=line([v v], ylim); 15 | else 16 | hl=line(xlim, [h h]); 17 | end 18 | set(hl,'Color',[0 0 0]); 19 | set(hl,'LineStyle',':'); 20 | set(hl,'LineWidth',1); 21 | 22 | if nargout>0 23 | out=hl; 24 | end 25 | -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/log_plot_with_background.m: -------------------------------------------------------------------------------- 1 | function log_plot_with_background(IMAGE,... 2 | imXdata , imYdata,... 3 | pltXdata, pltYdata) 4 | 5 | 6 | imagesc(imXdata,imYdata,IMAGE) 7 | ax1 = gca; 8 | axis xy 9 | ax1_pos = get(ax1,'Position'); 10 | set(ax1,'Visible','off') 11 | colormap(gray) 12 | colormap(flipud(colormap)) 13 | axis square 14 | 15 | 16 | %% 17 | ax2 = axes('Position',ax1_pos,... 18 | 'XAxisLocation','bottom',... 19 | 'YAxisLocation','left',... 20 | 'Color','none',... 21 | 'XScale','log',... 22 | 'XTick',[0.01 0.1 1, 10],... 23 | 'YTick',[0:0.1:1]); 24 | %ax2.TickDir='out' 25 | 26 | % ax2.XTick=[0.01 0.1 1, 10, 100] 27 | % ax2.YTick=[0:0.1:1] 28 | axis square 29 | 30 | %% 31 | l=line(pltXdata,pltYdata,'Parent',ax2,'Color','k'); 32 | set(l,'Marker','o',... 33 | 'MarkerFaceColor','w',... 34 | 'MarkerSize',8,... 35 | 'LineStyle','none') 36 | 37 | % l.Marker='o'; 38 | % l.MarkerFaceColor='w'; 39 | % l.MarkerSize=10; 40 | % l.LineStyle='none'; 41 | 42 | % ensure same y scale 43 | set(ax1,'YLim',[0.3 1]); 44 | set(ax2,'YLim',[0.3 1]); 45 | 46 | set(ax1,'XLim',[0 10]); 47 | set(ax2,'XLim',[0 10]); 48 | 49 | xlabel('signal intensity, \Delta\mu') 50 | ylabel('proportion correct, k/T') 51 | return -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/myDensityMatrix.m: -------------------------------------------------------------------------------- 1 | function myDensityMatrix(x,y,z, varnames, truevals, support) 2 | % 3 | % my cheap-ass implementation of https://github.com/dfm/triangle.py 4 | 5 | clf 6 | colormap(gray) 7 | 8 | bins = 40; 9 | fs = 10; 10 | 11 | % x=randn(1000,1)-3; 12 | % y=randn(1000,1); 13 | % z=randn(1000,1)+3; 14 | % varnames={'x','y','z'}; 15 | % %varnames={'\sigma^2','\lambda','b'}; 16 | % truevals=[-3 0 3]; 17 | 18 | % Calculate consistent ranges 19 | k=1.1; 20 | xrange=[min(x) max(x)]; 21 | yrange=[min(y) max(y)]; 22 | zrange=[min(z) max(z)]; 23 | 24 | 25 | 26 | %% plot the marginal histrograms 27 | h.x = subplot(3,3,1); 28 | internal_histogram(x,xrange, support{1}) 29 | hline([],truevals(1)) 30 | set(gca,'XLim',xrange) 31 | 32 | h.y = subplot(3,3,5); 33 | internal_histogram(y,yrange, support{2}) 34 | hline([],truevals(2)) 35 | set(gca,'XLim',yrange) 36 | 37 | h.z = subplot(3,3,9); 38 | internal_histogram(z,zrange, support{3}) 39 | hline([],truevals(3)) 40 | set(gca,'XLim',zrange) 41 | 42 | %% 43 | % x,y 44 | h.xy = subplot(3,3,4); 45 | internal_twowayplot(x,y) 46 | hline([],truevals(1)) 47 | hline(truevals(2)) 48 | set(gca,'XLim',xrange) 49 | set(gca,'YLim',yrange) 50 | 51 | % x,z 52 | h.xz = subplot(3,3,7); 53 | internal_twowayplot(x,z) 54 | hline([],truevals(1)) 55 | hline(truevals(3)) 56 | set(gca,'XLim',xrange) 57 | set(gca,'YLim',zrange) 58 | 59 | % y,z 60 | h.yz = subplot(3,3,8); 61 | internal_twowayplot(y,z) 62 | hline([],truevals(2)) 63 | hline(truevals(3)) 64 | set(gca,'XLim',yrange) 65 | set(gca,'YLim',zrange) 66 | 67 | %% set global axis properties 68 | all_handles = get(gcf,'Children'); 69 | set(all_handles,'FontSize',fs,... 70 | 'LineWidth',1) 71 | 72 | %% 73 | % add y-axis labels 74 | subplot(3,3,4), ylabel(varnames(2), 'FontSize',fs) 75 | subplot(3,3,7), ylabel(varnames(3), 'FontSize',fs) 76 | % add x-axis labels 77 | subplot(3,3,7), xlabel(varnames(1), 'FontSize',fs) 78 | subplot(3,3,8), xlabel(varnames(2), 'FontSize',fs) 79 | subplot(3,3,9), xlabel(varnames(3), 'FontSize',fs) 80 | 81 | % remove x tick labels 82 | subplot(3,3,1), set(gca,'XTickLabel',[]) 83 | subplot(3,3,4), set(gca,'XTickLabel',[]) 84 | subplot(3,3,5), set(gca,'XTickLabel',[]) 85 | 86 | % remove y tick labels 87 | subplot(3,3,1), set(gca,'YTickLabel',[]) 88 | subplot(3,3,5), set(gca,'YTickLabel',[]) 89 | subplot(3,3,8), set(gca,'YTickLabel',[]) 90 | subplot(3,3,9), set(gca,'YTickLabel',[]) 91 | 92 | %% set positions 93 | width=0.25; 94 | height=0.25; 95 | lb=0.15; % left border 96 | bb=0.15; % bottom border 97 | set(h.xz,'Position',[lb bb width height]); 98 | set(h.yz,'Position',[lb+width bb width height]); 99 | set(h.z,'Position',[lb+width+width bb width height]); 100 | 101 | set(h.xy,'Position',[lb bb+height width height]); 102 | set(h.x,'Position',[lb bb+height+height width height]); 103 | 104 | set(h.y,'Position',[lb+width bb+height width height]); 105 | 106 | %% 107 | set(gcf,'Position',[0 0 360 360]) 108 | drawnow 109 | 110 | %% 111 | function internal_twowayplot(a,b) 112 | %plot(a,b,'k.') 113 | 114 | my_2d_hist(a,b , bins, bins); 115 | colormap(flipud(colormap)) 116 | set(gca,'TickDir','in') 117 | axis square 118 | end 119 | 120 | function internal_histogram(data, range, supp) 121 | [n,xi] = hist(data,linspace(min(range),max(range),bins)); 122 | area(xi,n,... 123 | 'FaceColor', [0.7 0.7 0.7], ... 124 | 'LineStyle','none') 125 | % give a bit of headroom for the histogram 126 | axis tight; a=axis; ylim([0 a(4)*1.1]) 127 | axis square 128 | set(gca,'TickDir','in') 129 | hold on 130 | % 131 | [MAP, xi, p, CI95] = sampleStats(data, supp); 132 | 133 | % plot 95% CI line 134 | a=axis; ypos=a(4)*0.05; 135 | plot(CI95,[ypos ypos],'k-') 136 | % Add summary info 137 | addDistributionSummaryText(MAP, CI95, 'TR', fs-2) 138 | 139 | 140 | end 141 | 142 | 143 | end 144 | 145 | 146 | -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/my_2d_hist.m: -------------------------------------------------------------------------------- 1 | function [density,bx,by]=my_2d_hist(x,y , nxbins,nybins) 2 | % Converts 2D scatter x,y data into a 2D grid image of density 3 | 4 | 5 | bx=linspace(min(x),max(x),nxbins); 6 | 7 | by=linspace(min(y),max(y),nybins); 8 | 9 | xbinhalfwidth=abs(bx(2)-bx(1)); 10 | ybinhalfwidth=abs(by(2)-by(1)); 11 | 12 | density=zeros(length(bx),length(by)); 13 | 14 | for cx=1:length(bx) 15 | 16 | for cy=1:length(by) 17 | 18 | SET = (x > (bx(cx)-xbinhalfwidth) & x < (bx(cx)+xbinhalfwidth))... 19 | & (y > (by(cy)-ybinhalfwidth) & y < (by(cy)+ybinhalfwidth)); 20 | 21 | density(cx,cy)=sum(SET); 22 | 23 | clear SET 24 | 25 | end 26 | end 27 | 28 | 29 | imagesc(bx,by,density') 30 | axis xy 31 | 32 | end 33 | 34 | % hold on 35 | % 36 | % plot(x,y,'.','MarkerSize',1) 37 | % hold off -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/my_errorbarsUL.m: -------------------------------------------------------------------------------- 1 | function [lineHandles]=my_errorbarsUL(X,U,L,opts) 2 | % U = upper 3 | % L = lower 4 | 5 | l=zeros(numel(X),1); 6 | 7 | for n=1:numel(X) 8 | lineHandles(n) = line([X(n) X(n)],[U(n) L(n)]); 9 | end 10 | 11 | %% Apply formatting 12 | 13 | % cycle through options provided and apply them. These are patch properties 14 | % which are listed here: 15 | % http://www.mathworks.co.uk/help/matlab/ref/patch_props.html 16 | for n=1:2:numel(opts) 17 | set(lineHandles, opts{n}, opts{n+1}); 18 | end -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/my_shaded_errorbar_zone_UL.m: -------------------------------------------------------------------------------- 1 | function [h]=my_shaded_errorbar_zone_UL(x,upper,lower,col) 2 | % Plots a shaded region of error 3 | % 4 | % my_shaded_errorbar_zone_UL([-10:0.1:10],[-10:0.1:10]+1,[-10:0.1:10]-1,[0.7 0.7 0.7]) 5 | % 6 | % eg, my_shaded_errorbar_zone([-10:0.1:10],x,abs(randn(size(x)))+2,[0 0 1]) 7 | % 8 | %handle=patch([x max(x)-x+min(x)],[y+e flipud(y'-e')'],[0.8 0.8 0.8]); 9 | %handle=patch([x max(x)-x+min(x)],[upper flipud(lower')'],[0.8 0.8 0.8]); 10 | % 11 | % 12 | % written by: Benjamin T Vincent 13 | 14 | 15 | g=ishold(gca); 16 | hold on 17 | 18 | %% 19 | % draw the shaded error bar zone 20 | x =[x,fliplr(x)]; 21 | y =[upper,fliplr(lower)]; 22 | h =patch(x,y,[0.8 0.8 0.8]); 23 | 24 | % move it to the back 25 | uistack(h,'bottom') 26 | 27 | % set the colour 28 | set(h,'EdgeColor',col) 29 | set(h,'FaceColor',col) 30 | 31 | %set(handle,'FaceAlpha',0.5) 32 | 33 | % leave the figure hold status as it was 34 | if g==0 35 | hold off 36 | end 37 | 38 | return -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/plotChainAutocorrelationsModel2.m: -------------------------------------------------------------------------------- 1 | function plotChainAutocorrelationsModel2(samples) 2 | 3 | nChains = size(samples.v,1); 4 | 5 | cols = nChains; % chains 6 | rows = 3; % variables 7 | count= 1; 8 | 9 | fig=4; 10 | 11 | figure(fig), clf 12 | plot_autocorr_for_all_chains(samples.v) 13 | plot_autocorr_for_all_chains(samples.lr) 14 | plot_autocorr_for_all_chains(samples.b) 15 | 16 | 17 | figure(fig) 18 | 19 | subplot(rows,cols, (nChains*0)+1), ylabel('\sigma^2','FontSize',16) 20 | subplot(rows,cols, (nChains*1)+1), ylabel('\lambda','FontSize',16) 21 | subplot(rows,cols, (nChains*2)+1), ylabel('b','FontSize',16) 22 | 23 | for n=1:cols 24 | subplot(rows,cols,n) 25 | title(['Chain ' num2str(n)]) 26 | end 27 | 28 | 29 | 30 | function plot_autocorr_for_all_chains(samples) 31 | 32 | nchains = size(samples,1); 33 | lag = 200; 34 | 35 | for c=1:nchains 36 | subplot(rows,cols, count) 37 | 38 | acf( samples(c,:)', lag); 39 | 40 | % remove title etc 41 | title('') 42 | box off 43 | set(gca,'PlotBoxAspectRatio',[1.5 1 1]) 44 | count=count+1; 45 | end 46 | end 47 | 48 | end 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/plotPost.m: -------------------------------------------------------------------------------- 1 | function [h]=plotPost(x) 2 | 3 | % % PLOT AS A FILLED HISTOGRAM ----------------- 4 | % [f,xi]=hist(x,64); 5 | % 6 | % h.bar = bar(xi,f,32,... 7 | % 'BarWidth',1,... 8 | % 'FaceColor',[0.8 0.8 0.8],... 9 | % 'EdgeColor','none'); 10 | % % -------------------------------------------- 11 | 12 | % PLOT AS AN OUTLINED HISTOGRAM -------------- 13 | [f,xx] = hist(x, 50); 14 | % normalise it to a probability mass function 15 | f=f./sum(f); 16 | % plot 17 | h = stairs(xx, f, 'k-'); 18 | % -------------------------------------------- 19 | 20 | 21 | axis tight 22 | ylim([0 max(f)]) 23 | box off 24 | 25 | % keep y axis, but remove labels 26 | set(gca,'yticklabel',{},... 27 | 'YTick',[]) 28 | 29 | % Plot 95% CI 30 | hold on 31 | Y = prctile(x,[5 95]); 32 | a = axis; top = a(4); k=0.03; 33 | plot(Y,[top*k top*k],'k-',... 34 | 'LineWidth',3); 35 | 36 | hold off -------------------------------------------------------------------------------- /funcs/bensPlotFunctions/plot_formatting_setup.m: -------------------------------------------------------------------------------- 1 | % plot_formatting_setup 2 | % 3 | % Changes a few defaults to make plots nicer 4 | 5 | set(0,'DefaultFigurePaperType', 'A4'); 6 | %set(0,'DefaultFigureWindowStyle', 'normal'); % 'normal' or 'docked' 7 | 8 | set(0,'DefaultAxesBox', 'off'); 9 | set(0,'DefaultFigureColor',[1 1 1]) 10 | 11 | set(0,'DefaultAxesLineWidth',2) 12 | set(0,'DefaultLineLineWidth',2) 13 | 14 | set(0,'defaultaxesfontsize',14) 15 | set(0,'defaulttextfontsize',20) 16 | 17 | set(0,'DefaultFigureColormap',gray); close 18 | 19 | set(0,'DefaultAxesLayer','top') 20 | set(0,'DefaultAxesTickDir','out') 21 | set(0,'DefaultAxesTickLength',[0.02 0.01]) -------------------------------------------------------------------------------- /funcs/bensUtils/argmax.m: -------------------------------------------------------------------------------- 1 | function index = argmax(x) 2 | [~,index] = max(x(:)); 3 | return 4 | -------------------------------------------------------------------------------- /funcs/bensUtils/min_sec.m: -------------------------------------------------------------------------------- 1 | function [m,s]=min_sec(t,varargin) 2 | % This function simply prints the time between a tic and a toc command and 3 | % does so in a more friendly way than the default. 4 | % [m,s]=min_sec(toc, ['print','no_print']) 5 | % 6 | % written by: Benjamin T Vincent 7 | 8 | switch nargin 9 | case{1} 10 | shall_I_print='print'; 11 | case{2} 12 | shall_I_print=varargin{1}; 13 | end 14 | 15 | % How many minuites? 16 | m = fix(t/60); 17 | 18 | % How many seconds? 19 | s = t - (60*m); 20 | 21 | switch shall_I_print 22 | case{'print'} 23 | if m>0 24 | fprintf('%2.0fmin %2.0fsec\n',m,s) 25 | else 26 | fprintf('%2.3f sec \n',s) 27 | end 28 | end 29 | 30 | return -------------------------------------------------------------------------------- /funcs/bensUtils/vec.m: -------------------------------------------------------------------------------- 1 | function [output]=vec(input) 2 | % This function vectorises the input 3 | output=input(:); 4 | return 5 | -------------------------------------------------------------------------------- /funcs/define_experiment_params.m: -------------------------------------------------------------------------------- 1 | function data = define_experiment_params(model) 2 | % 3 | % data = define_experiment_params('model1') 4 | % data = define_experiment_params('model2') 5 | % data = define_experiment_params('model3') 6 | 7 | % common parameters 8 | data.T = 100; 9 | data.sioriginal = logspace(-2,2,10); 10 | data.muN = 0; 11 | data.v = 1; 12 | 13 | % in order to conduct model prediction on si values that we do not have 14 | % response data for, we will actually generate simulated data for all these 15 | % additional si values now. But the data we generate will be used in step 16 | % 2 where we only provide the model with response data for the actual si 17 | % values run in an experiment. In other words, we are just using this step 18 | % now to generate simulated 2AFC trial data (L) for these additional si 19 | % values we wish to examine. 20 | ni = 41; 21 | data.sii = logspace(-2,2,ni); 22 | 23 | 24 | switch model 25 | 26 | case{'model1'} 27 | data.si = [data.sioriginal data.sii]; 28 | 29 | case{'model2'} 30 | data.lr = 0.01; % true lapse rate 31 | data.b = 0; % true bias 32 | data.pdist = [0.5 0.5];% trie spatial prior 33 | data.si = [data.sioriginal data.sioriginal data.sii]; 34 | 35 | case{'model3'} 36 | data.si = [data.sioriginal data.sii]; 37 | 38 | end 39 | 40 | return -------------------------------------------------------------------------------- /funcs/define_mcmcparams.m: -------------------------------------------------------------------------------- 1 | function mcmcparams = define_mcmcparams(model,varargin) 2 | % 3 | % mcmcparams = define_mcmcparams('model1') 4 | % mcmcparams = define_mcmcparams('model2',T) 5 | % mcmcparams = define_mcmcparams('model3') 6 | 7 | 8 | % Parallel use of multiple CPU cores 9 | mcmcparams.doparallel = 1; 10 | 11 | % How many cores do we have? 12 | numOfCores= feature('numcores'); 13 | 14 | % Set up access to use multiple CPU cores 15 | try 16 | if isempty(gcp('nocreate'))==1 17 | % Enable use of multiple cores 18 | parpool 19 | end 20 | catch 21 | % for whatever reason, we have failed at setting up use of multiple 22 | % cores, so revert to non-parallel 23 | mcmcparams.doparallel = 0; 24 | numOfCores=1; % just use one core. 25 | end 26 | 27 | 28 | switch model 29 | case{'model1'} 30 | 31 | mcmcparams.JAGSmodel = 'jagsmodels/model1JAGS.txt'; 32 | 33 | % 1) Dataset generation - only used if we are generating new 34 | % simulated behavioural data, not when we are loading pre-computed 35 | % data. 36 | mcmcparams.generate.nchains = 1; 37 | mcmcparams.generate.nburnin = 1000; 38 | mcmcparams.generate.nsamples = mcmcparams.generate.nburnin + 1; 39 | 40 | % 2) Parameter recovery 41 | mcmcparams.infer.nchains = numOfCores; 42 | mcmcparams.infer.nburnin = 500; % 500 43 | total_samples = 10^5; % 10^7 in paper 44 | mcmcparams.infer.nsamples = round(total_samples/mcmcparams.infer.nchains); 45 | 46 | case{'model2'} 47 | 48 | T=varargin{1}; 49 | 50 | mcmcparams.JAGSmodel = 'jagsmodels/model2JAGS.txt'; 51 | 52 | % 1) Dataset generation - only used if we are generating new 53 | % simulated behavioural data, not when we are loading pre-computed 54 | % data. 55 | mcmcparams.generate.nchains = 1; 56 | mcmcparams.generate.nburnin = 500; 57 | mcmcparams.generate.nsamples = T; 58 | 59 | % 2) Parameter recovery 60 | mcmcparams.infer.nchains = numOfCores; % 4 61 | mcmcparams.infer.nburnin = 1000; % 1000 62 | % TOTAL SAMPLES: 63 | % 10^6 takes ~4-5 hours 64 | % 10^5 takes ~25 mins on my quadcore iMac 65 | total_samples = 10^4; % 10^5 in paper 66 | mcmcparams.infer.nsamples = round(total_samples/mcmcparams.infer.nchains); 67 | 68 | case{'model3'} 69 | 70 | T = 100; 71 | 72 | mcmcparams.JAGSmodel = 'jagsmodels/model3JAGS.txt'; 73 | 74 | % 1) Dataset generation 75 | mcmcparams.generate.nchains = 2; % 2 76 | mcmcparams.generate.nburnin = 500; % 500 77 | mcmcparams.generate.nsamples = T; % number of trials to simulate 78 | 79 | % 2) Parameter recovery 80 | mcmcparams.infer.nchains = numOfCores; 81 | mcmcparams.infer.nburnin = 1000; 82 | total_samples = 10^3; % 10^4 83 | mcmcparams.infer.nsamples = round(total_samples/mcmcparams.infer.nchains); 84 | 85 | end 86 | 87 | return -------------------------------------------------------------------------------- /funcs/export_fig/.gitignore: -------------------------------------------------------------------------------- 1 | /.ignore 2 | *.txt 3 | *.asv 4 | *~ 5 | *.mex* 6 | -------------------------------------------------------------------------------- /funcs/export_fig/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Oliver J. Woodford 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /funcs/export_fig/README.md: -------------------------------------------------------------------------------- 1 | export_fig 2 | ========== 3 | 4 | A toolbox for exporting figures from MATLAB to standard image and document formats nicely. 5 | 6 | ### Overview 7 | Exporting a figure from MATLAB the way you want it (hopefully the way it looks on screen), can be a real headache for the unitiated, thanks to all the settings that are required, and also due to some eccentricities (a.k.a. features and bugs) of functions such as `print`. The first goal of export_fig is to make transferring a plot from screen to document, just the way you expect (again, assuming that's as it appears on screen), a doddle. 8 | 9 | The second goal is to make the output media suitable for publication, allowing you to publish your results in the full glory that you originally intended. This includes embedding fonts, setting image compression levels (including lossless), anti-aliasing, cropping, setting the colourspace, alpha-blending and getting the right resolution. 10 | 11 | Perhaps the best way to demonstrate what export_fig can do is with some examples. 12 | 13 | ### Examples 14 | **Visual accuracy** - MATLAB's exporting functions, namely `saveas` and `print`, change many visual properties of a figure, such as size, axes limits and ticks, and background colour, in unexpected and unintended ways. Export_fig aims to faithfully reproduce the figure as it appears on screen. For example: 15 | ```Matlab 16 | plot(cos(linspace(0, 7, 1000))); 17 | set(gcf, 'Position', [100 100 150 150]); 18 | saveas(gcf, 'test.png'); 19 | export_fig test2.png 20 | ``` 21 | generates the following: 22 | 23 | | Figure: | test.png: | test2.png: | 24 | |:-------:|:---------:|:----------:| 25 | |![](https://farm6.staticflickr.com/5616/15589249291_16e485c29a_o_d.png)|![](https://farm4.staticflickr.com/3944/15406302850_4d2e1c7afa_o_d.png)|![](https://farm6.staticflickr.com/5607/15568225476_8ce9bd5f6b_o_d.png)| 26 | 27 | Note that the size and background colour of test2.png (the output of export_fig) are the same as those of the on screen figure, in contrast to test.png. Of course, if you want want the figure background to be white (or any other colour) in the exported file then you can set this prior to exporting using: 28 | ```Matlab 29 | set(gcf, 'Color', 'w'); 30 | ``` 31 | 32 | Notice also that export_fig crops and anti-aliases (smooths, for bitmaps only) the output by default. However, these options can be disabled; see the Tips section below for details. 33 | 34 | **Resolution** - by default, export_fig exports bitmaps at screen resolution. However, you may wish to save them at a different resolution. You can do this using either of two options: `-m`, where is a positive real number, magnifies the figure by the factor for export, e.g. `-m2` produces an image double the size (in pixels) of the on screen figure; `-r`, again where is a positive real number, specifies the output bitmap to have pixels per inch, the dimensions of the figure (in inches) being those of the on screen figure. For example, using: 35 | ```Matlab 36 | export_fig test.png -m2.5 37 | ``` 38 | on the figure from the example above generates: 39 | 40 | ![](https://farm4.staticflickr.com/3937/15591910915_dc7040c477_o_d.png) 41 | 42 | Sometimes you might have a figure with an image in. For example: 43 | ```Matlab 44 | imshow(imread('cameraman.tif')) 45 | hold on 46 | plot(0:255, sin(linspace(0, 10, 256))*127+128); 47 | set(gcf, 'Position', [100 100 150 150]); 48 | ``` 49 | generates this figure: 50 | 51 | ![](https://farm4.staticflickr.com/3942/15589249581_ff87a56a3f_o_d.png) 52 | 53 | Here the image is displayed in the figure at resolution lower than its native resolution. However, you might want to export the figure at a resolution such that the image is output at its native (i.e. original) size (in pixels). Ordinarily this would require some non-trivial computation to work out what that resolution should be, but export_fig has an option to do this for you. Using: 54 | ```Matlab 55 | export_fig test.png -native 56 | ``` 57 | produces: 58 | 59 | ![](https://farm6.staticflickr.com/5604/15589249591_da2b2652e4_o_d.png) 60 | 61 | with the image being the size (in pixels) of the original image. Note that if you want an image to be a particular size, in pixels, in the output (other than its original size) then you can resize it to this size and use the `-native` option to achieve this. 62 | 63 | All resolution options (`-m`, `-q` and `-native`) correctly set the resolution information in PNG and TIFF files, as if the image were the dimensions of the on screen figure. 64 | 65 | **Shrinking dots & dashes** - when exporting figures with dashed or dotted lines using either the ZBuffer or OpenGL (default for bitmaps) renderers, the dots and dashes can appear much shorter, even non-existent, in the output file, especially if the lines are thick and/or the resolution is high. For example: 66 | ```Matlab 67 | plot(sin(linspace(0, 10, 1000)), 'b:', 'LineWidth', 4); 68 | hold on 69 | plot(cos(linspace(0, 7, 1000)), 'r--', 'LineWidth', 3); 70 | grid on 71 | export_fig test.png 72 | ``` 73 | generates: 74 | 75 | ![](https://farm4.staticflickr.com/3956/15592747732_f943d4aa0a_o_d.png) 76 | 77 | This problem can be overcome by using the painters renderer. For example: 78 | ```Matlab 79 | export_fig test.png -painters 80 | ``` 81 | used on the same figure generates: 82 | 83 | ![](https://farm4.staticflickr.com/3945/14971168504_77692f11f5_o_d.png) 84 | 85 | Note that not only are the plot lines correct, but the grid lines are too. 86 | 87 | **Transparency** - sometimes you might want a figure and axes' backgrounds to be transparent, so that you can see through them to a document (for example a presentation slide, with coloured or textured background) that the exported figure is placed in. To achieve this, first (optionally) set the axes' colour to 'none' prior to exporting, using: 88 | ```Matlab 89 | set(gca, 'Color', 'none'); % Sets axes background 90 | ``` 91 | 92 | then use export_fig's `-transparent` option when exporting: 93 | ```Matlab 94 | export_fig test.png -transparent 95 | ``` 96 | 97 | This will make the background transparent in PDF, EPS and PNG outputs. You can additionally save fully alpha-blended semi-transparent patch objects to the PNG format. For example: 98 | 99 | ```Matlab 100 | logo; 101 | alpha(0.5); 102 | ``` 103 | 104 | generates a figure like this: 105 | 106 | ![](https://farm4.staticflickr.com/3933/15405290339_b08de33528_o_d.png) 107 | 108 | If you then export this to PNG using the `-transparent` option you can then put the resulting image into, for example, a presentation slide with fancy, textured background, like so: 109 | 110 | ![](https://farm6.staticflickr.com/5599/15406302920_59beaefff1_o_d.png) 111 | 112 | and the image blends seamlessly with the background. 113 | 114 | **Image quality** - when publishing images of your results, you want them to look as good as possible. By default, when outputting to lossy file formats (PDF, EPS and JPEG), export_fig uses a high quality setting, i.e. low compression, for images, so little information is lost. This is in contrast to MATLAB's print and saveas functions, whose default quality settings are poor. For example: 115 | ```Matlab 116 | A = im2double(imread('peppers.png')); 117 | B = randn(ceil(size(A, 1)/6), ceil(size(A, 2)/6), 3) * 0.1; 118 | B = cat(3, kron(B(:,:,1), ones(6)), kron(B(:,:,2), ones(6)), kron(B(:,:,3), ones(6))); 119 | B = A + B(1:size(A, 1),1:size(A, 2),:); 120 | imshow(B); 121 | print -dpdf test.pdf 122 | ``` 123 | generates a PDF file, a sub-window of which looks (when zoomed in) like this: 124 | 125 | ![](https://farm6.staticflickr.com/5613/15405290309_881b2774d6_o_d.png) 126 | 127 | while the command 128 | 129 | ```Matlab 130 | export_fig test.pdf 131 | ``` 132 | on the same figure produces this: 133 | 134 | ![](https://farm4.staticflickr.com/3947/14971168174_687473133f_o_d.png) 135 | 136 | While much better, the image still contains some compression artifacts (see the low level noise around the edge of the pepper). You may prefer to export with no artifacts at all, i.e. lossless compression. Alternatively, you might need a smaller file, and be willing to accept more compression. Either way, export_fig has an option that can suit your needs: `-q`, where is a number from 0-100, will set the level of lossy image compression (again in PDF, EPS and JPEG outputs only; other formats are lossless), from high compression (0) to low compression/high quality (100). If you want lossless compression in any of those formats then specify a greater than 100. For example: 137 | ```Matlab 138 | export_fig test.pdf -q101 139 | ``` 140 | again on the same figure, produces this: 141 | 142 | ![](https://farm6.staticflickr.com/5608/15405803908_934512c1fe_o_d.png) 143 | 144 | Notice that all the noise has gone. 145 | 146 | ### Tips 147 | **Anti-aliasing** - the anti-aliasing which export_fig applies to bitmap outputs by default makes the images look nice, but it can also blur images and increase exporting time and memory requirements, so you might not always want it. You can set the level of anti-aliasing by using the `-a` option, where is 1 (no anti-aliasing), 2, 3 (default) or 4 (maximum anti-aliasing). 148 | 149 | **Cropping** - by default, export_fig crops its output to minimize the amount of empty space around the figure. If you'd prefer the figure to be uncropped, and instead have the same appearance (in terms of border width) as the on screen figure, then use the `-nocrop` option. 150 | 151 | **Colourspace** - by default, export_fig generates files in the RGB [colourspace](http://en.wikipedia.org/wiki/Color_space). However, you can also export in greyscale or the CMYK colourspace, using the `-grey` (or `-gray`) and `-cmyk` options respectively. The CMYK option is useful for publishers who require documents in this colourspace, but the option is only supported for PDF, EPS and TIFF files. 152 | 153 | **Specifying a target directory** - you can get export_fig to save output files to any directory (for which you have write permission), simply by specifying the full or relative path in the filename. For example: 154 | ```Matlab 155 | export_fig ../subdir/fig.png; 156 | export_fig('C:/Users/Me/Documents/figures/myfig', '-pdf', '-png'); 157 | ``` 158 | 159 | **Variable file names** - often you might want to save a series of figures in a for loop, each with a different name. For this you can use the functional form of input arguments, i.e. `export_fig(arg1, arg2)`, and construct the filename string in a variable. Here's an example of this: 160 | ```Matlab 161 | for a = 1:5 162 | plot(rand(5, 2)); 163 | export_fig(sprintf('plot%d.png', a)); 164 | end 165 | ``` 166 | When using the functional form like this, be sure to put string variables in quotes: 167 | ```Matlab 168 | export_fig(sprintf('plot%d', a), '-a1', '-pdf', '-png'); 169 | ``` 170 | 171 | **Specifying the figure/axes** - if you have mutiple figures open you can specify which figure to export using its handle: 172 | ```Matlab 173 | export_fig(figure_handle, 'filename.fmt'); 174 | ``` 175 | Equally, if your figure contains several subplots then you can export just one of them by giving export_fig the handle to the relevant axes: 176 | ```Matlab 177 | export_fig(axes_handle, 'filename.fmt'); 178 | ``` 179 | 180 | **Multiple formats** - save time by exporting to multiple formats simultaneously. E.g.: 181 | ```Matlab 182 | export_fig filename -pdf -eps -png -jpg -tiff 183 | ``` 184 | 185 | **Other file formats** - if you'd like to save your figure to a bitmap format that is not supported by export_fig, e.g. animated GIF, PPM file or a frame in a movie, then you can use export_fig to output the image, and optionally an alpha-matte, to the workspace. E.g.: 186 | ```Matlab 187 | frame = export_fig; 188 | ``` 189 | or 190 | ```Matlab 191 | [frame, alpha] = export_fig; 192 | ``` 193 | These variables can then be saved to other image formats using other functions, such as imwrite. 194 | 195 | **Appending to a file** - you can use the `-append` option to append the figure to the end of an image/document, if it already exists. This is supported for PDF and TIFF files only. Note that if you wish to append a lot of figures consecutively to a PDF, it can be more efficient to save all the figures to PDF separately then append them all in one go at the end (e.g. using [append_pdfs](http://www.mathworks.com/matlabcentral/fileexchange/31215-appendpdfs)). 196 | 197 | **Font size** - if you want to place an exported figure in a document with the font a particular size then you need to set the font to that size in the figure, and not resize the output of export_fig in the document. To avoid resizing, simply make sure that the on screen figure is the size you want the output to be in the document before exporting. 198 | 199 | **Renderers** - MATLAB has three renderers for displaying and exporting figures: painters, OpenGL and ZBuffer. The different renderers have different [features](http://www.mathworks.com/access/helpdesk/help/techdoc/creating_plots/f3-84337.html#f3-102410), so if you aren't happy with the result from one renderer try another. By default, vector formats (i.e. PDF and EPS outputs) use the painters renderer, while other formats use the OpenGL renderer. Non-default renderers can be selected by using one of these three export_fig input options: `-painters`, `-opengl`, `-zbuffer`. 200 | 201 | **Artifacts** - sometimes the output that you get from export_fig is not what you expected. If an output file contains artifacts that aren't in the on screen figure then make sure that the renderer used for rendering the figure on screen is the same as that used for exporting. To set the renderer used to display the figure, use: 202 | ```Matlab 203 | set(figure_handle, 'Renderer', 'opengl'); 204 | ``` 205 | After matching the two renderers, if the artifact appears in the on screen figure then you'll need to fix that before exporting. Alternatively you can try changing the renderer used by export_fig. Finally check that it isn't one of the known issues mentioned in the section below. 206 | 207 | **Smoothed/interpolated images in output PDF** - if you produce a PDF using export_fig and images in the PDF look overly smoothed or interpolated, this is because the software you are using to view the PDF is smoothing or interpolating the image data. The image is not smoothed in the PDF file itself. If the software has an option to disable this feature, you should select it. Alternatively, use another PDF viewer that doesn't exhibit this problem. 208 | 209 | **Locating Ghostscript/pdftops** - You may find a dialogue box appears when using export_fig, asking you to locate either [Ghostscript](http://www.ghostscript.com) or [pdftops](http://www.foolabs.com/xpdf). These are separate applications which export_fig requires to perform certain functions. If such a dialogue appears it is because export_fig can't find the application automatically. This is because you either haven't installed it, or it isn't in the normal place. Make sure you install the applications correctly first. They can be downloaded from the following places: 210 | 1. Ghostscript: [www.ghostscript.com](http://www.ghostscript.com) 211 | 2. pdftops (install the Xpdf package): [www.foolabs.com/xpdf](http://www.foolabs.com/xpdf) 212 | 213 | If you choose to install them in a non-default location then point export_fig 214 | to this location using the dialogue box. 215 | 216 | **Undefined function errors** - If you download and run export_fig and get an error similar to this: 217 | ```Matlab 218 | ??? Undefined function or method 'print2array' for input arguments of type 'double'. 219 | ``` 220 | then you are missing one or more of the files that come in the export_fig package. Make sure that you click the "Get from GitHub" button at the top-right of the download [page](http://www.mathworks.co.uk/matlabcentral/fileexchange/23629-exportfig), then extract all the files in the zip file to the same directory. You should then have all the necessary files. 221 | 222 | ### Known issues 223 | There are lots of problems with MATLAB's exporting functions, and unfortunately export_fig, which is simply a glorified wrapper for MATLAB's print function, doesn't solve all of them (yet?). Some of the problems I know about are: 224 | 225 | **Fonts** - when using the painters renderer, MATLAB can only export a small number of fonts, details of which can be found [here](http://www.mathworks.com/access/helpdesk/help/techdoc/creating_plots/f3-103191.html#f3-96545). Export_fig attempts to correct font names in the resulting EPS file (for upto a maximum of 11 different fonts in one figure), but this is not always guaranteed to work. In particular, the text positions will be affected. It also does not work for text blocks where the 'Interpreter' property is set to 'latex'. 226 | 227 | Also, when using the painters renderer, ghostscript will sometimes throw an error such as `Error: /undefined in /findfont`. This suggests that ghostscript could not find a definition file for one of your fonts. One possible fix for this is to make sure the file `EXPORT_FIG_PATH/.ignore/gs_font_path.txt` exists and contains a list of paths to the folder(s) containing the necessary font definitions (make sure they're TrueType definitions), separated by a semicolon. 228 | 229 | **RGB color data not yet supported in Painter's mode** - you will see this as a warning if you try to export a figure which contains patch objects whose face or vertex colors are specified as a an RGB colour, rather than an index into the colormap, using the painters renderer (the default renderer for vector output). This problem can arise if you use `pcolor`, for example. This is a problem with MATLAB's painters renderer, which also affects `print`; there is currently no fix available in export_fig (other than to export to bitmap). The suggested workaround is to avoid colouring patches using RGB. First, try to use colours in the figure's colourmap (instructions [here](http://www.mathworks.co.uk/support/solutions/en/data/1-6OTPQE/)) - change the colourmap, if necessary. If you are using `pcolor`, try using [uimagesc](http://www.mathworks.com/matlabcentral/fileexchange/11368) (on the file exchange) instead. 230 | 231 | **Dashed contour lines appear solid** - when using the painters renderer, MATLAB cannot generate dashed lines using the `contour` function (either on screen or in exported PDF and EPS files). Details can be found [here](http://www.mathworks.com/support/solutions/en/data/1-14PPHB/?solution=1-14PPHB). 232 | 233 | **Text size** - when using the OpenGL or ZBuffer renderers, large text can be resized relative to the figure when exporting at non-screen-resolution (including using anti-alising at screen resolution). This is a feature of MATLAB's `print `function. In this case, try using the `-painters` option. 234 | 235 | **Lighting and transparency** - when using the painters renderer, transparency and lighting effects are not supported. Sorry, but this is a feature of the renderer. To find out more about the capabilities of each rendering method, see [here](http://www.mathworks.com/access/helpdesk/help/techdoc/creating_plots/f3-84337.html#f3-102410). You can still export transparent objects to vector format (SVG) using the excellent [plot2svg](http://www.mathworks.com/matlabcentral/fileexchange/7401) package, then convert this to PDF, for example using [Inkscape](http://inkscape.org/). However, it can't handle lighting. 236 | 237 | **Lines in patch objects** - when exporting patch objects to PDF using the painters renderer (default), sometimes the output can appear to have lines across the middle of rectangular patches; these lines are the colour of the background, as if there is a crack in the patch, allowing you to see through. This issue is a feature of the software used to display the PDF, rather than the PDF itself. Sometimes disabling anti-aliasing in this software can get rid of the lines. 238 | 239 | **Out of memory** - if you run into memory issues when using export_fig, some ways to get round this are: 240 | 1. Reduce the level of anti-aliasing. 241 | 2. Reduce the size of the on screen figure. 242 | 3. Reduce the resolution (dpi) the figure is exported at. 243 | 244 | **Errors** - the other common type of errors people get with export_fig are OpenGL errors. This isn't a fault of export_fig, but either a bug in MATLAB's `print`, or your graphics driver getting itself into a state. Always make sure your graphics driver is up-to-date. If it still doesn't work, try using the ZBuffer renderer. 245 | 246 | ### Raising issues 247 | If you think you have found a genuine error or issue with export_fig **that is not listed above**, first ensure that the figure looks correct on screen when rendered using the renderer that export_fig is set to use (e.g. if exporting to PDF or EPS, does the figure look correct on screen using the painters renderer, or if exporting to bitmap, does the figure look correct on screen using the OpenGL renderer?). If it looks wrong then the problem is there, and I cannot help (other than to suggest you try exporting using a different renderer). 248 | 249 | Secondly, if exporting to bitmap, do try all the renderers (i.e. try the options `-opengl`, `-zbuffer` and `-painters` separately), to see if one of them does produce an acceptable output, and if so, use that. 250 | 251 | If the figure looks correct on screen, but an error exists in the exported output (which cannot be solved using a different renderer) then please feel free to raise an [issue](https://github.com/ojwoodford/export_fig/issues). Please be sure to include the .fig file, the export_fig command you use, the output you get, and a description of what you expected. I can't promise anything, but if it's easy to fix I probably will do it. Often I will find that the error is due to a bug in MATLAB's print function, in which case I will suggest you submit it as a bug to TheMathWorks, and inform me of any fix they suggest. Also, if there's a feature you'd like that isn't supported please tell me what it is and I'll consider implementing it. 252 | 253 | ### And finally... 254 | 255 | ![](https://farm4.staticflickr.com/3956/15591911455_b9008bd77e_o_d.jpg) 256 | 257 | If you've ever wondered what's going on in the icon on the export_fig download page (reproduced on the left), then this explanantion is for you. The icon is designed to demonstrate as many of export_fig's features as possible. Given a 258 | figure containing a translucent mesh (top right), export_fig can export to pdf (bottom centre), which allows the figure to be zoomed in without losing quality (because it's a vector graphic), but isn't able to reproduce the translucency, and also, depending on the viewer, creates small gaps between the patches, which are seen here as thin white lines. By contrast, when exporting to png (top left), translucency is preserved (see how the graphic below shows through), the figure is anti-aliased, but zooming in does not reveal more detail. 259 | 260 | -------------------------------------------------------------------------------- /funcs/export_fig/append_pdfs.m: -------------------------------------------------------------------------------- 1 | %APPEND_PDFS Appends/concatenates multiple PDF files 2 | % 3 | % Example: 4 | % append_pdfs(output, input1, input2, ...) 5 | % append_pdfs(output, input_list{:}) 6 | % append_pdfs test.pdf temp1.pdf temp2.pdf 7 | % 8 | % This function appends multiple PDF files to an existing PDF file, or 9 | % concatenates them into a PDF file if the output file doesn't yet exist. 10 | % 11 | % This function requires that you have ghostscript installed on your 12 | % system. Ghostscript can be downloaded from: http://www.ghostscript.com 13 | % 14 | % IN: 15 | % output - string of output file name (including the extension, .pdf). 16 | % If it exists it is appended to; if not, it is created. 17 | % input1 - string of an input file name (including the extension, .pdf). 18 | % All input files are appended in order. 19 | % input_list - cell array list of input file name strings. All input 20 | % files are appended in order. 21 | 22 | % Copyright: Oliver Woodford, 2011 23 | 24 | % Thanks to Reinhard Knoll for pointing out that appending multiple pdfs in 25 | % one go is much faster than appending them one at a time. 26 | 27 | % Thanks to Michael Teo for reporting the issue of a too long command line. 28 | % Issue resolved on 5/5/2011, by passing gs a command file. 29 | 30 | % Thanks to Martin Wittmann for pointing out the quality issue when 31 | % appending multiple bitmaps. 32 | % Issue resolved (to best of my ability) 1/6/2011, using the prepress 33 | % setting 34 | 35 | function append_pdfs(varargin) 36 | % Are we appending or creating a new file 37 | append = exist(varargin{1}, 'file') == 2; 38 | if append 39 | output = [tempname '.pdf']; 40 | else 41 | output = varargin{1}; 42 | varargin = varargin(2:end); 43 | end 44 | % Create the command file 45 | cmdfile = [tempname '.txt']; 46 | fh = fopen(cmdfile, 'w'); 47 | fprintf(fh, '-q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile="%s" -f', output); 48 | fprintf(fh, ' "%s"', varargin{:}); 49 | fclose(fh); 50 | % Call ghostscript 51 | ghostscript(['@"' cmdfile '"']); 52 | % Delete the command file 53 | delete(cmdfile); 54 | % Rename the file if needed 55 | if append 56 | movefile(output, varargin{1}); 57 | end -------------------------------------------------------------------------------- /funcs/export_fig/copyfig.m: -------------------------------------------------------------------------------- 1 | %COPYFIG Create a copy of a figure, without changing the figure 2 | % 3 | % Examples: 4 | % fh_new = copyfig(fh_old) 5 | % 6 | % This function will create a copy of a figure, but not change the figure, 7 | % as copyobj sometimes does, e.g. by changing legends. 8 | % 9 | % IN: 10 | % fh_old - The handle of the figure to be copied. Default: gcf. 11 | % 12 | % OUT: 13 | % fh_new - The handle of the created figure. 14 | 15 | % Copyright (C) Oliver Woodford 2012 16 | 17 | function fh = copyfig(fh) 18 | % Set the default 19 | if nargin == 0 20 | fh = gcf; 21 | end 22 | % Is there a legend? 23 | if isempty(findall(fh, 'Type', 'axes', 'Tag', 'legend')) 24 | % Safe to copy using copyobj 25 | fh = copyobj(fh, 0); 26 | else 27 | % copyobj will change the figure, so save and then load it instead 28 | tmp_nam = [tempname '.fig']; 29 | hgsave(fh, tmp_nam); 30 | fh = hgload(tmp_nam); 31 | delete(tmp_nam); 32 | end 33 | return -------------------------------------------------------------------------------- /funcs/export_fig/eps2pdf.m: -------------------------------------------------------------------------------- 1 | %EPS2PDF Convert an eps file to pdf format using ghostscript 2 | % 3 | % Examples: 4 | % eps2pdf source dest 5 | % eps2pdf(source, dest, crop) 6 | % eps2pdf(source, dest, crop, append) 7 | % eps2pdf(source, dest, crop, append, gray) 8 | % eps2pdf(source, dest, crop, append, gray, quality) 9 | % 10 | % This function converts an eps file to pdf format. The output can be 11 | % optionally cropped and also converted to grayscale. If the output pdf 12 | % file already exists then the eps file can optionally be appended as a new 13 | % page on the end of the eps file. The level of bitmap compression can also 14 | % optionally be set. 15 | % 16 | % This function requires that you have ghostscript installed on your 17 | % system. Ghostscript can be downloaded from: http://www.ghostscript.com 18 | % 19 | %IN: 20 | % source - filename of the source eps file to convert. The filename is 21 | % assumed to already have the extension ".eps". 22 | % dest - filename of the destination pdf file. The filename is assumed to 23 | % already have the extension ".pdf". 24 | % crop - boolean indicating whether to crop the borders off the pdf. 25 | % Default: true. 26 | % append - boolean indicating whether the eps should be appended to the 27 | % end of the pdf as a new page (if the pdf exists already). 28 | % Default: false. 29 | % gray - boolean indicating whether the output pdf should be grayscale or 30 | % not. Default: false. 31 | % quality - scalar indicating the level of image bitmap quality to 32 | % output. A larger value gives a higher quality. quality > 100 33 | % gives lossless output. Default: ghostscript prepress default. 34 | 35 | % Copyright (C) Oliver Woodford 2009-2011 36 | 37 | % Suggestion of appending pdf files provided by Matt C at: 38 | % http://www.mathworks.com/matlabcentral/fileexchange/23629 39 | 40 | % Thank you to Fabio Viola for pointing out compression artifacts, leading 41 | % to the quality setting. 42 | % Thank you to Scott for pointing out the subsampling of very small images, 43 | % which was fixed for lossless compression settings. 44 | 45 | % 9/12/2011 Pass font path to ghostscript. 46 | 47 | function eps2pdf(source, dest, crop, append, gray, quality) 48 | % Intialise the options string for ghostscript 49 | options = ['-q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile="' dest '"']; 50 | % Set crop option 51 | if nargin < 3 || crop 52 | options = [options ' -dEPSCrop']; 53 | end 54 | % Set the font path 55 | fp = font_path(); 56 | if ~isempty(fp) 57 | options = [options ' -sFONTPATH="' fp '"']; 58 | end 59 | % Set the grayscale option 60 | if nargin > 4 && gray 61 | options = [options ' -sColorConversionStrategy=Gray -dProcessColorModel=/DeviceGray']; 62 | end 63 | % Set the bitmap quality 64 | if nargin > 5 && ~isempty(quality) 65 | options = [options ' -dAutoFilterColorImages=false -dAutoFilterGrayImages=false']; 66 | if quality > 100 67 | options = [options ' -dColorImageFilter=/FlateEncode -dGrayImageFilter=/FlateEncode -c ".setpdfwrite << /ColorImageDownsampleThreshold 10 /GrayImageDownsampleThreshold 10 >> setdistillerparams"']; 68 | else 69 | options = [options ' -dColorImageFilter=/DCTEncode -dGrayImageFilter=/DCTEncode']; 70 | v = 1 + (quality < 80); 71 | quality = 1 - quality / 100; 72 | s = sprintf('<< /QFactor %.2f /Blend 1 /HSample [%d 1 1 %d] /VSample [%d 1 1 %d] >>', quality, v, v, v, v); 73 | options = sprintf('%s -c ".setpdfwrite << /ColorImageDict %s /GrayImageDict %s >> setdistillerparams"', options, s, s); 74 | end 75 | end 76 | % Check if the output file exists 77 | if nargin > 3 && append && exist(dest, 'file') == 2 78 | % File exists - append current figure to the end 79 | tmp_nam = tempname; 80 | % Copy the file 81 | copyfile(dest, tmp_nam); 82 | % Add the output file names 83 | options = [options ' -f "' tmp_nam '" "' source '"']; 84 | try 85 | % Convert to pdf using ghostscript 86 | [status, message] = ghostscript(options); 87 | catch me 88 | % Delete the intermediate file 89 | delete(tmp_nam); 90 | rethrow(me); 91 | end 92 | % Delete the intermediate file 93 | delete(tmp_nam); 94 | else 95 | % File doesn't exist or should be over-written 96 | % Add the output file names 97 | options = [options ' -f "' source '"']; 98 | % Convert to pdf using ghostscript 99 | [status, message] = ghostscript(options); 100 | end 101 | % Check for error 102 | if status 103 | % Report error 104 | if isempty(message) 105 | error('Unable to generate pdf. Check destination directory is writable.'); 106 | else 107 | error(message); 108 | end 109 | end 110 | return 111 | 112 | % Function to return (and create, where necessary) the font path 113 | function fp = font_path() 114 | fp = user_string('gs_font_path'); 115 | if ~isempty(fp) 116 | return 117 | end 118 | % Create the path 119 | % Start with the default path 120 | fp = getenv('GS_FONTPATH'); 121 | % Add on the typical directories for a given OS 122 | if ispc 123 | if ~isempty(fp) 124 | fp = [fp ';']; 125 | end 126 | fp = [fp getenv('WINDIR') filesep 'Fonts']; 127 | else 128 | if ~isempty(fp) 129 | fp = [fp ':']; 130 | end 131 | fp = [fp '/usr/share/fonts:/usr/local/share/fonts:/usr/share/fonts/X11:/usr/local/share/fonts/X11:/usr/share/fonts/truetype:/usr/local/share/fonts/truetype']; 132 | end 133 | user_string('gs_font_path', fp); 134 | return 135 | -------------------------------------------------------------------------------- /funcs/export_fig/fix_lines.m: -------------------------------------------------------------------------------- 1 | function fix_lines(fname, fname2) 2 | %FIX_LINES Improves the line style of eps files generated by print 3 | % 4 | % Examples: 5 | % fix_lines fname 6 | % fix_lines fname fname2 7 | % 8 | % This function improves the style of lines in eps files generated by 9 | % MATLAB's print function, making them more similar to those seen on 10 | % screen. Grid lines are also changed from a dashed style to a dotted 11 | % style, for greater differentiation from dashed lines. 12 | % 13 | % The function also places embedded fonts after the postscript header, in 14 | % versions of MATLAB which place the fonts first (R2006b and earlier), in 15 | % order to allow programs such as Ghostscript to find the bounding box 16 | % information. 17 | % 18 | % IN: 19 | % fname - Name or path of source eps file. 20 | % fname2 - Name or path of destination eps file. Default: same as fname. 21 | 22 | % Copyright: (C) Oliver Woodford, 2008-2010 23 | 24 | % The idea of editing the EPS file to change line styles comes from Jiro 25 | % Doke's FIXPSLINESTYLE (fex id: 17928) 26 | % The idea of changing dash length with line width came from comments on 27 | % fex id: 5743, but the implementation is mine :) 28 | 29 | % Thank you to Sylvain Favrot for bringing the embedded font/bounding box 30 | % interaction in older versions of MATLAB to my attention. 31 | % Thank you to D Ko for bringing an error with eps files with tiff previews 32 | % to my attention. 33 | % Thank you to Laurence K for suggesting the check to see if the file was 34 | % opened. 35 | 36 | % Read in the file 37 | fh = fopen(fname, 'r'); 38 | if fh == -1 39 | error('File %s not found.', fname); 40 | end 41 | try 42 | fstrm = fread(fh, '*char')'; 43 | catch ex 44 | fclose(fh); 45 | rethrow(ex); 46 | end 47 | fclose(fh); 48 | 49 | % Move any embedded fonts after the postscript header 50 | if strcmp(fstrm(1:15), '%!PS-AdobeFont-') 51 | % Find the start and end of the header 52 | ind = regexp(fstrm, '[\n\r]%!PS-Adobe-'); 53 | [ind2 ind2] = regexp(fstrm, '[\n\r]%%EndComments[\n\r]+'); 54 | % Put the header first 55 | if ~isempty(ind) && ~isempty(ind2) && ind(1) < ind2(1) 56 | fstrm = fstrm([ind(1)+1:ind2(1) 1:ind(1) ind2(1)+1:end]); 57 | end 58 | end 59 | 60 | % Make sure all line width commands come before the line style definitions, 61 | % so that dash lengths can be based on the correct widths 62 | % Find all line style sections 63 | ind = [regexp(fstrm, '[\n\r]SO[\n\r]'),... % This needs to be here even though it doesn't have dots/dashes! 64 | regexp(fstrm, '[\n\r]DO[\n\r]'),... 65 | regexp(fstrm, '[\n\r]DA[\n\r]'),... 66 | regexp(fstrm, '[\n\r]DD[\n\r]')]; 67 | ind = sort(ind); 68 | % Find line width commands 69 | [ind2 ind3] = regexp(fstrm, '[\n\r]\d* w[\n\r]'); 70 | % Go through each line style section and swap with any line width commands 71 | % near by 72 | b = 1; 73 | m = numel(ind); 74 | n = numel(ind2); 75 | for a = 1:m 76 | % Go forwards width commands until we pass the current line style 77 | while b <= n && ind2(b) < ind(a) 78 | b = b + 1; 79 | end 80 | if b > n 81 | % No more width commands 82 | break; 83 | end 84 | % Check we haven't gone past another line style (including SO!) 85 | if a < m && ind2(b) > ind(a+1) 86 | continue; 87 | end 88 | % Are the commands close enough to be confident we can swap them? 89 | if (ind2(b) - ind(a)) > 8 90 | continue; 91 | end 92 | % Move the line style command below the line width command 93 | fstrm(ind(a)+1:ind3(b)) = [fstrm(ind(a)+4:ind3(b)) fstrm(ind(a)+1:ind(a)+3)]; 94 | b = b + 1; 95 | end 96 | 97 | % Find any grid line definitions and change to GR format 98 | % Find the DO sections again as they may have moved 99 | ind = int32(regexp(fstrm, '[\n\r]DO[\n\r]')); 100 | if ~isempty(ind) 101 | % Find all occurrences of what are believed to be axes and grid lines 102 | ind2 = int32(regexp(fstrm, '[\n\r] *\d* *\d* *mt *\d* *\d* *L[\n\r]')); 103 | if ~isempty(ind2) 104 | % Now see which DO sections come just before axes and grid lines 105 | ind2 = repmat(ind2', [1 numel(ind)]) - repmat(ind, [numel(ind2) 1]); 106 | ind2 = any(ind2 > 0 & ind2 < 12); % 12 chars seems about right 107 | ind = ind(ind2); 108 | % Change any regions we believe to be grid lines to GR 109 | fstrm(ind+1) = 'G'; 110 | fstrm(ind+2) = 'R'; 111 | end 112 | end 113 | 114 | % Isolate line style definition section 115 | first_sec = strfind(fstrm, '% line types:'); 116 | [second_sec remaining] = strtok(fstrm(first_sec+1:end), '/'); 117 | [remaining remaining] = strtok(remaining, '%'); 118 | 119 | % Define the new styles, including the new GR format 120 | % Dot and dash lengths have two parts: a constant amount plus a line width 121 | % variable amount. The constant amount comes after dpi2point, and the 122 | % variable amount comes after currentlinewidth. If you want to change 123 | % dot/dash lengths for a one particular line style only, edit the numbers 124 | % in the /DO (dotted lines), /DA (dashed lines), /DD (dot dash lines) and 125 | % /GR (grid lines) lines for the style you want to change. 126 | new_style = {'/dom { dpi2point 1 currentlinewidth 0.08 mul add mul mul } bdef',... % Dot length macro based on line width 127 | '/dam { dpi2point 2 currentlinewidth 0.04 mul add mul mul } bdef',... % Dash length macro based on line width 128 | '/SO { [] 0 setdash 0 setlinecap } bdef',... % Solid lines 129 | '/DO { [1 dom 1.2 dom] 0 setdash 0 setlinecap } bdef',... % Dotted lines 130 | '/DA { [4 dam 1.5 dam] 0 setdash 0 setlinecap } bdef',... % Dashed lines 131 | '/DD { [1 dom 1.2 dom 4 dam 1.2 dom] 0 setdash 0 setlinecap } bdef',... % Dot dash lines 132 | '/GR { [0 dpi2point mul 4 dpi2point mul] 0 setdash 1 setlinecap } bdef'}; % Grid lines - dot spacing remains constant 133 | 134 | if nargin < 2 135 | % Overwrite the input file 136 | fname2 = fname; 137 | end 138 | 139 | % Save the file with the section replaced 140 | fh = fopen(fname2, 'w'); 141 | if fh == -1 142 | error('Unable to open %s for writing.', fname2); 143 | end 144 | try 145 | fwrite(fh, fstrm(1:first_sec), 'char*1'); 146 | fwrite(fh, second_sec, 'char*1'); 147 | fprintf(fh, '%s\r', new_style{:}); 148 | fwrite(fh, remaining, 'char*1'); 149 | catch ex 150 | fclose(fh); 151 | rethrow(ex); 152 | end 153 | fclose(fh); 154 | return -------------------------------------------------------------------------------- /funcs/export_fig/ghostscript.m: -------------------------------------------------------------------------------- 1 | %GHOSTSCRIPT Calls a local GhostScript executable with the input command 2 | % 3 | % Example: 4 | % [status result] = ghostscript(cmd) 5 | % 6 | % Attempts to locate a ghostscript executable, finally asking the user to 7 | % specify the directory ghostcript was installed into. The resulting path 8 | % is stored for future reference. 9 | % 10 | % Once found, the executable is called with the input command string. 11 | % 12 | % This function requires that you have Ghostscript installed on your 13 | % system. You can download this from: http://www.ghostscript.com 14 | % 15 | % IN: 16 | % cmd - Command string to be passed into ghostscript. 17 | % 18 | % OUT: 19 | % status - 0 iff command ran without problem. 20 | % result - Output from ghostscript. 21 | 22 | % Copyright: Oliver Woodford, 2009-2013 23 | 24 | % Thanks to Jonas Dorn for the fix for the title of the uigetdir window on 25 | % Mac OS. 26 | % Thanks to Nathan Childress for the fix to the default location on 64-bit 27 | % Windows systems. 28 | % 27/4/11 - Find 64-bit Ghostscript on Windows. Thanks to Paul Durack and 29 | % Shaun Kline for pointing out the issue 30 | % 4/5/11 - Thanks to David Chorlian for pointing out an alternative 31 | % location for gs on linux. 32 | % 12/12/12 - Add extra executable name on Windows. Thanks to Ratish 33 | % Punnoose for highlighting the issue. 34 | % 28/6/13 - Fix error using GS 9.07 in Linux. Many thanks to Jannick 35 | % Steinbring for proposing the fix. 36 | % 24/10/13 - Fix error using GS 9.07 in Linux. Many thanks to Johannes 37 | % for the fix. 38 | % 23/01/2014 - Add full path to ghostscript.txt in warning. Thanks to Koen 39 | % Vermeer for raising the issue. 40 | 41 | function varargout = ghostscript(cmd) 42 | % Initialize any required system calls before calling ghostscript 43 | shell_cmd = ''; 44 | if isunix 45 | shell_cmd = 'export LD_LIBRARY_PATH=""; '; % Avoids an error on Linux with GS 9.07 46 | end 47 | if ismac 48 | shell_cmd = 'export DYLD_LIBRARY_PATH=""; '; % Avoids an error on Mac with GS 9.07 49 | end 50 | % Call ghostscript 51 | [varargout{1:nargout}] = system(sprintf('%s"%s" %s', shell_cmd, gs_path, cmd)); 52 | return 53 | 54 | function path_ = gs_path 55 | % Return a valid path 56 | % Start with the currently set path 57 | path_ = user_string('ghostscript'); 58 | % Check the path works 59 | if check_gs_path(path_) 60 | return 61 | end 62 | % Check whether the binary is on the path 63 | if ispc 64 | bin = {'gswin32c.exe', 'gswin64c.exe', 'gs'}; 65 | else 66 | bin = {'gs'}; 67 | end 68 | for a = 1:numel(bin) 69 | path_ = bin{a}; 70 | if check_store_gs_path(path_) 71 | return 72 | end 73 | end 74 | % Search the obvious places 75 | if ispc 76 | default_location = 'C:\Program Files\gs\'; 77 | dir_list = dir(default_location); 78 | if isempty(dir_list) 79 | default_location = 'C:\Program Files (x86)\gs\'; % Possible location on 64-bit systems 80 | dir_list = dir(default_location); 81 | end 82 | executable = {'\bin\gswin32c.exe', '\bin\gswin64c.exe'}; 83 | ver_num = 0; 84 | % If there are multiple versions, use the newest 85 | for a = 1:numel(dir_list) 86 | ver_num2 = sscanf(dir_list(a).name, 'gs%g'); 87 | if ~isempty(ver_num2) && ver_num2 > ver_num 88 | for b = 1:numel(executable) 89 | path2 = [default_location dir_list(a).name executable{b}]; 90 | if exist(path2, 'file') == 2 91 | path_ = path2; 92 | ver_num = ver_num2; 93 | end 94 | end 95 | end 96 | end 97 | if check_store_gs_path(path_) 98 | return 99 | end 100 | else 101 | executable = {'/usr/bin/gs', '/usr/local/bin/gs'}; 102 | for a = 1:numel(executable) 103 | path_ = executable{a}; 104 | if check_store_gs_path(path_) 105 | return 106 | end 107 | end 108 | end 109 | % Ask the user to enter the path 110 | while 1 111 | if strncmp(computer, 'MAC', 3) % Is a Mac 112 | % Give separate warning as the uigetdir dialogue box doesn't have a 113 | % title 114 | uiwait(warndlg('Ghostscript not found. Please locate the program.')) 115 | end 116 | base = uigetdir('/', 'Ghostcript not found. Please locate the program.'); 117 | if isequal(base, 0) 118 | % User hit cancel or closed window 119 | break; 120 | end 121 | base = [base filesep]; 122 | bin_dir = {'', ['bin' filesep], ['lib' filesep]}; 123 | for a = 1:numel(bin_dir) 124 | for b = 1:numel(bin) 125 | path_ = [base bin_dir{a} bin{b}]; 126 | if exist(path_, 'file') == 2 127 | if check_store_gs_path(path_) 128 | return 129 | end 130 | end 131 | end 132 | end 133 | end 134 | error('Ghostscript not found. Have you installed it from www.ghostscript.com?'); 135 | 136 | function good = check_store_gs_path(path_) 137 | % Check the path is valid 138 | good = check_gs_path(path_); 139 | if ~good 140 | return 141 | end 142 | % Update the current default path to the path found 143 | if ~user_string('ghostscript', path_) 144 | warning('Path to ghostscript installation could not be saved. Enter it manually in %s.', fullfile(fileparts(which('user_string.m')), '.ignore', 'ghostscript.txt')); 145 | return 146 | end 147 | return 148 | 149 | function good = check_gs_path(path_) 150 | % Check the path is valid 151 | shell_cmd = ''; 152 | if ismac 153 | shell_cmd = 'export DYLD_LIBRARY_PATH=""; '; % Avoids an error on Mac with GS 9.07 154 | end 155 | [good, message] = system(sprintf('%s"%s" -h', shell_cmd, path_)); 156 | good = good == 0; 157 | return -------------------------------------------------------------------------------- /funcs/export_fig/im2gif.m: -------------------------------------------------------------------------------- 1 | %IM2GIF Convert a multiframe image to an animated GIF file 2 | % 3 | % Examples: 4 | % im2gif infile 5 | % im2gif infile outfile 6 | % im2gif(A, outfile) 7 | % im2gif(..., '-nocrop') 8 | % im2gif(..., '-nodither') 9 | % im2gif(..., '-ncolors', n) 10 | % im2gif(..., '-loops', n) 11 | % im2gif(..., '-delay', n) 12 | % 13 | % This function converts a multiframe image to an animated GIF. 14 | % 15 | % To create an animation from a series of figures, export to a multiframe 16 | % TIFF file using export_fig, then convert to a GIF, as follows: 17 | % 18 | % for a = 2 .^ (3:6) 19 | % peaks(a); 20 | % export_fig test.tif -nocrop -append 21 | % end 22 | % im2gif('test.tif', '-delay', 0.5); 23 | % 24 | %IN: 25 | % infile - string containing the name of the input image. 26 | % outfile - string containing the name of the output image (must have the 27 | % .gif extension). Default: infile, with .gif extension. 28 | % A - HxWxCxN array of input images, stacked along fourth dimension, to 29 | % be converted to gif. 30 | % -nocrop - option indicating that the borders of the output are not to 31 | % be cropped. 32 | % -nodither - option indicating that dithering is not to be used when 33 | % converting the image. 34 | % -ncolors - option pair, the value of which indicates the maximum number 35 | % of colors the GIF can have. This can also be a quantization 36 | % tolerance, between 0 and 1. Default/maximum: 256. 37 | % -loops - option pair, the value of which gives the number of times the 38 | % animation is to be looped. Default: 65535. 39 | % -delay - option pair, the value of which gives the time, in seconds, 40 | % between frames. Default: 1/15. 41 | 42 | % Copyright (C) Oliver Woodford 2011 43 | 44 | function im2gif(A, varargin) 45 | 46 | % Parse the input arguments 47 | [A, options] = parse_args(A, varargin{:}); 48 | 49 | if options.crop ~= 0 50 | % Crop 51 | A = crop_borders(A); 52 | end 53 | 54 | % Convert to indexed image 55 | [h, w, c, n] = size(A); 56 | A = reshape(permute(A, [1 2 4 3]), h, w*n, c); 57 | map = unique(reshape(A, h*w*n, c), 'rows'); 58 | if size(map, 1) > 256 59 | dither_str = {'dither', 'nodither'}; 60 | dither_str = dither_str{1+(options.dither==0)}; 61 | if options.ncolors <= 1 62 | [B, map] = rgb2ind(A, options.ncolors, dither_str); 63 | if size(map, 1) > 256 64 | [B, map] = rgb2ind(A, 256, dither_str); 65 | end 66 | else 67 | [B, map] = rgb2ind(A, min(round(options.ncolors), 256), dither_str); 68 | end 69 | else 70 | if max(map(:)) > 1 71 | map = double(map) / 255; 72 | A = double(A) / 255; 73 | end 74 | B = rgb2ind(im2double(A), map); 75 | end 76 | B = reshape(B, h, w, 1, n); 77 | 78 | % Bug fix to rgb2ind 79 | map(B(1)+1,:) = im2double(A(1,1,:)); 80 | 81 | % Save as a gif 82 | imwrite(B, map, options.outfile, 'LoopCount', round(options.loops(1)), 'DelayTime', options.delay); 83 | return 84 | 85 | %% Parse the input arguments 86 | function [A, options] = parse_args(A, varargin) 87 | % Set the defaults 88 | options = struct('outfile', '', ... 89 | 'dither', true, ... 90 | 'crop', true, ... 91 | 'ncolors', 256, ... 92 | 'loops', 65535, ... 93 | 'delay', 1/15); 94 | 95 | % Go through the arguments 96 | a = 0; 97 | n = numel(varargin); 98 | while a < n 99 | a = a + 1; 100 | if ischar(varargin{a}) && ~isempty(varargin{a}) 101 | if varargin{a}(1) == '-' 102 | opt = lower(varargin{a}(2:end)); 103 | switch opt 104 | case 'nocrop' 105 | options.crop = false; 106 | case 'nodither' 107 | options.dither = false; 108 | otherwise 109 | if ~isfield(options, opt) 110 | error('Option %s not recognized', varargin{a}); 111 | end 112 | a = a + 1; 113 | if ischar(varargin{a}) && ~ischar(options.(opt)) 114 | options.(opt) = str2double(varargin{a}); 115 | else 116 | options.(opt) = varargin{a}; 117 | end 118 | end 119 | else 120 | options.outfile = varargin{a}; 121 | end 122 | end 123 | end 124 | 125 | if isempty(options.outfile) 126 | if ~ischar(A) 127 | error('No output filename given.'); 128 | end 129 | % Generate the output filename from the input filename 130 | [path, outfile] = fileparts(A); 131 | options.outfile = fullfile(path, [outfile '.gif']); 132 | end 133 | 134 | if ischar(A) 135 | % Read in the image 136 | A = imread_rgb(A); 137 | end 138 | return 139 | 140 | %% Read image to uint8 rgb array 141 | function [A, alpha] = imread_rgb(name) 142 | % Get file info 143 | info = imfinfo(name); 144 | % Special case formats 145 | switch lower(info(1).Format) 146 | case 'gif' 147 | [A, map] = imread(name, 'frames', 'all'); 148 | if ~isempty(map) 149 | map = uint8(map * 256 - 0.5); % Convert to uint8 for storage 150 | A = reshape(map(uint32(A)+1,:), [size(A) size(map, 2)]); % Assume indexed from 0 151 | A = permute(A, [1 2 5 4 3]); 152 | end 153 | case {'tif', 'tiff'} 154 | A = cell(numel(info), 1); 155 | for a = 1:numel(A) 156 | [A{a}, map] = imread(name, 'Index', a, 'Info', info); 157 | if ~isempty(map) 158 | map = uint8(map * 256 - 0.5); % Convert to uint8 for storage 159 | A{a} = reshape(map(uint32(A{a})+1,:), [size(A) size(map, 2)]); % Assume indexed from 0 160 | end 161 | if size(A{a}, 3) == 4 162 | % TIFF in CMYK colourspace - convert to RGB 163 | if isfloat(A{a}) 164 | A{a} = A{a} * 255; 165 | else 166 | A{a} = single(A{a}); 167 | end 168 | A{a} = 255 - A{a}; 169 | A{a}(:,:,4) = A{a}(:,:,4) / 255; 170 | A{a} = uint8(A(:,:,1:3) .* A{a}(:,:,[4 4 4])); 171 | end 172 | end 173 | A = cat(4, A{:}); 174 | otherwise 175 | [A, map, alpha] = imread(name); 176 | A = A(:,:,:,1); % Keep only first frame of multi-frame files 177 | if ~isempty(map) 178 | map = uint8(map * 256 - 0.5); % Convert to uint8 for storage 179 | A = reshape(map(uint32(A)+1,:), [size(A) size(map, 2)]); % Assume indexed from 0 180 | elseif size(A, 3) == 4 181 | % Assume 4th channel is an alpha matte 182 | alpha = A(:,:,4); 183 | A = A(:,:,1:3); 184 | end 185 | end 186 | return 187 | 188 | %% Crop the borders 189 | function A = crop_borders(A) 190 | [h, w, c, n] = size(A); 191 | bcol = A(ceil(end/2),1,:,1); 192 | bail = false; 193 | for l = 1:w 194 | for a = 1:c 195 | if ~all(col(A(:,l,a,:)) == bcol(a)) 196 | bail = true; 197 | break; 198 | end 199 | end 200 | if bail 201 | break; 202 | end 203 | end 204 | bcol = A(ceil(end/2),w,:,1); 205 | bail = false; 206 | for r = w:-1:l 207 | for a = 1:c 208 | if ~all(col(A(:,r,a,:)) == bcol(a)) 209 | bail = true; 210 | break; 211 | end 212 | end 213 | if bail 214 | break; 215 | end 216 | end 217 | bcol = A(1,ceil(end/2),:,1); 218 | bail = false; 219 | for t = 1:h 220 | for a = 1:c 221 | if ~all(col(A(t,:,a,:)) == bcol(a)) 222 | bail = true; 223 | break; 224 | end 225 | end 226 | if bail 227 | break; 228 | end 229 | end 230 | bcol = A(h,ceil(end/2),:,1); 231 | bail = false; 232 | for b = h:-1:t 233 | for a = 1:c 234 | if ~all(col(A(b,:,a,:)) == bcol(a)) 235 | bail = true; 236 | break; 237 | end 238 | end 239 | if bail 240 | break; 241 | end 242 | end 243 | A = A(t:b,l:r,:,:); 244 | return 245 | 246 | function A = col(A) 247 | A = A(:); 248 | return 249 | -------------------------------------------------------------------------------- /funcs/export_fig/isolate_axes.m: -------------------------------------------------------------------------------- 1 | %ISOLATE_AXES Isolate the specified axes in a figure on their own 2 | % 3 | % Examples: 4 | % fh = isolate_axes(ah) 5 | % fh = isolate_axes(ah, vis) 6 | % 7 | % This function will create a new figure containing the axes/uipanels 8 | % specified, and also their associated legends and colorbars. The objects 9 | % specified must all be in the same figure, but they will generally only be 10 | % a subset of the objects in the figure. 11 | % 12 | % IN: 13 | % ah - An array of axes and uipanel handles, which must come from the 14 | % same figure. 15 | % vis - A boolean indicating whether the new figure should be visible. 16 | % Default: false. 17 | % 18 | % OUT: 19 | % fh - The handle of the created figure. 20 | 21 | % Copyright (C) Oliver Woodford 2011-2013 22 | 23 | % Thank you to Rosella Blatt for reporting a bug to do with axes in GUIs 24 | % 16/3/2012 Moved copyfig to its own function. Thanks to Bob Fratantonio 25 | % for pointing out that the function is also used in export_fig.m. 26 | % 12/12/12 - Add support for isolating uipanels. Thanks to michael for 27 | % suggesting it. 28 | % 08/10/13 - Bug fix to allchildren suggested by Will Grant (many thanks!). 29 | % 05/12/13 - Bug fix to axes having different units. Thanks to Remington 30 | % Reid for reporting the issue. 31 | 32 | function fh = isolate_axes(ah, vis) 33 | % Make sure we have an array of handles 34 | if ~all(ishandle(ah)) 35 | error('ah must be an array of handles'); 36 | end 37 | % Check that the handles are all for axes or uipanels, and are all in the same figure 38 | fh = ancestor(ah(1), 'figure'); 39 | nAx = numel(ah); 40 | for a = 1:nAx 41 | if ~ismember(get(ah(a), 'Type'), {'axes', 'uipanel'}) 42 | error('All handles must be axes or uipanel handles.'); 43 | end 44 | if ~isequal(ancestor(ah(a), 'figure'), fh) 45 | error('Axes must all come from the same figure.'); 46 | end 47 | end 48 | % Tag the objects so we can find them in the copy 49 | old_tag = get(ah, 'Tag'); 50 | if nAx == 1 51 | old_tag = {old_tag}; 52 | end 53 | set(ah, 'Tag', 'ObjectToCopy'); 54 | % Create a new figure exactly the same as the old one 55 | fh = copyfig(fh); %copyobj(fh, 0); 56 | if nargin < 2 || ~vis 57 | set(fh, 'Visible', 'off'); 58 | end 59 | % Reset the object tags 60 | for a = 1:nAx 61 | set(ah(a), 'Tag', old_tag{a}); 62 | end 63 | % Find the objects to save 64 | ah = findall(fh, 'Tag', 'ObjectToCopy'); 65 | if numel(ah) ~= nAx 66 | close(fh); 67 | error('Incorrect number of objects found.'); 68 | end 69 | % Set the axes tags to what they should be 70 | for a = 1:nAx 71 | set(ah(a), 'Tag', old_tag{a}); 72 | end 73 | % Keep any legends and colorbars which overlap the subplots 74 | lh = findall(fh, 'Type', 'axes', '-and', {'Tag', 'legend', '-or', 'Tag', 'Colorbar'}); 75 | nLeg = numel(lh); 76 | if nLeg > 0 77 | set([ah(:); lh(:)], 'Units', 'normalized'); 78 | ax_pos = get(ah, 'OuterPosition'); 79 | if nAx > 1 80 | ax_pos = cell2mat(ax_pos(:)); 81 | end 82 | ax_pos(:,3:4) = ax_pos(:,3:4) + ax_pos(:,1:2); 83 | leg_pos = get(lh, 'OuterPosition'); 84 | if nLeg > 1; 85 | leg_pos = cell2mat(leg_pos); 86 | end 87 | leg_pos(:,3:4) = leg_pos(:,3:4) + leg_pos(:,1:2); 88 | ax_pos = shiftdim(ax_pos, -1); 89 | % Overlap test 90 | M = bsxfun(@lt, leg_pos(:,1), ax_pos(:,:,3)) & ... 91 | bsxfun(@lt, leg_pos(:,2), ax_pos(:,:,4)) & ... 92 | bsxfun(@gt, leg_pos(:,3), ax_pos(:,:,1)) & ... 93 | bsxfun(@gt, leg_pos(:,4), ax_pos(:,:,2)); 94 | ah = [ah; lh(any(M, 2))]; 95 | end 96 | % Get all the objects in the figure 97 | axs = findall(fh); 98 | % Delete everything except for the input objects and associated items 99 | delete(axs(~ismember(axs, [ah; allchildren(ah); allancestors(ah)]))); 100 | return 101 | 102 | function ah = allchildren(ah) 103 | ah = findall(ah); 104 | if iscell(ah) 105 | ah = cell2mat(ah); 106 | end 107 | ah = ah(:); 108 | return 109 | 110 | function ph = allancestors(ah) 111 | ph = []; 112 | for a = 1:numel(ah) 113 | h = get(ah(a), 'parent'); 114 | while h ~= 0 115 | ph = [ph; h]; 116 | h = get(h, 'parent'); 117 | end 118 | end 119 | return -------------------------------------------------------------------------------- /funcs/export_fig/pdf2eps.m: -------------------------------------------------------------------------------- 1 | %PDF2EPS Convert a pdf file to eps format using pdftops 2 | % 3 | % Examples: 4 | % pdf2eps source dest 5 | % 6 | % This function converts a pdf file to eps format. 7 | % 8 | % This function requires that you have pdftops, from the Xpdf suite of 9 | % functions, installed on your system. This can be downloaded from: 10 | % http://www.foolabs.com/xpdf 11 | % 12 | %IN: 13 | % source - filename of the source pdf file to convert. The filename is 14 | % assumed to already have the extension ".pdf". 15 | % dest - filename of the destination eps file. The filename is assumed to 16 | % already have the extension ".eps". 17 | 18 | % Copyright (C) Oliver Woodford 2009-2010 19 | 20 | % Thanks to Aldebaro Klautau for reporting a bug when saving to 21 | % non-existant directories. 22 | 23 | function pdf2eps(source, dest) 24 | % Construct the options string for pdftops 25 | options = ['-q -paper match -eps -level2 "' source '" "' dest '"']; 26 | % Convert to eps using pdftops 27 | [status, message] = pdftops(options); 28 | % Check for error 29 | if status 30 | % Report error 31 | if isempty(message) 32 | error('Unable to generate eps. Check destination directory is writable.'); 33 | else 34 | error(message); 35 | end 36 | end 37 | % Fix the DSC error created by pdftops 38 | fid = fopen(dest, 'r+'); 39 | if fid == -1 40 | % Cannot open the file 41 | return 42 | end 43 | fgetl(fid); % Get the first line 44 | str = fgetl(fid); % Get the second line 45 | if strcmp(str(1:min(13, end)), '% Produced by') 46 | fseek(fid, -numel(str)-1, 'cof'); 47 | fwrite(fid, '%'); % Turn ' ' into '%' 48 | end 49 | fclose(fid); 50 | return 51 | 52 | -------------------------------------------------------------------------------- /funcs/export_fig/pdftops.m: -------------------------------------------------------------------------------- 1 | function varargout = pdftops(cmd) 2 | %PDFTOPS Calls a local pdftops executable with the input command 3 | % 4 | % Example: 5 | % [status result] = pdftops(cmd) 6 | % 7 | % Attempts to locate a pdftops executable, finally asking the user to 8 | % specify the directory pdftops was installed into. The resulting path is 9 | % stored for future reference. 10 | % 11 | % Once found, the executable is called with the input command string. 12 | % 13 | % This function requires that you have pdftops (from the Xpdf package) 14 | % installed on your system. You can download this from: 15 | % http://www.foolabs.com/xpdf 16 | % 17 | % IN: 18 | % cmd - Command string to be passed into pdftops. 19 | % 20 | % OUT: 21 | % status - 0 iff command ran without problem. 22 | % result - Output from pdftops. 23 | 24 | % Copyright: Oliver Woodford, 2009-2010 25 | 26 | % Thanks to Jonas Dorn for the fix for the title of the uigetdir window on 27 | % Mac OS. 28 | % Thanks to Christoph Hertel for pointing out a bug in check_xpdf_path 29 | % under linux. 30 | % 23/01/2014 - Add full path to pdftops.txt in warning. 31 | 32 | % Call pdftops 33 | [varargout{1:nargout}] = system(sprintf('"%s" %s', xpdf_path, cmd)); 34 | return 35 | 36 | function path_ = xpdf_path 37 | % Return a valid path 38 | % Start with the currently set path 39 | path_ = user_string('pdftops'); 40 | % Check the path works 41 | if check_xpdf_path(path_) 42 | return 43 | end 44 | % Check whether the binary is on the path 45 | if ispc 46 | bin = 'pdftops.exe'; 47 | else 48 | bin = 'pdftops'; 49 | end 50 | if check_store_xpdf_path(bin) 51 | path_ = bin; 52 | return 53 | end 54 | % Search the obvious places 55 | if ispc 56 | path_ = 'C:\Program Files\xpdf\pdftops.exe'; 57 | else 58 | path_ = '/usr/local/bin/pdftops'; 59 | end 60 | if check_store_xpdf_path(path_) 61 | return 62 | end 63 | % Ask the user to enter the path 64 | while 1 65 | if strncmp(computer,'MAC',3) % Is a Mac 66 | % Give separate warning as the uigetdir dialogue box doesn't have a 67 | % title 68 | uiwait(warndlg('Pdftops not found. Please locate the program, or install xpdf-tools from http://users.phg-online.de/tk/MOSXS/.')) 69 | end 70 | base = uigetdir('/', 'Pdftops not found. Please locate the program.'); 71 | if isequal(base, 0) 72 | % User hit cancel or closed window 73 | break; 74 | end 75 | base = [base filesep]; 76 | bin_dir = {'', ['bin' filesep], ['lib' filesep]}; 77 | for a = 1:numel(bin_dir) 78 | path_ = [base bin_dir{a} bin]; 79 | if exist(path_, 'file') == 2 80 | break; 81 | end 82 | end 83 | if check_store_xpdf_path(path_) 84 | return 85 | end 86 | end 87 | error('pdftops executable not found.'); 88 | 89 | function good = check_store_xpdf_path(path_) 90 | % Check the path is valid 91 | good = check_xpdf_path(path_); 92 | if ~good 93 | return 94 | end 95 | % Update the current default path to the path found 96 | if ~user_string('pdftops', path_) 97 | warning('Path to pdftops executable could not be saved. Enter it manually in %s.', fullfile(fileparts(which('user_string.m')), '.ignore', 'pdftops.txt')); 98 | return 99 | end 100 | return 101 | 102 | function good = check_xpdf_path(path_) 103 | % Check the path is valid 104 | [good, message] = system(sprintf('"%s" -h', path_)); 105 | % system returns good = 1 even when the command runs 106 | % Look for something distinct in the help text 107 | good = ~isempty(strfind(message, 'PostScript')); 108 | return -------------------------------------------------------------------------------- /funcs/export_fig/print2array.m: -------------------------------------------------------------------------------- 1 | %PRINT2ARRAY Exports a figure to an image array 2 | % 3 | % Examples: 4 | % A = print2array 5 | % A = print2array(figure_handle) 6 | % A = print2array(figure_handle, resolution) 7 | % A = print2array(figure_handle, resolution, renderer) 8 | % [A bcol] = print2array(...) 9 | % 10 | % This function outputs a bitmap image of the given figure, at the desired 11 | % resolution. 12 | % 13 | % If renderer is '-painters' then ghostcript needs to be installed. This 14 | % can be downloaded from: http://www.ghostscript.com 15 | % 16 | % IN: 17 | % figure_handle - The handle of the figure to be exported. Default: gcf. 18 | % resolution - Resolution of the output, as a factor of screen 19 | % resolution. Default: 1. 20 | % renderer - string containing the renderer paramater to be passed to 21 | % print. Default: '-opengl'. 22 | % 23 | % OUT: 24 | % A - MxNx3 uint8 image of the figure. 25 | % bcol - 1x3 uint8 vector of the background color 26 | 27 | % Copyright (C) Oliver Woodford 2008-2012 28 | 29 | % 05/09/11: Set EraseModes to normal when using opengl or zbuffer 30 | % renderers. Thanks to Pawel Kocieniewski for reporting the 31 | % issue. 32 | % 21/09/11: Bug fix: unit8 -> uint8! Thanks to Tobias Lamour for reporting 33 | % the issue. 34 | % 14/11/11: Bug fix: stop using hardcopy(), as it interfered with figure 35 | % size and erasemode settings. Makes it a bit slower, but more 36 | % reliable. Thanks to Phil Trinh and Meelis Lootus for reporting 37 | % the issues. 38 | % 09/12/11: Pass font path to ghostscript. 39 | % 27/01/12: Bug fix affecting painters rendering tall figures. Thanks to 40 | % Ken Campbell for reporting it. 41 | % 03/04/12: Bug fix to median input. Thanks to Andy Matthews for reporting 42 | % it. 43 | % 26/10/12: Set PaperOrientation to portrait. Thanks to Michael Watts for 44 | % reporting the issue. 45 | 46 | function [A, bcol] = print2array(fig, res, renderer) 47 | % Generate default input arguments, if needed 48 | if nargin < 2 49 | res = 1; 50 | if nargin < 1 51 | fig = gcf; 52 | end 53 | end 54 | % Warn if output is large 55 | old_mode = get(fig, 'Units'); 56 | set(fig, 'Units', 'pixels'); 57 | px = get(fig, 'Position'); 58 | set(fig, 'Units', old_mode); 59 | npx = prod(px(3:4)*res)/1e6; 60 | if npx > 30 61 | % 30M pixels or larger! 62 | warning('MATLAB:LargeImage', 'print2array generating a %.1fM pixel image. This could be slow and might also cause memory problems.', npx); 63 | end 64 | % Retrieve the background colour 65 | bcol = get(fig, 'Color'); 66 | % Set the resolution parameter 67 | res_str = ['-r' num2str(ceil(get(0, 'ScreenPixelsPerInch')*res))]; 68 | % Generate temporary file name 69 | tmp_nam = [tempname '.tif']; 70 | if nargin > 2 && strcmp(renderer, '-painters') 71 | % Print to eps file 72 | tmp_eps = [tempname '.eps']; 73 | print2eps(tmp_eps, fig, renderer, '-loose'); 74 | try 75 | % Initialize the command to export to tiff using ghostscript 76 | cmd_str = ['-dEPSCrop -q -dNOPAUSE -dBATCH ' res_str ' -sDEVICE=tiff24nc']; 77 | % Set the font path 78 | fp = font_path(); 79 | if ~isempty(fp) 80 | cmd_str = [cmd_str ' -sFONTPATH="' fp '"']; 81 | end 82 | % Add the filenames 83 | cmd_str = [cmd_str ' -sOutputFile="' tmp_nam '" "' tmp_eps '"']; 84 | % Execute the ghostscript command 85 | ghostscript(cmd_str); 86 | catch me 87 | % Delete the intermediate file 88 | delete(tmp_eps); 89 | rethrow(me); 90 | end 91 | % Delete the intermediate file 92 | delete(tmp_eps); 93 | % Read in the generated bitmap 94 | A = imread(tmp_nam); 95 | % Delete the temporary bitmap file 96 | delete(tmp_nam); 97 | % Set border pixels to the correct colour 98 | if isequal(bcol, 'none') 99 | bcol = []; 100 | elseif isequal(bcol, [1 1 1]) 101 | bcol = uint8([255 255 255]); 102 | else 103 | for l = 1:size(A, 2) 104 | if ~all(reshape(A(:,l,:) == 255, [], 1)) 105 | break; 106 | end 107 | end 108 | for r = size(A, 2):-1:l 109 | if ~all(reshape(A(:,r,:) == 255, [], 1)) 110 | break; 111 | end 112 | end 113 | for t = 1:size(A, 1) 114 | if ~all(reshape(A(t,:,:) == 255, [], 1)) 115 | break; 116 | end 117 | end 118 | for b = size(A, 1):-1:t 119 | if ~all(reshape(A(b,:,:) == 255, [], 1)) 120 | break; 121 | end 122 | end 123 | bcol = uint8(median(single([reshape(A(:,[l r],:), [], size(A, 3)); reshape(A([t b],:,:), [], size(A, 3))]), 1)); 124 | for c = 1:size(A, 3) 125 | A(:,[1:l-1, r+1:end],c) = bcol(c); 126 | A([1:t-1, b+1:end],:,c) = bcol(c); 127 | end 128 | end 129 | else 130 | if nargin < 3 131 | renderer = '-opengl'; 132 | end 133 | err = false; 134 | % Set paper size 135 | old_pos_mode = get(fig, 'PaperPositionMode'); 136 | old_orientation = get(fig, 'PaperOrientation'); 137 | set(fig, 'PaperPositionMode', 'auto', 'PaperOrientation', 'portrait'); 138 | try 139 | % Print to tiff file 140 | print(fig, renderer, res_str, '-dtiff', tmp_nam); 141 | % Read in the printed file 142 | A = imread(tmp_nam); 143 | % Delete the temporary file 144 | delete(tmp_nam); 145 | catch ex 146 | err = true; 147 | end 148 | % Reset paper size 149 | set(fig, 'PaperPositionMode', old_pos_mode, 'PaperOrientation', old_orientation); 150 | % Throw any error that occurred 151 | if err 152 | rethrow(ex); 153 | end 154 | % Set the background color 155 | if isequal(bcol, 'none') 156 | bcol = []; 157 | else 158 | bcol = bcol * 255; 159 | if isequal(bcol, round(bcol)) 160 | bcol = uint8(bcol); 161 | else 162 | bcol = squeeze(A(1,1,:)); 163 | end 164 | end 165 | end 166 | % Check the output size is correct 167 | if isequal(res, round(res)) 168 | px = [px([4 3])*res 3]; 169 | if ~isequal(size(A), px) 170 | % Correct the output size 171 | A = A(1:min(end,px(1)),1:min(end,px(2)),:); 172 | end 173 | end 174 | return 175 | 176 | % Function to return (and create, where necessary) the font path 177 | function fp = font_path() 178 | fp = user_string('gs_font_path'); 179 | if ~isempty(fp) 180 | return 181 | end 182 | % Create the path 183 | % Start with the default path 184 | fp = getenv('GS_FONTPATH'); 185 | % Add on the typical directories for a given OS 186 | if ispc 187 | if ~isempty(fp) 188 | fp = [fp ';']; 189 | end 190 | fp = [fp getenv('WINDIR') filesep 'Fonts']; 191 | else 192 | if ~isempty(fp) 193 | fp = [fp ':']; 194 | end 195 | fp = [fp '/usr/share/fonts:/usr/local/share/fonts:/usr/share/fonts/X11:/usr/local/share/fonts/X11:/usr/share/fonts/truetype:/usr/local/share/fonts/truetype']; 196 | end 197 | user_string('gs_font_path', fp); 198 | return 199 | -------------------------------------------------------------------------------- /funcs/export_fig/print2eps.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/funcs/export_fig/print2eps.m -------------------------------------------------------------------------------- /funcs/export_fig/user_string.m: -------------------------------------------------------------------------------- 1 | %USER_STRING Get/set a user specific string 2 | % 3 | % Examples: 4 | % string = user_string(string_name) 5 | % saved = user_string(string_name, new_string) 6 | % 7 | % Function to get and set a string in a system or user specific file. This 8 | % enables, for example, system specific paths to binaries to be saved. 9 | % 10 | % IN: 11 | % string_name - String containing the name of the string required. The 12 | % string is extracted from a file called (string_name).txt, 13 | % stored in the same directory as user_string.m. 14 | % new_string - The new string to be saved under the name given by 15 | % string_name. 16 | % 17 | % OUT: 18 | % string - The currently saved string. Default: ''. 19 | % saved - Boolean indicating whether the save was succesful 20 | 21 | % Copyright (C) Oliver Woodford 2011-2013 22 | 23 | % This method of saving paths avoids changing .m files which might be in a 24 | % version control system. Instead it saves the user dependent paths in 25 | % separate files with a .txt extension, which need not be checked in to 26 | % the version control system. Thank you to Jonas Dorn for suggesting this 27 | % approach. 28 | 29 | % 10/01/2013 - Access files in text, not binary mode, as latter can cause 30 | % errors. Thanks to Christian for pointing this out. 31 | 32 | function string = user_string(string_name, string) 33 | if ~ischar(string_name) 34 | error('string_name must be a string.'); 35 | end 36 | % Create the full filename 37 | string_name = fullfile(fileparts(mfilename('fullpath')), '.ignore', [string_name '.txt']); 38 | if nargin > 1 39 | % Set string 40 | if ~ischar(string) 41 | error('new_string must be a string.'); 42 | end 43 | % Make sure the save directory exists 44 | dname = fileparts(string_name); 45 | if ~exist(dname, 'dir') 46 | % Create the directory 47 | try 48 | if ~mkdir(dname) 49 | string = false; 50 | return 51 | end 52 | catch 53 | string = false; 54 | return 55 | end 56 | % Make it hidden 57 | try 58 | fileattrib(dname, '+h'); 59 | catch 60 | end 61 | end 62 | % Write the file 63 | fid = fopen(string_name, 'wt'); 64 | if fid == -1 65 | string = false; 66 | return 67 | end 68 | try 69 | fprintf(fid, '%s', string); 70 | catch 71 | fclose(fid); 72 | string = false; 73 | return 74 | end 75 | fclose(fid); 76 | string = true; 77 | else 78 | % Get string 79 | fid = fopen(string_name, 'rt'); 80 | if fid == -1 81 | string = ''; 82 | return 83 | end 84 | string = fgetl(fid); 85 | fclose(fid); 86 | end 87 | return -------------------------------------------------------------------------------- /funcs/export_fig/using_hg2.m: -------------------------------------------------------------------------------- 1 | %USING_HG2 Determine if the HG2 graphics pipeline is used 2 | % 3 | % tf = using_hg2(fig) 4 | % 5 | %IN: 6 | % fig - handle to the figure in question. 7 | % 8 | %OUT: 9 | % tf - boolean indicating whether the HG2 graphics pipeline is being used 10 | % (true) or not (false). 11 | 12 | function tf = using_hg2(fig) 13 | try 14 | tf = ~graphicsversion(fig, 'handlegraphics'); 15 | catch 16 | tf = false; 17 | end -------------------------------------------------------------------------------- /funcs/generate_common_data.m: -------------------------------------------------------------------------------- 1 | function generate_common_data 2 | % This function uses the trial-to-trial model of 2AFC to common data with 3 | % which all the models can use. By converting the trial-to-trial 4 | % performance into a number correct responses (k) then the data can be used 5 | % by models 1 and 2 in addition to model 3 which will use the full 6 | % trial-to-trial data. 7 | 8 | clear, clc, %close all, drawnow 9 | 10 | %% Double check this is what we want to do 11 | choice = questdlg('Do you REALLY want to proceed? This will overwrite any existing common dataset.', ... 12 | 'Careful', ... 13 | 'Yes, create new common dataset','No thank you','No thank you'); 14 | % Handle response 15 | switch choice 16 | case 'No thank you' 17 | error('Aborted') 18 | end 19 | 20 | 21 | %% MODEL PARAMETERS 22 | 23 | data.T = 100; % trials per signal level 24 | data.sioriginal = logspace(-2,1,10); % define the signal intensities 25 | data.v = 1; % internal noise variance 26 | data.lr = 0.01; % true lapse rate 27 | data.b = 0; % true bias 28 | data.pdist = [0.5 0.5]; % true spatial prior 29 | 30 | %% INFERENCE OPTIONS: just for generating the datasets 31 | mcmcparams.doparallel = 0; 32 | mcmcparams.JAGSmodel = 'funcs/model2JAGS.txt'; 33 | 34 | mcmcparams.generate.nchains = 1; 35 | mcmcparams.generate.nburnin = 500; 36 | mcmcparams.generate.nsamples = data.T; 37 | 38 | mcmcparams.infer.nchains = 2; 39 | mcmcparams.infer.nburnin = 500; 40 | mcmcparams.infer.nsamples = round(5000/mcmcparams.infer.nchains); % 50000 in the paper 41 | 42 | 43 | 44 | %% 45 | % in order to conduct model prediction on si values that we do not have 46 | % response data for, we will actually generate simulated data for all these 47 | % additional si values now. But the data we generate will be used in step 48 | % 2 where we only provide the model with response data for the actual si 49 | % values run in an experiment. In other words, we are just using this step 50 | % now to generate simulated 2AFC trial data (L) for these additional si 51 | % values we wish to examine. 52 | ni = 41; % number of si levels to examine 53 | data.sii = logspace(-2,1,ni); 54 | 55 | %data.si = [data.sioriginal data.sii]; 56 | data.si = [data.sioriginal data.sioriginal data.sii]; 57 | 58 | %% Step 1: Generate simulated dataset 59 | 60 | % model 2 requires [si si sii] 61 | [data] = model2generate(data, mcmcparams); 62 | 63 | %% Export for Model 2 64 | data.koriginal = data.k; 65 | save('data/commondata_model2.mat', 'data') 66 | 67 | 68 | %% Export for Model 3 69 | data.si = [data.sioriginal data.sii]; 70 | 71 | % These models do not use trial level data of location and response, so 72 | % this will be removed to keep things tidy 73 | data = rmfield(data,'L'); 74 | data = rmfield(data,'R'); 75 | data = rmfield(data,'lr'); 76 | data = rmfield(data,'b'); 77 | data = rmfield(data,'pdist'); 78 | data = rmfield(data,'pc'); 79 | 80 | % Add unknowns which we want to make inferenes over for the interpolated 81 | % signal intensity values 82 | data.koriginal = data.k; 83 | data.k(:,[numel(data.k)+1:numel(data.si)]) = NaN; 84 | %data.pc(:,[numel(data.sioriginal)+1:numel(data.si)]) = NaN; 85 | 86 | save('data/commondata_model3.mat', 'data') 87 | 88 | %% Export for Model 1 89 | save('data/commondata_model1.mat', 'data') 90 | 91 | %% 92 | fprintf('Generated and saved common data\n') 93 | 94 | return -------------------------------------------------------------------------------- /funcs/latex_fig/latex_fig.m: -------------------------------------------------------------------------------- 1 | function latex_fig(font_size, f_width, f_height) 2 | 3 | % font_size: the font size used in the paper; 4 | % f_width: the figure width (in inches) 5 | % f_height: the figure height (in inches) 6 | 7 | font_rate=10/font_size; 8 | set(gcf,'Position',[100 200 round(f_width*font_rate*144) round(f_height*font_rate*144)]) 9 | -------------------------------------------------------------------------------- /funcs/latex_fig/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Jinyu Zuo 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /funcs/m1InferGridApprox.m: -------------------------------------------------------------------------------- 1 | function [posterior_var,vMode,HDI] = m1InferGridApprox(estOpts,data) 2 | 3 | fprintf('Running parameter recovery via grid approximation...') 4 | posterior_var=zeros(size(estOpts.V)); % preallocate 5 | % GRID APPROX --------------------------------------------------- 6 | parfor n=1:numel(estOpts.V) % Do the grid approximation 7 | posterior_var(n) = m1jointPosterior(estOpts.V(n),... 8 | data.sioriginal, data.koriginal, data.T); 9 | end 10 | % --------------------------------------------------------------- 11 | fprintf(' done\n') 12 | % normalise 13 | posterior_var = posterior_var ./ sum(posterior_var); 14 | 15 | 16 | 17 | % Calculate posterior mode, the MAP value 18 | [~,index]=max(posterior_var); 19 | vMode = estOpts.V(index); 20 | % Calculate 95% HDI 21 | [HDI] = HDIofGrid(estOpts.V,posterior_var, 0.95); 22 | 23 | fprintf('Posterior over internal variance: mode=%2.3f (%2.3f - %2.3f)\n',... 24 | vMode, HDI.lower, HDI.upper) 25 | 26 | 27 | return -------------------------------------------------------------------------------- /funcs/m1generateJAGS.m: -------------------------------------------------------------------------------- 1 | function [data] = m1generateJAGS(data, mcmcparams) 2 | 3 | 4 | %% 5 | % Set initial values for each chain 6 | for n=1:mcmcparams.generate.nchains 7 | initial_param(n).k = round( (rand(numel(data.si),1)*10) +1 ); 8 | end 9 | 10 | %% Invoke JAGS via the |matjags| function 11 | % invoke JAGS to return samples from the posterior distribution of 12 | % 'variance' given the parameters defined in the structure 'dataset' 13 | 14 | [samples, stats] = matjags( ... 15 | data, ... 16 | fullfile(pwd, mcmcparams.JAGSmodel), ... 17 | initial_param, ... 18 | 'doparallel' , mcmcparams.doparallel, ... 19 | 'nchains', mcmcparams.generate.nchains,... 20 | 'nburnin', mcmcparams.generate.nburnin,... 21 | 'nsamples', mcmcparams.generate.nsamples, ... 22 | 'thin', 1, ... 23 | 'monitorparams', {'k'}, ... 24 | 'savejagsoutput' , 1 , ... 25 | 'verbosity' , 1 , ... 26 | 'cleanup' , 1 ,... 27 | 'rndseed', 1,... 28 | 'dic',0); 29 | 30 | % keep data from chain 1, for one sample 31 | data.k = squeeze( samples.k(1,end,:) )'; 32 | 33 | % we want to overwrite the generated k values corresponding to the signal 34 | % intensity levels we are examining for prediction 35 | data.k(:,[numel(data.sioriginal)+1:end]) = NaN; 36 | 37 | %data.k=knowns.k([1:numel(data.muS)]); 38 | data.koriginal=data.k([1:numel(data.sioriginal)]); 39 | 40 | return -------------------------------------------------------------------------------- /funcs/m1inferJAGS.m: -------------------------------------------------------------------------------- 1 | function [samples, stats] = m1inferJAGS(knowns, starting_var, mcmcparams) 2 | 3 | % Double check we have removed knowledge of the internal variance as this 4 | % is what we are trying to estimate 5 | try 6 | knowns = rmfield(knowns,'v'); 7 | catch 8 | % the field was already removed 9 | end 10 | %% 11 | % Set initial values for each chain 12 | clear initial_param 13 | for n=1:mcmcparams.infer.nchains 14 | initial_param(n).v = starting_var(n); 15 | end 16 | 17 | %% Invoke JAGS via the |matjags.m| function 18 | % invoke JAGS to return samples from the posterior distribution of 19 | % 'variance' given the parameters defined in the structure 'dataset' 20 | 21 | fprintf( 'Running JAGS...\n' ); 22 | [samples, stats] = matjags( ... 23 | knowns, ... 24 | fullfile(pwd, mcmcparams.JAGSmodel), ... 25 | initial_param, ... 26 | 'doparallel' , mcmcparams.doparallel, ... 27 | 'nchains', mcmcparams.infer.nchains,... 28 | 'nburnin', mcmcparams.infer.nburnin,... 29 | 'nsamples', mcmcparams.infer.nsamples, ... 30 | 'thin', 1, ... 31 | 'monitorparams', {'v','predk','k'}, ... 32 | 'savejagsoutput' , 1 , ... 33 | 'verbosity' , 1 , ... 34 | 'cleanup' , 1 ,... 35 | 'rndseed',1); 36 | 37 | fprintf('done\n') 38 | 39 | return -------------------------------------------------------------------------------- /funcs/m1jointPosterior.m: -------------------------------------------------------------------------------- 1 | function [posterior, predk] = m1jointPosterior(variance, si, k, T) 2 | % This function will return the posterior of the joint distribution 3 | % P(variance, si, k, T). 4 | % It will do this by summing log probabilities 5 | 6 | logp = 0; 7 | 8 | %% prior 9 | logp = logp + log(1/1000); % uniform prior on range 0-1000 10 | % calculate value of deterministic node PC 11 | PC = pcfunc(si, variance); 12 | 13 | %% data likelihood 14 | klogp = sum(log( binopdf(k, T, PC ))); 15 | logp = logp + klogp; 16 | 17 | %% convert from log posterior to posterior 18 | posterior = exp(logp); 19 | 20 | % %% Posterior prediction 21 | % 22 | % % sample some values of k, given the params {variance, si, T} 23 | % predk = binornd(T, PC ); 24 | 25 | return -------------------------------------------------------------------------------- /funcs/m1posteriorPrediction.m: -------------------------------------------------------------------------------- 1 | function k = m1posteriorPrediction(T, si, variance) 2 | % This function generates random samples of k 3 | 4 | % Calculate value of the deterministic node P(PC|si,muN,variance) 5 | PC = pcfunc(si, variance); 6 | 7 | % Sample from P(k|T,PC) 8 | k = binornd(T,PC); 9 | end -------------------------------------------------------------------------------- /funcs/makeExplanatoryFigures/fig2AfcDiagram.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:a54628ffde146a3677b4921cd771f020bf5d25048536579eeb371d1e88767ee7" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "Produce 2AFC explanatory diagram" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "This requires you to not use inline mode. To do this, launch IPython from the terminal with the command \n", 24 | "\n", 25 | "`ipython notebook --pylab`" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "collapsed": false, 31 | "input": [ 32 | "import numpy as np\n", 33 | "import matplotlib.pyplot as plt\n", 34 | "\n", 35 | "# for normal distribution\n", 36 | "from scipy.stats import norm " 37 | ], 38 | "language": "python", 39 | "metadata": {}, 40 | "outputs": [], 41 | "prompt_number": 1 42 | }, 43 | { 44 | "cell_type": "heading", 45 | "level": 2, 46 | "metadata": {}, 47 | "source": [ 48 | "Formatting issues" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "The following allows latex rendering of text" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "collapsed": false, 61 | "input": [ 62 | "from matplotlib import rc\n", 63 | "rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})\n", 64 | "## for Palatino and other serif fonts use:\n", 65 | "#rc('font',**{'family':'serif','serif':['Palatino']})\n", 66 | "rc('text', usetex=True)" 67 | ], 68 | "language": "python", 69 | "metadata": {}, 70 | "outputs": [], 71 | "prompt_number": 2 72 | }, 73 | { 74 | "cell_type": "code", 75 | "collapsed": false, 76 | "input": [], 77 | "language": "python", 78 | "metadata": {}, 79 | "outputs": [], 80 | "prompt_number": 2 81 | }, 82 | { 83 | "cell_type": "code", 84 | "collapsed": false, 85 | "input": [], 86 | "language": "python", 87 | "metadata": {}, 88 | "outputs": [], 89 | "prompt_number": 2 90 | }, 91 | { 92 | "cell_type": "code", 93 | "collapsed": false, 94 | "input": [ 95 | "N=1000\n", 96 | "muS=1\n", 97 | "muN=0\n", 98 | "sigma=1\n", 99 | "SNx1 = np.random.normal(muS, sigma, 1000)\n", 100 | "SNx2 = np.random.normal(muN, sigma, 1000)\n", 101 | "\n", 102 | "NSx1 = np.random.normal(muN, sigma, 1000)\n", 103 | "NSx2 = np.random.normal(muS, sigma, 1000)" 104 | ], 105 | "language": "python", 106 | "metadata": {}, 107 | "outputs": [], 108 | "prompt_number": 3 109 | }, 110 | { 111 | "cell_type": "heading", 112 | "level": 2, 113 | "metadata": {}, 114 | "source": [ 115 | "Create a figure" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "collapsed": false, 121 | "input": [ 122 | "clf() \n", 123 | "f = plt.figure(num=1, figsize=(8, 6), dpi=600, facecolor='none', edgecolor='k')" 124 | ], 125 | "language": "python", 126 | "metadata": {}, 127 | "outputs": [], 128 | "prompt_number": 123 129 | }, 130 | { 131 | "cell_type": "heading", 132 | "level": 2, 133 | "metadata": {}, 134 | "source": [ 135 | "First panel" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "collapsed": false, 141 | "input": [ 142 | "ax1=plt.subplot(1,2,1, aspect='equal',axisbg='none')\n", 143 | "plt.rc('text', usetex=True)\n", 144 | "plt.scatter(SNx1, SNx2, s=15, c=[1,0.3,0.3], alpha=0.4, edgecolors='none')\n", 145 | "plt.scatter(NSx1, NSx2, s=15, c=[0.3,0.3,1], alpha=0.4, edgecolors='none')\n", 146 | "padding=sigma*3\n", 147 | "axis([muN-padding,muS+padding,muN-padding,muS+padding])\n", 148 | "\n", 149 | "\n", 150 | "\n", 151 | "\n", 152 | "ax1.tick_params(direction='out')\n", 153 | "\n", 154 | "# Move left and bottom spines outward by 10 points\n", 155 | "#ax1.spines['left'].set_position(('outward', 10))\n", 156 | "#ax1.spines['bottom'].set_position(('outward', 10))\n", 157 | "# Hide the right and top spines\n", 158 | "ax1.spines['right'].set_visible(False)\n", 159 | "ax1.spines['top'].set_visible(False)\n", 160 | "# Only show ticks on the left and bottom spines\n", 161 | "ax1.yaxis.set_ticks_position('left')\n", 162 | "ax1.xaxis.set_ticks_position('bottom')\n", 163 | "\n", 164 | "xlabel('$x_1$')\n", 165 | "ylabel('$x_2$')\n", 166 | "\n", 167 | "ax1.set_xticks([0,muS])\n", 168 | "ax1.set_yticks([0,muS])\n", 169 | "\n", 170 | "ax1.set_xticklabels(['$0$','$\\mu_S$'])\n", 171 | "ax1.set_yticklabels(['$0$','$\\mu_S$'])\n", 172 | "\n", 173 | "\n", 174 | "#plt.axis([-6, -6, 10, 10])\n", 175 | "\n", 176 | "# plot decision line\n", 177 | "plt.plot((-8,10),(-8,10),'k:')\n", 178 | "\n", 179 | "\n", 180 | "# Plot distance between centres\n", 181 | "plt.annotate ('', (muS,0), (0,muS), arrowprops={'arrowstyle':'<->'})\n", 182 | "#plt.text( muS,muS, '$\\sqrt{2\\mu_S^2}$', rotation=45)\n", 183 | "\n", 184 | "\n", 185 | "#plt.annotate(\n", 186 | "# 'D = 1', xy=(1, 9), xycoords = 'data',\n", 187 | "# xytext = (5, 0), textcoords = 'offset points')\n", 188 | "\n", 189 | "\n", 190 | "\n", 191 | "plt.text( 3*muS,-2*muS, '$SN$')\n", 192 | "\n", 193 | "plt.text( -2*muS,3*muS, '$NS$')\n", 194 | "\n", 195 | "\n", 196 | "\n" 197 | ], 198 | "language": "python", 199 | "metadata": {}, 200 | "outputs": [ 201 | { 202 | "metadata": {}, 203 | "output_type": "pyout", 204 | "prompt_number": 124, 205 | "text": [ 206 | "" 207 | ] 208 | } 209 | ], 210 | "prompt_number": 124 211 | }, 212 | { 213 | "cell_type": "heading", 214 | "level": 2, 215 | "metadata": {}, 216 | "source": [ 217 | "Create second panel" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "collapsed": false, 223 | "input": [ 224 | "\n", 225 | "ax2 = plt.subplot(1,2,2, aspect=20,axisbg='none')\n", 226 | "plt.rc('text', usetex=True)\n", 227 | "\n", 228 | "xmin=-muS-5\n", 229 | "xmax=muS+5\n", 230 | "# vector of x values\n", 231 | "x=np.linspace(xmin,xmax,501)\n", 232 | "# create a corresponding vector of zeros for use later\n", 233 | "theFloor = np.zeros(dsn.size)\n", 234 | "# Calculate the y-values of the distributions\n", 235 | "dsn = norm.pdf(x,muS,sqrt(2)*sigma)\n", 236 | "dns = norm.pdf(x,-muS,sqrt(2)*sigma)\n", 237 | "maxy=np.max(dsn)\n", 238 | "plt.plot( x, dns,'b-')\n", 239 | "plt.plot( x, dsn,'r-')\n", 240 | "\n", 241 | "# Fill error regions\n", 242 | "ax2.fill_between(x, 0, dsn, where=x<=0, facecolor='r', alpha=0.5, edgecolor='none')\n", 243 | "ax2.fill_between(x, 0, dns, where=x>=0, facecolor='b', alpha=0.5, edgecolor='none')\n", 244 | "\n", 245 | "\n", 246 | "# Move left and bottom spines outward by 10 points\n", 247 | "#ax.spines['left'].set_position(('outward', 10))\n", 248 | "#ax.spines['bottom'].set_position(('outward', 10))\n", 249 | "# Hide the right and top spines\n", 250 | "ax2.spines['right'].set_visible(False)\n", 251 | "ax2.spines['top'].set_visible(False)\n", 252 | "ax2.spines['left'].set_visible(False)\n", 253 | "# Only show ticks on the left and bottom spines\n", 254 | "#ax2.yaxis.set_ticks_position('left')\n", 255 | "ax2.xaxis.set_ticks_position('bottom')\n", 256 | "\n", 257 | "ax2.tick_params(direction='out')\n", 258 | "\n", 259 | "xlabel('$d = x_1-x_2$')\n", 260 | "\n", 261 | "ax2.set_xticks([-muS,0,muS])\n", 262 | "ax2.set_yticks([])\n", 263 | "\n", 264 | "ax2.set_xticklabels(['$-\\mu_S$','$0$','$\\mu_S$'])\n", 265 | "\n", 266 | "# draw decision boundary\n", 267 | "plt.axvline(x=0, linestyle=':', color='k')\n", 268 | "\n", 269 | "\n", 270 | "# distribution labels\n", 271 | "plt.text( muS*2.7,maxy*0.5, '$d_{SN}$', \n", 272 | " horizontalalignment='left',\n", 273 | " color ='r')\n", 274 | "\n", 275 | "plt.text( -muS*2.7,maxy*0.5, '$d_{NS} $', \n", 276 | " horizontalalignment='right',\n", 277 | " color ='b')\n", 278 | "\n", 279 | "# Decision test\n", 280 | "plt.text(0.5, maxy*1.05, 'say ${SN}$', \n", 281 | " horizontalalignment='left')\n", 282 | "\n", 283 | "plt.text(-0.5, maxy*1.05, 'say ${NS}$',\n", 284 | " horizontalalignment='right')\n", 285 | "\n" 286 | ], 287 | "language": "python", 288 | "metadata": {}, 289 | "outputs": [ 290 | { 291 | "metadata": {}, 292 | "output_type": "pyout", 293 | "prompt_number": 125, 294 | "text": [ 295 | "" 296 | ] 297 | } 298 | ], 299 | "prompt_number": 125 300 | }, 301 | { 302 | "cell_type": "code", 303 | "collapsed": false, 304 | "input": [], 305 | "language": "python", 306 | "metadata": {}, 307 | "outputs": [], 308 | "prompt_number": 125 309 | }, 310 | { 311 | "cell_type": "code", 312 | "collapsed": false, 313 | "input": [], 314 | "language": "python", 315 | "metadata": {}, 316 | "outputs": [], 317 | "prompt_number": 125 318 | }, 319 | { 320 | "cell_type": "heading", 321 | "level": 2, 322 | "metadata": {}, 323 | "source": [ 324 | "Save the figure as a pdf" 325 | ] 326 | }, 327 | { 328 | "cell_type": "code", 329 | "collapsed": false, 330 | "input": [ 331 | "# save it\n", 332 | "plt.savefig('testAFCplot.pdf',dpi=300, bbox_inches='tight', transparent=True)" 333 | ], 334 | "language": "python", 335 | "metadata": {}, 336 | "outputs": [], 337 | "prompt_number": 126 338 | }, 339 | { 340 | "cell_type": "code", 341 | "collapsed": false, 342 | "input": [], 343 | "language": "python", 344 | "metadata": {}, 345 | "outputs": [], 346 | "prompt_number": 122 347 | }, 348 | { 349 | "cell_type": "code", 350 | "collapsed": false, 351 | "input": [ 352 | "?fill_between" 353 | ], 354 | "language": "python", 355 | "metadata": {}, 356 | "outputs": [], 357 | "prompt_number": 129 358 | }, 359 | { 360 | "cell_type": "code", 361 | "collapsed": false, 362 | "input": [], 363 | "language": "python", 364 | "metadata": {}, 365 | "outputs": [] 366 | } 367 | ], 368 | "metadata": {} 369 | } 370 | ] 371 | } -------------------------------------------------------------------------------- /funcs/makeExplanatoryFigures/generateGridVsMCMCschematic.m: -------------------------------------------------------------------------------- 1 | function generateGridVsMCMCschematic 2 | 3 | %% true distribution 4 | % k heads out of T coin tosses 5 | k=3;T=10; 6 | 7 | thetai = linspace(0,1,1000); 8 | postTrue = betapdf(thetai,1+k,1+(T-k)); 9 | postTrue = postTrue./max(postTrue); 10 | 11 | 12 | 13 | %% GRID APPROXIMATION 14 | theta_vec=linspace(0,1,21); 15 | for n=1:numel(theta_vec) 16 | 17 | % calculate joint 18 | logp=0; 19 | logp=logp+log(betapdf(theta_vec(n),1,1)); 20 | logp=logp+log(binopdf(k,T,theta_vec(n))); 21 | p(n) = exp(logp); 22 | end 23 | 24 | 25 | p = p./max(p); 26 | 27 | figure(1), clf 28 | subplot(1,2,1), plot(thetai,postTrue,'r-'), hold on 29 | 30 | 31 | subplot(1,2,1) 32 | stem(theta_vec,p,'filled') 33 | box off 34 | xlabel('parameter value') 35 | ylabel('posterior') 36 | axis tight 37 | set(gca,'YTick',[],... 38 | 'XTick',[0:0.1:1],... 39 | 'PlotBoxAspectRatio',[1.5 1 1]) 40 | title('Grid approximation','FontWeight','Bold') 41 | 42 | 43 | %% samples 44 | % This is just a schematic diagram, so the code below does not atually 45 | % conduct MCMC sampling. We just draw samples from a known posterior. 46 | nsamples = 200; 47 | R = betarnd(1+k,1+(T-k), nsamples,1)'; 48 | 49 | subplot(1,2,2), hold on 50 | 51 | 52 | 53 | 54 | postTrue = postTrue./sum(postTrue); 55 | subplot(1,2,2), plot(thetai,postTrue,'r-') 56 | 57 | % kernel density estimate 58 | dens = ksdensity(R,thetai,'support',[0 1]); 59 | dens= dens./sum(dens); 60 | plot(thetai,dens) 61 | 62 | ymax = max(get(gca,'YLim')); 63 | 64 | % rug plot 65 | plot([R;R], [zeros(size(R));ones(size(R)).*0.05*ymax],... 66 | 'k',... 67 | 'LineWidth',1) 68 | hold on 69 | 70 | box off 71 | xlabel('parameter value') 72 | ylabel('posterior') 73 | axis tight 74 | set(gca,'YTick',[],... 75 | 'XTick',[0:0.1:1],... 76 | 'PlotBoxAspectRatio',[1.5 1 1]) 77 | title('MCMC sampling','FontWeight','Bold') 78 | 79 | 80 | %% Export 81 | latex_fig(12, 7, 3) 82 | 83 | % Export in .fig and .pdf 84 | cd('figs') 85 | figure(1), export_fig param_estimation -pdf -m1 86 | figure(1), hgsave('param_estimation') 87 | cd('..') 88 | end -------------------------------------------------------------------------------- /funcs/makeExplanatoryFigures/generate_psychometric_functions.m: -------------------------------------------------------------------------------- 1 | function generate_psychometric_functions 2 | 3 | % Initial setting up 4 | clear, close all; clc 5 | addpath([cd '/funcs']) 6 | addpath([cd '/funcs/export_fig']) 7 | 8 | %% Define anonymous functions 9 | pc = @(muS,muN,variance) normcdf( muS ./ sqrt(2*variance) ); 10 | %Lk = @(k,T,pc) binopdf(k , T, pc); 11 | %LkTotal = @(L) exp( sum( log(L) ) ); 12 | 13 | 14 | muN=0; 15 | muS=logspace(-2,1,500); 16 | 17 | v=[0.5, 1, 2]; % variance 18 | 19 | for n=1:numel(v) 20 | performance(:,n) = pc(muS,muN,v(n))*100; 21 | end 22 | 23 | h = semilogx(muS,performance,'LineWidth',3); 24 | 25 | % set line colour 26 | set(h(1),'Color',[0.25 0.25 0.25]) 27 | set(h(2),'Color',[0.5 0.5 0.5]) 28 | set(h(3),'Color',[0.75 0.75 0.75]) 29 | 30 | % formatting 31 | set(gca,'XScale','log',... 32 | 'PlotBoxAspectRatio',[1 1 1],... 33 | 'box', 'off',... 34 | 'xlim', [0.009 10],... 35 | 'ylim', [49 101],... 36 | 'XTick',[0.01 0.1 1, 10],... 37 | 'YTick',[0:10:100],... 38 | 'PlotBoxAspectRatio',[2 1 1],... 39 | 'TickDir','out') 40 | xlabel('signal intensity') 41 | ylabel('percent correct') 42 | 43 | % Legend 44 | legend(num2str(v'),... 45 | 'Location', 'SouthEast') 46 | legend boxoff 47 | 48 | %% Export 49 | figure(1) 50 | % Automatic resizing to make figure appropriate for font size 51 | latex_fig(12, 5, 3) 52 | 53 | % Export in .fig and .pdf 54 | cd('figs') 55 | hgsave('psychometric_funcs') 56 | export_fig psychometric_funcs -pdf -m1 57 | cd('..') 58 | return 59 | -------------------------------------------------------------------------------- /funcs/mhAlgorithm.m: -------------------------------------------------------------------------------- 1 | function [samples,acceptance_rate] = mhAlgorithm(estOpts,... 2 | si, k, T) 3 | 4 | accepted = 0; 5 | 6 | % preallocate vector of param values 7 | samples = zeros(estOpts.n_samples,1); 8 | samples(1) = estOpts.initial_variance; 9 | 10 | for n=2:estOpts.n_samples 11 | 12 | old_sample = samples(n-1); 13 | old_posterior = estOpts.pdf(old_sample, si, k, T); 14 | 15 | % suggest a new sample, but variance can't be less than or equal to zero 16 | new_sample=-inf; 17 | while le(new_sample,0) 18 | new_sample = normrnd(old_sample,estOpts.proposalstd); 19 | end 20 | new_posterior = estOpts.pdf(new_sample, si, k, T); 21 | 22 | % maybe accept new sample 23 | if new_posterior > old_posterior 24 | samples(n) = new_sample; 25 | accepted = accepted + 1; 26 | else 27 | u = rand; 28 | if u < new_posterior/old_posterior 29 | samples(n) = new_sample; 30 | accepted = accepted + 1; 31 | else 32 | samples(n) = old_sample; 33 | end 34 | end 35 | 36 | if rem(n,5000)==0 37 | fprintf('%3.1f%%\n',(n/estOpts.n_samples)*100) 38 | end 39 | end 40 | 41 | acceptance_rate = accepted / estOpts.n_samples; 42 | fprintf('Acceptance rate: %2.1f %%\n',acceptance_rate*100) 43 | 44 | 45 | return -------------------------------------------------------------------------------- /funcs/model1MCMCconvergence.m: -------------------------------------------------------------------------------- 1 | % Initial set up 2 | clear, close all; clc 3 | addpath([cd '/funcs']) 4 | addpath([cd '/funcs/export_fig']) 5 | addpath([cd '/funcs/ColorBand']) 6 | DATAMODE='generate' % {'load','generate'} 7 | 8 | % Define MCMC parameters 9 | mcmcparams = define_mcmcparams('model1'); 10 | 11 | 12 | %% Step 1: Either Load common dataset, or generate simulated dataset 13 | switch DATAMODE 14 | case{'load'} 15 | load('commondata_model1.mat') 16 | 17 | case{'generate'} 18 | % Define experiment parameters 19 | data = define_experiment_params('model1'); 20 | 21 | % Generate simulated number of correct trials for a rang of signal 22 | % levels. 23 | data = m1generateJAGS(data, mcmcparams); 24 | 25 | end 26 | 27 | %% Step 2: Infer the internal noise using the graphical model and the simulated data 28 | % Now we have a dataset consisting of the number of trials the simulated 29 | % observer correctly localised the target across a range of signal levels. 30 | % 31 | % First define a range of initial parameter estimates for the variance, 32 | % each will be the starting point for an individual MCMC chain 33 | starting_var = logspace(-5,5,11); 34 | mcmcparams.infer.nchains = numel(starting_var); 35 | %% 36 | % We want to observer chain values, so we are removing the burn in period 37 | mcmcparams.infer.nburnin=0; 38 | %% 39 | % we only need to calculate a low number of samples, just to demonstrate 40 | % initial convervence 41 | mcmcparams.infer.nsamples=1000; 42 | %% 43 | % Now do inference on all the generated data. The function |model2infer.m| 44 | % gathers the data and sends it to JAGS via _MATJAGS_. 45 | 46 | [samples, stats] = m1inferJAGS(data, starting_var, mcmcparams); 47 | 48 | %% JAGS seems to cut off the initial parameters 49 | % So to demonstrate the point, we'll add them back on 50 | chains = samples.v(:,[1:200])'; 51 | chains =[starting_var ; chains]; 52 | 53 | %% plot MCMC chains 54 | figure(1), clf, hold all 55 | 56 | ColorSet=ColorBand( numel(starting_var) ); 57 | set(gca, 'ColorOrder', ColorSet); 58 | 59 | % Visually inspect chains and examine the $\hat{R}$ statistic. 60 | 61 | plot(chains) 62 | axis tight 63 | %legend(num2str(starting_var')) 64 | xlabel('mcmc sample') 65 | ylabel('\sigma^2') 66 | box off 67 | set(gca,'XScale','log',... 68 | 'YScale','log',... 69 | 'YTick',starting_var) 70 | 71 | 72 | 73 | 74 | %% Export 75 | temp=cd; 76 | try 77 | latex_fig(12, 6, 4) 78 | cd('figs') 79 | hgsave('model1mcmcConvergence') 80 | export_fig model1mcmcConvergence -pdf -m1 81 | cd('..') 82 | catch 83 | cd(temp) 84 | end -------------------------------------------------------------------------------- /funcs/model1MCMCse.m: -------------------------------------------------------------------------------- 1 | function model1MCMCse(TASK) 2 | % model1MCMCse('calculate') 3 | % model1MCMCse('plot') 4 | 5 | 6 | rows=2; 7 | cols=1; 8 | 9 | 10 | switch TASK 11 | case{'calculate'} 12 | N_SIMULATIONS = 30; % 30 in paper 13 | 14 | % nsamples =[ 10^2 10^3 10^4 10^5 10^6]; 15 | % n =[ 20 20 20 20 20]; 16 | nsamples =logspace(2,6,5); %logspace(2,6,5) in paper 17 | n =N_SIMULATIONS.*ones(size(nsamples)); 18 | 19 | %% load the MAP estimate made by Model 1, which we will use in a plot 20 | cd('output') 21 | load('m1MAPestimate.mat') % variable stored as 'vMode' 22 | cd('..') 23 | 24 | %% 25 | counter = 1; 26 | for k=1:numel(nsamples) 27 | for rep=1:n(k) 28 | fprintf('job %d of %d. %d samples \n', counter, sum(n) , nsamples(k)) 29 | Emode(counter)= internal_function_paramrecovery( nsamples(k) ); 30 | Ensamples(counter) = nsamples(k) ; 31 | 32 | figure(3), clf 33 | subplot(rows,cols,1) 34 | semilogx(Ensamples,Emode,'k+') 35 | xlabel('total MCMC samples') 36 | ylabel('MAP value of \sigma^2') 37 | drawnow 38 | 39 | counter = counter+1; 40 | end 41 | % Calculate the standard error of the estimate 42 | temp_estimates = Emode( Ensamples==nsamples(k) ); 43 | se(k) = std(temp_estimates) / sqrt( n(k) ); 44 | end 45 | 46 | 47 | %% SAVE 48 | save(['~/Dropbox/tempModelOutputs/' 'tempModel_MCMCse'], '-v7.3') 49 | 50 | case{'plot'} 51 | % load data 52 | try 53 | load(['~/Dropbox/tempModelOutputs/' 'tempModel_MCMCse.mat']) 54 | catch 55 | 56 | end 57 | 58 | figure(3), clf 59 | subplot(rows,cols,1) 60 | semilogx(Ensamples,Emode,'k+') 61 | xlabel('total MCMC samples') 62 | ylabel('MAP value of \sigma^2') 63 | drawnow 64 | 65 | 66 | axis tight 67 | figure(3) 68 | subplot(rows,cols,1) 69 | set(gca, 'PlotBoxAspectRatio',[2 1 1],... 70 | 'box', 'off',... 71 | 'xlim', [min(nsamples)-10 max(nsamples)+10],... 72 | 'XTick',nsamples,... 73 | 'YTick',[0.6:0.1:1.2]) 74 | 75 | % plot line at the estimated mode by Model 1 76 | hline(vMode) 77 | 78 | subplot(rows,cols,2) 79 | %bar(se) 80 | semilogx(nsamples,se,'ko-',... 81 | 'MarkerFaceColor','w') 82 | xlabel('total MCMC samples') 83 | ylabel('standard error') 84 | axis tight 85 | a=axis; 86 | set(gca, 'PlotBoxAspectRatio',[2 1 1],... 87 | 'box', 'off',... 88 | 'xlim', [min(nsamples)-10 max(nsamples)+10],... 89 | 'ylim', [0 a(4)*1.1],... 90 | 'XTick',nsamples) 91 | 92 | 93 | %% Export 94 | latex_fig(12, 3,3.5) 95 | 96 | cd('figs') 97 | hgsave('model1MCMCse') 98 | export_fig model1MCMCse -pdf -m1 99 | cd('..') 100 | 101 | end 102 | 103 | end 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | function [mcmc_estimated_mode]=internal_function_paramrecovery(nsamples) 113 | 114 | %% define MCMC params 115 | % Define MCMC parameters 116 | mcmcparams = define_mcmcparams('model1'); 117 | % overwrite default params 118 | mcmcparams.infer.nsamples = round(nsamples / mcmcparams.infer.nchains); 119 | 120 | 121 | load('commondata_model1.mat') 122 | 123 | 124 | % First define a range of initial parameter estimates for the variance, 125 | % each will be the starting point for an individual MCMC chain 126 | starting_var = [0.1 1 10 100]; 127 | mcmcparams.infer.nchains = numel(starting_var); 128 | 129 | %% 130 | % Now do inference on all the generated data. The function |model2infer.m| 131 | % gathers the data and sends it to JAGS via _MATJAGS_. 132 | 133 | [samples, stats] = m1inferJAGS(data, starting_var, mcmcparams); 134 | 135 | %% 136 | % Calculate mode (the MAP estimate) by kernel density estimation 137 | mcmc_estimated_mode = sampleStats(samples.v(:), 'positive'); 138 | 139 | end 140 | -------------------------------------------------------------------------------- /funcs/model1plot.m: -------------------------------------------------------------------------------- 1 | function model1plot(filename) 2 | % This function plots the results (parameter recovery and posterior prediction) that was conducted by model1runme. 3 | % 4 | % model1plot('tempModel1run_gridApprox') 5 | % model1plot('tempModel1run_mcmcCustom') 6 | % model1plot('tempModel1run_mcmcJAGS') 7 | 8 | % load the parameter estimation results calculated by model1runme.m 9 | load(['~/Dropbox/tempModelOutputs/' filename]) 10 | 11 | switch PARAM_RECOVERY_METHOD 12 | case{'mcmcJAGS'} 13 | 14 | % plot MCMC chains 15 | % Visually inspect chains and examine the $\hat{R}$ statistic. 16 | MCMCdiagnoticsPlot(samples,stats,{'v'}) 17 | temp=cd; 18 | try 19 | latex_fig(12,6,3) 20 | cd('figs') 21 | hgsave('model1_mcmcJAGS_infer_chains') 22 | export_fig model1_mcmcJAGS_infer_chains -pdf -m1 23 | cd('..') 24 | catch 25 | cd(temp) 26 | end 27 | end 28 | 29 | 30 | 31 | %% Plot the inferences in parameter space 32 | % Plot the posterior distribution over $\sigma^2$ 33 | figure(1), clf, latex_fig(12, 5, 3) 34 | subplot(1,2,2) 35 | % plot posterior distribtion 36 | switch PARAM_RECOVERY_METHOD 37 | case{'gridApprox'} 38 | area(estOpts.V,posterior_var,... 39 | 'FaceColor', [0.7 0.7 0.7], ... 40 | 'LineStyle','none') 41 | hold on, a=axis; top =a(4); z=0.03; 42 | % plot 95% CHI interval as horizontal line 43 | plot([HDI.lower HDI.upper],[top*z top*z],'k-'); 44 | % Add summary info 45 | addDistributionSummaryText(vMode, [HDI.lower HDI.upper], 'TR', 12) 46 | 47 | case{'mcmcCustom','mcmcJAGS'} 48 | area(xi,p,... 49 | 'FaceColor', [0.7 0.7 0.7], ... 50 | 'LineStyle','none') 51 | % Add summary info 52 | addDistributionSummaryText(MAP, CI95, 'TR', 12) 53 | 54 | end 55 | 56 | axis tight 57 | hline([],data.v) 58 | 59 | % format graph 60 | xlabel('inferred \sigma^2') 61 | ylabel('posterior density') 62 | set(gca, 'PlotBoxAspectRatio',[1 1 1],... 63 | 'box', 'off',... 64 | 'yticklabel',{},... 65 | 'YTick',[],... 66 | 'xlim', [0 3]) 67 | %title('b.') 68 | 69 | 70 | 71 | %% Plot results in data space 72 | % Plot the simulated behaviour data alongside model predictions 73 | 74 | subplot(1,2,1) 75 | % ===== TEST ===== 76 | for n=1:size(predk,2) 77 | IM(:,n) = hist( predk(:,n) ,[1:1:100] ); 78 | % scale so the max numerical value = 100 79 | IM(:,n) =IM(:,n) / (max(IM(:,n))/100); 80 | end 81 | imXdata = data.sii; 82 | imYdata = [0:1:100]/100; 83 | 84 | pltXdata = data.sioriginal; 85 | pltYdata = data.koriginal ./ data.T; 86 | 87 | log_plot_with_background(IM,... 88 | imXdata , imYdata,... 89 | pltXdata, pltYdata) 90 | 91 | 92 | %% Export 93 | figure(1) 94 | %latex_fig(12, 5, 3) 95 | 96 | % Export in .fig and .pdf 97 | cd('figs') 98 | 99 | switch PARAM_RECOVERY_METHOD 100 | case{'gridApprox'} 101 | hgsave('model1') 102 | export_fig model1 -pdf -m1 103 | 104 | case{'mcmcCustom'} 105 | hgsave('model1mcmcCustom') 106 | export_fig model1mcmcCustom -pdf -m1 107 | 108 | case{'mcmcJAGS'} 109 | hgsave('model1mcmcJAGS') 110 | export_fig model1mcmcJAGS -pdf -m1 111 | end 112 | 113 | cd('..') 114 | 115 | -------------------------------------------------------------------------------- /funcs/model2generate.m: -------------------------------------------------------------------------------- 1 | 2 | function [knowns] = model2generate(knowns, mcmcparams) 3 | 4 | %% Preliminaries 5 | 6 | N = 2; % DO NOT CHANGE THIS. 7 | 8 | % If the external noise variance is set to zero, then we need to adjust 9 | % this to be a very small number. If it is exactly zero, then we get divide 10 | % by zero errors in JAGS. 11 | if knowns.extvariance==0 12 | knowns.extvariance=10^-20; 13 | end 14 | 15 | % Set initial values for latent variable in each chain 16 | clear initial_param 17 | for i=1:mcmcparams.generate.nchains 18 | for s=1:numel(knowns.si) 19 | for t=1:knowns.T 20 | initial_param(i).L(s,t) = round( ( rand*(N-1)) +1); 21 | %initial_param(i).x(t,s) = randn*5; 22 | end 23 | end 24 | end 25 | 26 | 27 | %% Calling JAGS to sample 28 | [samples, stats] = matjags( ... 29 | knowns, ... 30 | fullfile(pwd, mcmcparams.JAGSmodel), ... 31 | initial_param, ... 32 | 'doparallel' , mcmcparams.doparallel, ... 33 | 'nchains', mcmcparams.generate.nchains,... 34 | 'nburnin', mcmcparams.generate.nburnin,... 35 | 'nsamples', mcmcparams.generate.nsamples, ... 36 | 'thin', 1, ... 37 | 'monitorparams', {'L', 'R'}, ... 38 | 'savejagsoutput' , 1 , ... 39 | 'verbosity' , 1 , ... 40 | 'cleanup' , 1 ,... 41 | 'rndseed', 1); 42 | 43 | %% 44 | % We wanted to create our dataset by only simulating a single trial, but we 45 | % gather our data over multiple trials from different MCMC samples. There 46 | % seems to be a limitation in JAGS where you cannot evaluate only 1 47 | % trial... seems to be due to size of the arrays etc. So we need to extract 48 | % the information we are after now 49 | knowns.L = squeeze(samples.L(1,:,:,1))'; 50 | knowns.R = squeeze(samples.R(1,:,:,1))'; 51 | 52 | 53 | % PROPORTION CORRECT - ACTUAL DATA 54 | for n=1:numel(knowns.sioriginal) 55 | %knowns.k(n) = sum(knowns.L(:,n)==knowns.R(:,n)); 56 | knowns.k(n) = sum(knowns.L(n,:)==knowns.R(n,:)); 57 | knowns.pc(n) = knowns.k(n) / knowns.T; 58 | end 59 | 60 | % we now want to remove the responses generated for the additional si 61 | % values. These would never have been obtained in an experiment. In fact, 62 | % rather than literally delete the data, we replace with NaN's so that JAGS 63 | % knows these are missing data point that we will make inferences about. 64 | knowns.R([numel(knowns.sioriginal)+1:end],:) = NaN; 65 | 66 | return -------------------------------------------------------------------------------- /funcs/model2infer.m: -------------------------------------------------------------------------------- 1 | function [samples,stats] = model2infer(knowns, mcmcparams) 2 | 3 | try 4 | % remove fields for which we want to make inferences 5 | knowns = rmfield(knowns,'v'); 6 | knowns = rmfield(knowns,'lr'); 7 | knowns = rmfield(knowns,'b'); 8 | catch 9 | % the field was already removed 10 | end 11 | 12 | % Set initial values for latent variable in each chain 13 | clear initial_param 14 | for i=1:mcmcparams.infer.nchains 15 | initial_param(i).v= 0.01+rand*10; 16 | %if inferlapserateFLAG==1 17 | initial_param(i).lr = rand; 18 | %end 19 | %if inferbFLAG==1 20 | initial_param(i).b = (rand-0.5)*8; 21 | %end 22 | end 23 | 24 | %% Call JAGS 25 | fprintf( 'Running JAGS...\n' ); 26 | tic 27 | [samples, stats] = matjags( ... 28 | knowns, ... 29 | fullfile(pwd, mcmcparams.JAGSmodel), ... 30 | initial_param, ... 31 | 'doparallel' , mcmcparams.doparallel, ... 32 | 'nchains', mcmcparams.infer.nchains,... 33 | 'nburnin', mcmcparams.infer.nburnin,... 34 | 'nsamples', mcmcparams.infer.nsamples, ... 35 | 'thin', 1, ... 36 | 'monitorparams', {'v','lr', 'b', 'postpredR','bprior'}, ... 37 | 'savejagsoutput' , 1 , ... 38 | 'verbosity' , 1 , ... 39 | 'cleanup' , 1 ,... 40 | 'rndseed', 1); 41 | min_sec(toc); 42 | 43 | % note: we are monitoring R because we have supplied missing data 44 | % corresponding to additional muS values that we want to predict the 45 | % performance for 46 | 47 | 48 | 49 | 50 | return -------------------------------------------------------------------------------- /funcs/model2modelfit.m: -------------------------------------------------------------------------------- 1 | function [predictions] = model2modelfit(predictions, T, pc, k, free_params) 2 | 3 | % % ******* THIS IS IMPORTANT ******* 4 | % % Choice of samples.R and samples.postpredR is important here 5 | % 6 | % % These are model predictions given knowledge of the exact signal locations 7 | % % L and the response locations R that the simulated participant made. Given 8 | % % this knowledge, the model predictions can be much more precise (if it's a 9 | % % good model). 10 | % 11 | % % collapse across chains 12 | % % We are sampling from the posterior predictive distribution 13 | % % not the supplied actual data! ie samples.R 14 | % tempR = samples.postpredR(:,:,[1:numel(muS)],:); 15 | % predR = reshape( tempR ,... 16 | % mcmcparams.infer.nchains*mcmcparams.infer.nsamples,... 17 | % numel(muS),... 18 | % T); 19 | % 20 | % % grab out the L and the R for the interpolated muS values only 21 | % L = dataset.L(:,[1:numel(muS)]); 22 | % 23 | % 24 | % % Calculate model predicted performance - METHOD 2 ----------------------- 25 | % % This method uses the full predictive distribution, using all MCMC samples 26 | % % of predicted responses and compares them to the actual response. 27 | % mcmc_samples = mcmcparams.infer.nchains*mcmcparams.infer.nsamples; 28 | % 29 | % % SLOW VERSION ----- 30 | % % % preallocate 31 | % % model_predicted_correct = zeros(T,numel(muS),mcmc_samples); 32 | % % tic 33 | % % for m = 1:mcmc_samples 34 | % % for t=1:T 35 | % % for sl=1:numel(muS) 36 | % % model_predicted_correct(t,sl,m) = ... 37 | % % dataset.L(t,sl) == postpredR(m,sl,t) ; 38 | % % end 39 | % % end 40 | % % end 41 | % % toc 42 | % 43 | % % FAST VERSION ----- 44 | % model_predicted_correct = zeros(T,numel(muS),mcmc_samples); 45 | % %tic 46 | % for t=1:T 47 | % for sl=1:numel(muS) 48 | % model_predicted_correct(t,sl,:) = L(t,sl) == predR(:,sl,t) ; 49 | % end 50 | % end 51 | % %toc 52 | % 53 | % predpc = squeeze( sum(model_predicted_correct,1)) ./ T; 54 | % 55 | % % Calculate the 95% CI in the model predictions for the actual data. The 56 | % % model will give much more specific predictions because the model had 57 | % % access to the trial-to-trial set of actual locations 58 | % fit.CI = prctile(predpc',[5 95]); 59 | 60 | 61 | 62 | % Calcualte errors for knowingL 63 | error = bsxfun(@minus, pc', predictions.knowingL.predpc)' ; 64 | MSE = mean( error(:).^2 ); % summed error over ALL MCMC samples 65 | predictions.knowingL.RMSE = sqrt(MSE); 66 | 67 | % Calcualte errors for notknowingL 68 | error = bsxfun(@minus, pc', predictions.notknowingL.predpc)' ; 69 | MSE = mean( error(:).^2 ); % summed error over ALL MCMC samples 70 | predictions.notknowingL.RMSE = sqrt(MSE); 71 | 72 | 73 | 74 | 75 | 76 | 77 | nsl = size(predictions.knowingL.predpc,1); 78 | 79 | % % Calcualte errors 80 | % error = bsxfun(@minus, pc', predpc)' ; 81 | % fit.MSE = mean( error(:).^2 ); % summed error over ALL MCMC samples 82 | % fit.RMSE = sqrt(fit.MSE); 83 | 84 | %% Compute Likelihood FOR KNOWING L 85 | % p(data|model,parameters) = L(parameters) 86 | 87 | 88 | 89 | for s=1:nsl % loop over signal level 90 | f = hist( predictions.knowingL.predpc(s,:), linspace(0,1,T)); 91 | p = f./sum(f); 92 | actual_correct_responses = k(s); 93 | data_likelihoodS(s) = p(actual_correct_responses); 94 | end 95 | data_likelihoodS; 96 | probOfDataGivenParameters = prod(data_likelihoodS); 97 | % Calculate AIC 98 | predictions.knowingL.likelihood = probOfDataGivenParameters; 99 | predictions.knowingL.AIC = 2*free_params - 2*log( probOfDataGivenParameters ); 100 | 101 | clear f p actual_correct_responses data_likelihoodS probOfDataGivenParameters 102 | 103 | %% Compute for NOT KNOWING L 104 | 105 | for s=1:nsl % loop over signal level 106 | f = hist( predictions.notknowingL.predpc(s,:), linspace(0,1,T)); 107 | p = f./sum(f); 108 | actual_correct_responses = k(s); 109 | data_likelihoodS(s) = p(actual_correct_responses); 110 | end 111 | data_likelihoodS; 112 | probOfDataGivenParameters = prod(data_likelihoodS); 113 | % Calculate AIC 114 | predictions.notknowingL.likelihood = probOfDataGivenParameters; 115 | predictions.notknowingL.AIC = 2*free_params - 2*log( probOfDataGivenParameters ); 116 | 117 | 118 | 119 | 120 | 121 | return -------------------------------------------------------------------------------- /funcs/model2modelpredictions2.m: -------------------------------------------------------------------------------- 1 | function [predictions] = model2modelpredictions2(samples, mcmcparams, data) 2 | % Calcualte the model predictions for a much largner number of si values 3 | % than we have data for. 4 | 5 | 6 | % ******* THIS IS IMPORTANT ******* 7 | % Choice of samples.R and samples.postpredR is important here 8 | 9 | 10 | 11 | 12 | % *** could swap for postpredR and just do all in one go 13 | 14 | 15 | 16 | 17 | totalSlevels =numel(data.si); 18 | 19 | % collapse across chains 20 | % Note we are taking samples from "R" that were supplied as missing values 21 | % in the dataset.R. ie we are not looking at samples of postpredR 22 | tempR = samples.postpredR(:,:,:,:); 23 | predR = reshape( tempR ,... 24 | mcmcparams.infer.nchains*mcmcparams.infer.nsamples,... 25 | totalSlevels,... 26 | data.T); 27 | 28 | % grab out the L and the R for the interpolated si values only 29 | L = data.L(:,:)'; 30 | 31 | 32 | 33 | 34 | % Calculate model predicted performance - METHOD 2 ----------------------- 35 | % This method uses the full predictive distribution, using all MCMC samples 36 | % of predicted responses and compares them to the actual response. 37 | mcmc_samples = mcmcparams.infer.nchains*mcmcparams.infer.nsamples; 38 | 39 | % preallocate 40 | model_predicted_correct = zeros(data.T, totalSlevels, mcmc_samples); 41 | tic 42 | for t=1:data.T 43 | for sl=1:totalSlevels 44 | model_predicted_correct(t,sl,:) = L(t,sl) == predR(:,sl,t) ; 45 | end 46 | end 47 | toc 48 | 49 | 50 | % so the signal levels consist of this vector [si si sii] 51 | % The precited correct for the first si is where we have knowledge of L 52 | % and R on each trial... so very specific predictions 53 | % The second is for model fitting later, for the actual si values used in 54 | % the (simulated) experiment. 55 | % The third chunch sii is a set of interpolated values that we want to 56 | % know the model predictions for. 57 | 58 | 59 | 60 | predk = squeeze( sum(model_predicted_correct(:,[1:numel(data.sioriginal)],:),1)); 61 | predpc = squeeze( sum(model_predicted_correct(:,[1:numel(data.sioriginal)],:),1)) ./ data.T; 62 | predictions.knowingL.predk = predk; 63 | predictions.knowingL.predpc = predpc; 64 | predictions.knowingL.mean = mean(predpc'); 65 | predictions.knowingL.lower = prctile(predpc',5); 66 | predictions.knowingL.upper = prctile(predpc',95); 67 | clear predpc 68 | 69 | predk = squeeze( sum(model_predicted_correct(:,[numel(data.sioriginal)+1:numel(data.sioriginal)*2],:),1)); 70 | predpc = squeeze( sum(model_predicted_correct(:,[numel(data.sioriginal)+1:numel(data.sioriginal)*2],:),1)) ./ data.T; 71 | predictions.notknowingL.predk = predk; 72 | predictions.notknowingL.predpc = predpc; 73 | predictions.notknowingL.mean = mean(predpc'); 74 | predictions.notknowingL.lower = prctile(predpc',5); 75 | predictions.notknowingL.upper = prctile(predpc',95); 76 | clear predpc 77 | 78 | predk = squeeze( sum(model_predicted_correct(:,[numel(data.sioriginal)*2+1:end],:),1)); 79 | predpc = squeeze( sum(model_predicted_correct(:,[numel(data.sioriginal)*2+1:end],:),1)) ./ data.T; 80 | predictions.interp.predk = predk; 81 | predictions.interp.predpc = predpc; 82 | predictions.interp.mean = mean(predpc'); 83 | predictions.interp.lower = prctile(predpc',5); 84 | predictions.interp.upper = prctile(predpc',95); 85 | clear predpc 86 | 87 | return -------------------------------------------------------------------------------- /funcs/model2plot.m: -------------------------------------------------------------------------------- 1 | function model2plot 2 | 3 | display('Loading model 2 data...') 4 | load('~/Dropbox/tempModelOutputs/tempModel2run.mat') 5 | 6 | 7 | %% Plot MCMC chains 8 | figure(1), clf 9 | MCMCdiagnoticsPlot(samples,stats,{'v','lr','b'}) 10 | 11 | temp=cd; 12 | try 13 | latex_fig(12, 6,4) 14 | cd('figs') 15 | hgsave('model2_infer_chains') 16 | export_fig model2_infer_chains -pdf -m1 17 | cd('..') 18 | catch 19 | cd(temp) 20 | end 21 | 22 | 23 | %% Plot Autocorrelations 24 | plotChainAutocorrelationsModel2(samples) 25 | 26 | temp=cd; 27 | try 28 | latex_fig(8, 6, 4) 29 | cd('figs') 30 | export_fig model2autocorrelations -pdf -m1 31 | hgsave('model2autocorrelations') 32 | cd('..') 33 | catch 34 | cd(temp) 35 | end 36 | 37 | 38 | 39 | display('Plotting model 2 data...\n') 40 | 41 | %% Plot in data space 42 | figure(2), clf 43 | % subplot(1,2,1) 44 | % hold on 45 | % h.data = semilogx(data.sioriginal',data.pc,'k.','MarkerSize',24); 46 | 47 | predk = predictions.interp.predk'; 48 | 49 | subplot(1,2,1) 50 | % ===== TEST ===== 51 | clear IM 52 | for n=1:size(predk,2) % loop over signal intensities 53 | [IM(:,n), ~] = hist( predk(:,n) ,[1:1:100] ); 54 | % scale so the max numerical value = 1 55 | IM(:,n) =IM(:,n) / (max(IM(:,n))/100); 56 | end 57 | imXdata = data.sii; 58 | imYdata = [0:1:100]/100; 59 | 60 | pltXdata = data.sioriginal; 61 | pltYdata = data.koriginal ./ data.T; 62 | 63 | log_plot_with_background(IM,... 64 | imXdata , imYdata,... 65 | pltXdata, pltYdata) 66 | 67 | % Plot the 95% CI for the model predictions give the data. These predictions 68 | % are much more specific than the other models. The trial-to-trial basis of 69 | % the model provides it with knowledge of the true target location on every 70 | % trial 71 | % for n=1:numel(si) 72 | % plot([si(n) si(n)],... 73 | % [predictions.knowingL.upper(n) predictions.knowingL.lower(n)],... 74 | % '-','Color',([111 181 227]./255)./3) 75 | % end 76 | % % do that, but as a shaded region 77 | 78 | % plot 95% CI posterior prediction given that we do have knowledge of the 79 | % true signal locations in the actual experiment. In a real situation we 80 | % would indeed know this. 81 | my_errorbarsUL(data.sioriginal,... 82 | predictions.knowingL.upper,... 83 | predictions.knowingL.lower,... 84 | {'Color','r',... 85 | 'LineWidth',4}) 86 | 87 | % my_shaded_errorbar_zone_UL... 88 | % (data.sioriginal,... 89 | % predictions.knowingL.upper, predictions.knowingL.lower,... 90 | % ([111 181 227]./255).*0.6); 91 | 92 | 93 | % xlim([0.009 10]) 94 | % ylim([0.3 1.01]) 95 | % set(gca,'XTick',[0.01 0.1 1, 10]) 96 | % set(gca,'XScale','log') 97 | % 98 | % % PLOT POSTERIOR PREDICTIVE CHECK 99 | % my_shaded_errorbar_zone_UL... 100 | % (data.sii, ... 101 | % predictions.interp.upper, predictions.interp.lower,... 102 | % [111 181 227]./255); 103 | 104 | %title('a.') 105 | xlabel('signal intensity, \Delta\mu') 106 | ylabel('proportion correct') 107 | box off 108 | set(gca,'PlotBoxAspectRatio',[1 1 1]) 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | % %% NEW TYPE OF PLOT FOR POSTERIORS 125 | % 126 | % fs=10; % font size for summary stats 127 | % % ~~~~~~~~~~~~~~~~~~~~~~~ 128 | % subplot(3,2,2) % PLOT POSTERIOR DISTRIBUTION OVER VARIANCE 129 | % % ~~~~~~~~~~~~~~~~~~~~~~~ 130 | % 131 | % % Covert MCMC samples into a kernel density estimate and extract summary 132 | % % stats 133 | % [MAP, xi, p, CI95] = mode_of_samples_1D(samples.v(:), 'positive'); 134 | % 135 | % % plot posterior distribution 136 | % % plot(xi,p,'k-') 137 | % area(xi,p,... 138 | % 'FaceColor', [0.7 0.7 0.7], ... 139 | % 'LineStyle','none') 140 | % % format 141 | % axis tight 142 | % xlim([0 3]) 143 | % title('\sigma^2') 144 | % hline([],data.v) % PLOT TRUE VALUE 145 | % % remove y-axis 146 | % box off 147 | % set(gca,'YTick',[],... 148 | % 'XTick',[0:1:3],... 149 | % 'PlotBoxAspectRatio',[4,1,1]) 150 | % hold on 151 | % 152 | % % Add summary info 153 | % addDistributionSummaryText(MAP, CI95, 'TR', fs) 154 | % % plot 95% CI line 155 | % a=axis; ypos=a(4)*0.1; 156 | % plot(CI95,[ypos ypos],'k-') 157 | % 158 | % 159 | % % ~~~~~~~~~~~~~~~~~~~~~~~ 160 | % subplot(3,2,4) 161 | % % ~~~~~~~~~~~~~~~~~~~~~~~ 162 | % % Covert MCMC samples into a kernel density estimate and extract summary 163 | % % stats 164 | % [MAP, xi, p, CI95] = mode_of_samples_1D(samples.lr(:), 'positive'); 165 | % 166 | % % plot posterior distribution 167 | % % plot(xi,p,'k-') 168 | % area(xi,p,... 169 | % 'FaceColor', [0.7 0.7 0.7], ... 170 | % 'LineStyle','none') 171 | % % format 172 | % axis tight 173 | % xlim([0 0.1]) 174 | % title('\lambda') 175 | % hline([],data.lr) % PLOT TRUE VALUE 176 | % % remove y-axis 177 | % box off 178 | % set(gca,'YTick',[],... 179 | % 'XTick',[0:0.025:0.1],... 180 | % 'PlotBoxAspectRatio',[4,1,1]) 181 | % hold on 182 | % 183 | % % Add summary info 184 | % addDistributionSummaryText(MAP, CI95, 'TR', fs) 185 | % % plot 95% CI line 186 | % a=axis; ypos=a(4)*0.1; 187 | % plot(CI95,[ypos ypos],'k-') 188 | % 189 | % 190 | % 191 | % 192 | % % ~~~~~~~~~~~~~~~~~~~~~~~ 193 | % subplot(3,2,6) % PLOT POSTERIOR OVER BIAS (b) 194 | % % ~~~~~~~~~~~~~~~~~~~~~~~ 195 | % % Covert MCMC samples into a kernel density estimate and extract summary 196 | % % stats 197 | % [MAP, xi, p, CI95] = mode_of_samples_1D(samples.b(:), [-100 100]); 198 | % 199 | % % plot posterior distribution 200 | % % plot(xi,p,'k-') 201 | % area(xi,p,... 202 | % 'FaceColor', [0.7 0.7 0.7], ... 203 | % 'LineStyle','none') 204 | % % format 205 | % axis tight 206 | % 207 | % title('b') 208 | % hline([],data.b) % PLOT TRUE VALUE 209 | % % remove y-axis 210 | % box off 211 | % set(gca,'YTick',[],... 212 | % 'XTick',[-0.5:0.5:0.5],... 213 | % 'XLim',[-0.5 0.5],... 214 | % 'PlotBoxAspectRatio',[4,1,1]) 215 | % hold on 216 | % 217 | % % Add summary info 218 | % addDistributionSummaryText(MAP, CI95, 'TR', fs) 219 | % % plot 95% CI line 220 | % a=axis; ypos=a(4)*0.1; 221 | % plot(CI95,[ypos ypos],'k-') 222 | 223 | % Export ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 224 | figure(2), latex_fig(12, 5, 3) 225 | 226 | % Export in .fig and .pdf 227 | cd('figs') 228 | figure(2), export_fig model2 -pdf -m1 229 | figure(2), hgsave('model2') 230 | cd('..') 231 | 232 | 233 | %% PLOT PARAMETER SPACE 234 | temp=cd; 235 | try 236 | cd('figs') 237 | figure(5), clf 238 | myDensityMatrix(samples.v(:),samples.lr(:),samples.b(:),... 239 | {'\sigma^2','\lambda','b'},... 240 | [data.v, data.lr, data.b],... 241 | {'positive','positive',[-100 100]}) 242 | 243 | figure(5), hgsave('model2paramMatrix') 244 | figure(5), export_fig model2paramMatrix -pdf -m1 245 | 246 | cd('..') 247 | catch 248 | cd(temp) 249 | end 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /funcs/model3generate.m: -------------------------------------------------------------------------------- 1 | function [params] = model3generate(params, mcmcparams) 2 | 3 | 4 | %% Pleliminaries 5 | N = 2; % DO NOT CHANGE 6 | 7 | % % If the external noise variance is set to zero, then we need to adjust 8 | % % this to be a very small number. If it is exactly zero, then we get divide 9 | % % by zero errors in JAGS. 10 | % if params.varext==0 11 | % params.varext=10^-20; 12 | % end 13 | 14 | 15 | %% Definine observed data 16 | 17 | TRIALS_SIMULATED = params.T; 18 | % simulate 1 trial, but generate many MCMC samples, see below. But due to 19 | % some quirk of matjags(?) there are problems when evaluating just 1 trial, 20 | % so we will simulate 2 and then discard this. 21 | params.T = 2; 22 | 23 | 24 | %% 25 | % Set initial values for latent variable in each chain 26 | clear initial_param 27 | for i=1:mcmcparams.generate.nchains 28 | for s=1:numel(params.si) 29 | for t=1:params.T 30 | initial_param(i).L(s,t) = round( ( rand*(N-1)) +1); 31 | end 32 | end 33 | end 34 | 35 | %% Do the MCMC sampling by invoking JAGS 36 | fprintf('Generating dataset of x and L.\n') 37 | fprintf('\nJAGS: running %d chains, each with %d MCMC samples...\n',... 38 | mcmcparams.generate.nchains, mcmcparams.generate.nsamples); 39 | tic 40 | [samples, stats, structArray] = matjags( ... 41 | params, ... 42 | fullfile(pwd, mcmcparams.JAGSmodel), ... 43 | initial_param, ... 44 | 'doparallel' , mcmcparams.doparallel, ... 45 | 'nchains', mcmcparams.generate.nchains,... 46 | 'nburnin', mcmcparams.generate.nburnin,... 47 | 'nsamples', mcmcparams.generate.nsamples, ... 48 | 'thin', 1, ... 49 | 'monitorparams', {'L','x'}, ... 50 | 'savejagsoutput' , 1 , ... 51 | 'verbosity' , 1 , ... 52 | 'cleanup' , 0 ,... 53 | 'rndseed', 1,... 54 | 'dic',0); 55 | min_sec(toc); 56 | 57 | clear initial_param 58 | 59 | %% Gather data we need in correct form 60 | % We wanted to create our dataset by only simulating a single trial, but we 61 | % gather our data over multiple trials from different MCMC samples. There 62 | % seems to be a limitation in JAGS where you cannot evaluate only 1 63 | % trial... seems to be due to size of the arrays etc. So we need to extract 64 | % the information we are after now 65 | params.L = squeeze(samples.L(1,:,:,1)); 66 | params.x = squeeze(samples.x(1,:,:,:,1)); 67 | 68 | params.T = TRIALS_SIMULATED; 69 | 70 | % reshape dataset.x into form needed later 71 | for t=1:params.T 72 | for s=1:numel(params.si) 73 | temp(s,1,t) = params.x(t,s,1) ; 74 | temp(s,2,t) = params.x(t,s,2) ; 75 | end 76 | end 77 | params.x = temp; 78 | 79 | %% 80 | % Now we have out simulated dataset, we can calculate the proportion of 81 | % correct responses per signal level 82 | 83 | return -------------------------------------------------------------------------------- /funcs/model3infer.m: -------------------------------------------------------------------------------- 1 | %% model2infer.m 2 | % Use MCMC to infer the posterior distribution $P(L|parameters)$ 3 | %% 4 | 5 | function [samples,stats, PC, PCI, R, k] = model3infer(params, mcmcparams) 6 | 7 | %% Preliminaries 8 | N=2; % DO NOT CHANGE 9 | 10 | % create a structure of knowns by the observer 11 | knowns = params; 12 | %% 13 | % Double check we have removed knowledge of the internal variance as this 14 | % is what we are trying to estimate 15 | try 16 | knowns = rmfield(knowns,'L'); 17 | catch 18 | % the field was already removed 19 | end 20 | %% 21 | % Set initial values for latent variable in each chain 22 | clear initial_param 23 | for i=1:mcmcparams.infer.nchains 24 | initial_param(i).L = round( (rand(numel(knowns.si), knowns.T)*(N-1)) +1); 25 | end 26 | 27 | 28 | %% Invoke JAGS to generate samples 29 | fprintf('Inferring signal locations\n') 30 | fprintf('\nJAGS: running %d chains, each with %d MCMC samples...\n',... 31 | mcmcparams.infer.nchains, mcmcparams.infer.nsamples); 32 | tic 33 | [samples, stats] = matjags( ... 34 | knowns, ... 35 | fullfile(pwd, mcmcparams.JAGSmodel), ... 36 | initial_param, ... 37 | 'doparallel' , mcmcparams.doparallel, ... 38 | 'nchains', mcmcparams.infer.nchains,... 39 | 'nburnin', mcmcparams.infer.nburnin,... 40 | 'nsamples', mcmcparams.infer.nsamples, ... 41 | 'thin', 1, ... 42 | 'monitorparams', {'L'}, ... 43 | 'savejagsoutput' , 1 , ... 44 | 'verbosity' , 1 , ... 45 | 'cleanup' , 0 ,... 46 | 'rndseed', 1); 47 | min_sec(toc); 48 | 49 | 50 | 51 | 52 | %% Decision rule 53 | % The MCMC samples of L are the observer's posterior predictive 54 | % distribution over the signal location. For each trial, we exact this 55 | % distribution and calculate their response as the most likely location, 56 | % which corresponds to the MAP decision rule. 57 | 58 | R=zeros(params.T,numel(params.si)); % preallocate matrix 59 | for t=1:params.T 60 | for s=1:numel(params.si) 61 | % extract all the samples from all the chains, for this trial (t) 62 | L_temp = vec(samples.L(:,:,s,t)); 63 | % samples are {1,2} so we want to calculate the most likely 64 | % location of the signal, so we can use the mode function. 65 | R(t,s) = mode( L_temp ); 66 | end 67 | end 68 | 69 | %% 70 | % The matrix |R| now holds the response data. 71 | 72 | %% Evaluate the performance of the optimal observer 73 | % On how many trials did the optimal observer make the correct inference 74 | % (and response) about the correct location of the target. We know the 75 | % correct location from our simulated dataset in step 1. 76 | k=zeros(numel(params.si),1); % preallocate 77 | for s=1:numel(params.si) 78 | k(s) = sum( R(:,s)==params.L(:,s) ); 79 | end 80 | 81 | %% 82 | % Calculate the percent correct, and the 95% credibility intervals of the 83 | % percent correct, given the number of trials we ran. 84 | [PC, PCI] = binofit(k,params.T); 85 | 86 | return -------------------------------------------------------------------------------- /funcs/model3nonMCMC.m: -------------------------------------------------------------------------------- 1 | function [PC, k] = model3nonMCMC(variance, muS, T, dPrior) 2 | 3 | %Provided as input arguments 4 | %T=50000; % Trials per signal level 5 | %sigma = 0.8; 6 | %muS = logspace(-2,1,40); 7 | % prior over signal location 8 | % dPrior = [0.5 0.5]; 9 | 10 | % convert variance parameter to std (sigma) 11 | sigma = sqrt(variance); 12 | 13 | C = numel(muS); 14 | N = 2; % 2AFC 15 | true_spatial_prior = [0.5 0.5]; 16 | % initialse number of correct trials 17 | k = zeros(C,1); 18 | 19 | % deterministic p(xmu|L), to be modified later for each signal intensity 20 | % condition. muN is assumed to equal zero. 21 | xMu = eye(N); 22 | 23 | %fprintf('Evaluating model 3... ') 24 | tic 25 | parfor t=1:T % loop over trials (using multiple cores) 26 | 27 | %% STEP 1: GENERATIVE 28 | % Sample signal locations (for all signal intensity conditions). This 29 | % will be a Cx10 size binary matrix indicating signal location on each 30 | % trial. 31 | 32 | l = mnrnd(1,true_spatial_prior,C); 33 | % Calculate means of observations: mean signal is equal to the muS for 34 | % that signal intensity condition, mean noise is zero. 35 | mu = bsxfun(@times,l,muS'); 36 | % generate noisy observations 37 | x = normrnd( mu, sigma); 38 | 39 | 40 | %% STEP 2: INFERENCE 41 | Post=zeros(C,N); 42 | 43 | mu_if_signal_in_location_1 = [muS', zeros(size(muS'))]; 44 | n=1; 45 | Post(:,n) = prod( normpdf(x, mu_if_signal_in_location_1 , sigma) ,2); 46 | Post(:,n) = Post(:,n) .* dPrior(n); 47 | 48 | mu_if_signal_in_location_2 = [zeros(size(muS')), muS']; 49 | n=2; 50 | Post(:,n) = prod( normpdf(x, mu_if_signal_in_location_2 , sigma) ,2); 51 | Post(:,n) = Post(:,n) .* dPrior(n); 52 | 53 | %% STEP 3: RESPONSE 54 | % for each location, response will be either 1 or 2 (indexing the 55 | % inferred signal location) 56 | response=zeros(1,C); 57 | response( Post(:,1) > Post(:,2) ) = 1; 58 | response( Post(:,1) < Post(:,2) ) = 2; 59 | 60 | % see if the response location is correct 61 | [~,true_location] = max(l,[],2); 62 | iscorrect = true_location==response'; 63 | 64 | % update vector of correct responses over easch muS value 65 | k= k+iscorrect; 66 | 67 | 68 | %% Below is my initially coded, non-vectorised, thus slow code. 69 | % for c=1:C % loop over muS conditions 70 | % 71 | % % %% STEP 1: GENERATIVE 72 | % % % sample signal location from prior 73 | % % l = mnrnd(1,dPrior); 74 | % 75 | % % sample noisy observation, with observation mean dependent upon 76 | % % the signal location 77 | % %x = normrnd( l(c,:)*muS(c), sigma); 78 | % 79 | % %% step 2: INFERENCE, now we know x 80 | % % This portion of the code model's the observer's inferences about 81 | % % the signal being in each of the two locations. The joint 82 | % % posterior of this unknown parameter value is evaluated for each 83 | % % location, so it is a simple grid approximation, with just 2 84 | % % values of the location parameter. 85 | % for n=1:N % loop over spatial locations 86 | % % log likelihood of each value of L (each location) 87 | % LLd(n) = sum( log( normpdf(x(c,:), xMu(n,:)*muS(c) , sigma) )); 88 | % end 89 | % logPosteriorD = LLd + log(dPrior); % posterior 90 | % 91 | % %% STEP 3: DECISION 92 | % response = argmax(logPosteriorD); 93 | % if response == argmax(l) 94 | % k(c) = k(c) + 1; 95 | % end 96 | 97 | end 98 | 99 | fprintf(' %2.1f simulations per second\n',T/toc) 100 | %fprintf('%f u sec per iteration\n',(toc/T)*1000) 101 | min_sec(toc); 102 | fprintf('.\n') 103 | 104 | PC = k./T; 105 | 106 | return -------------------------------------------------------------------------------- /funcs/model3plot.m: -------------------------------------------------------------------------------- 1 | function model3plot 2 | plot_formatting_setup 3 | 4 | load('~/Dropbox/tempModelOutputs/tempModel3run.mat') 5 | % temp=cd; 6 | % try 7 | % cd('~/Dropbox/tempModelOutputs') 8 | % load tempModel3run.mat 9 | % cd(temp) 10 | % catch 11 | % 12 | % end 13 | 14 | 15 | 16 | 17 | %% Plot results in data space 18 | % Plot the simulated behaviour data alongside model predictions 19 | 20 | figure(1),clf 21 | subplot(1,2,1) 22 | % % plot simulated data 23 | % semilogx(data.sioriginal, data.koriginal ./ data.T,'k.','MarkerSize',24); 24 | % hold on 25 | % 26 | % % plot model predictions 27 | % my_shaded_errorbar_zone_UL... 28 | % (data.sii,CI(2,:),CI(1,:),[111 181 227]./255); 29 | % set(gca,'XScale','log') 30 | 31 | % ===== TEST ===== 32 | for n=1:size(predk,2) 33 | IM(:,n) = hist( predk(:,n) ,[1:1:100] ); 34 | % scale so the max numerical value = 100 35 | IM(:,n) =IM(:,n) / (max(IM(:,n))/100); 36 | end 37 | imXdata = data.sii; 38 | imYdata = [0:1:100]/100; 39 | 40 | pltXdata = data.sioriginal; 41 | pltYdata = data.koriginal ./ data.T; 42 | 43 | log_plot_with_background(IM,... 44 | imXdata , imYdata,... 45 | pltXdata, pltYdata) 46 | 47 | 48 | % formatting 49 | set(gca,'XScale','log',... 50 | 'PlotBoxAspectRatio',[1 1 1],... 51 | 'box', 'off',... 52 | 'xlim', [0.009 10],... 53 | 'ylim', [0.3 1.01],... 54 | 'XTick',[0.01 0.1 1, 10],... 55 | 'YTick',[0:0.1:1]) 56 | xlabel('signal level \mu_S') 57 | ylabel('proportion correct') 58 | title('a.') 59 | 60 | 61 | %% Plot the inferences in parameter space 62 | % Plot the posterior distribution over $\sigma^2$ 63 | 64 | subplot(1,2,2) 65 | % plot posterior distribtion 66 | %plot(varianceGridVals,posterior_var,'k-') 67 | area(varianceGridVals,posterior_var,... 68 | 'FaceColor', [0.7 0.7 0.7], ... 69 | 'LineStyle','none') 70 | axis tight 71 | hline([],data.v) 72 | % plot 95% HDI 73 | hold on, a=axis; top =a(4); z=0.03; 74 | plot([HDI.lower HDI.upper],[top*z top*z],'k-'); 75 | % format graph 76 | xlabel('inferred \sigma^2') 77 | ylabel('posterior density') 78 | set(gca, 'PlotBoxAspectRatio',[1 1 1],... 79 | 'box', 'off',... 80 | 'yticklabel',{},... 81 | 'YTick',[],... 82 | 'xlim', [0 3]) 83 | title('b.') 84 | % Add summary info 85 | addDistributionSummaryText(vMode, [HDI.lower HDI.upper], 'TR', 12) 86 | 87 | 88 | %% Export 89 | figure(1) 90 | % Automatic resizing to make figure appropriate for font size 91 | latex_fig(12, 5, 3) 92 | 93 | % Export in .fig and .pdf 94 | cd('figs') 95 | hgsave('model3') 96 | export_fig model3 -pdf -m1 97 | cd('..') 98 | -------------------------------------------------------------------------------- /funcs/model3psychometric.m: -------------------------------------------------------------------------------- 1 | function model3psychometric(TASK) 2 | % model3psychometric('calculate') 3 | % model3psychometric('plot') 4 | 5 | %% model3psychometric.m 6 | % 7 | % Future updates or big fixes will appeclear, close all; clc % First, tidy thigs up 8 | addpath([cd '/funcs']) 9 | addpath([cd '/funcs/export_fig']) 10 | 11 | 12 | switch TASK 13 | case{'calculate'} 14 | 15 | data.si = logspace(-2,1,10); % define the signal intensities 16 | data.varint = 1; % internal noise variance 17 | data.pdist = [0.5 0.5]; 18 | 19 | % Define MCMC parameters 20 | mcmcparams = define_mcmcparams('model3'); 21 | data.T = mcmcparams.generate.nsamples; % TRIALS TO SIMULATE 22 | %% 23 | % WARNING: This is a relatively complex model, and simulating more than 24 | % about 1000 trials per stimulus level will result in quite long 25 | % computation time. 26 | 27 | 28 | %% Generate simulated dataset 29 | % Generate samples of L and x. These samples are generated with a uniform 30 | % probability of signals occuring in each of N=2 locations. 31 | 32 | [data] = model3generate(data, mcmcparams); 33 | % 34 | % and/or 35 | % 36 | %% 37 | 38 | %% Define initial parameters 39 | % Feel free to experiment with altering the following parameters. For the 40 | % paper I used a relatively high number of trials per stimulus level (|T=2000|), 41 | % but this does start to take quite a long time to compute. So for learning 42 | % or testing purposes I set it lower, e.g. |T=100|. 43 | 44 | 45 | 46 | 47 | %% Inferences, for unbiased observer 48 | % Inferences are made from the dataset generated above. This observer 49 | % is unbiased in that it expects signals to occur in each location with 50 | % equal probability. 51 | tic 52 | data.pdist=[0.5 0.5]; 53 | [samples,stats, PCunbiased, PCIm1, R, k] = model3infer(data, mcmcparams); 54 | toc 55 | 56 | %% Inferences, for biased observer 57 | % Inferences are again made from the dataset generated above. This observer 58 | % is biased, and assumes there is a 75% change of signals occuring in 59 | % location 1. 60 | 61 | data.pdist=[0.75 0.25]; 62 | [samples,stats, PCbiased, PCIm2, R, k] = model3infer(data, mcmcparams); 63 | 64 | 65 | 66 | %% Calculate psychometric functions for model 3, but for MANY simulated trials 67 | % MCMC approach is not workable for more than a few thousand simulated 68 | % trials, so I'll calculate it using non-MCMC methods 69 | nSimulatedTrials = 10^6; 70 | 71 | si = logspace(-2,1,100); 72 | % unbiased observer 73 | dPrior=[0.5 0.5]; 74 | pc_unbiased = model3nonMCMC(data.varint, si,... 75 | nSimulatedTrials, dPrior); 76 | % biased observer 77 | dPrior=[0.75 0.25]; 78 | pc_biased = model3nonMCMC(data.varint, si,... 79 | nSimulatedTrials, dPrior); 80 | 81 | %% SAVE 82 | save(['~/Dropbox/tempModelOutputs/' 'tempModel3psychometric'], '-v7.3') 83 | 84 | 85 | case{'plot'} 86 | 87 | % load data 88 | try 89 | load(['~/Dropbox/tempModelOutputs/' 'tempModel3psychometric.mat']) 90 | catch 91 | 92 | end 93 | 94 | %% 95 | % Plot the MCMC-derived performances 96 | figure(1), clf 97 | 98 | % plot psychometric functions 99 | semilogx(si,pc_unbiased,'k-') 100 | hold on 101 | semilogx(si,pc_biased,'k--') 102 | 103 | 104 | [h.m1]=semilogx(data.si',PCunbiased,'ko','MarkerSize',8,... 105 | 'MarkerFaceColor','k'); 106 | hold on 107 | [h.m2]=semilogx(data.si',PCbiased,'ks','MarkerSize',8,... 108 | 'MarkerFaceColor','w'); 109 | xlabel('signal intensity, \Delta\mu') 110 | ylabel('proportion correct') 111 | 112 | % Formatting of the figure 113 | box off 114 | set(gca,'XScale','log',... 115 | 'PlotBoxAspectRatio',[1.5 1 1]) 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | %% Export 126 | latex_fig(12, 3, 3) 127 | 128 | % Export in .fig and .pdf 129 | cd('figs') 130 | hgsave('model3psychometric') 131 | export_fig model3psychometric -pdf -m1 132 | cd('..') 133 | 134 | end 135 | 136 | end 137 | -------------------------------------------------------------------------------- /funcs/pcfunc.m: -------------------------------------------------------------------------------- 1 | function pc = pcfunc(si,variance) 2 | % This function calculates the percent correct given a value (or a vector 3 | % of values) of signal intesities (si) and a variance. 4 | 5 | pc = normcdf( (si) ./ sqrt(2*variance) ); 6 | 7 | return -------------------------------------------------------------------------------- /funcs/sampleStats.m: -------------------------------------------------------------------------------- 1 | function [estimated_mode, XI, p, ci95] = sampleStats(samples, support) 2 | 3 | % Compute the kernel density estimate based on MCMC samples 4 | [F,XI]=ksdensity(samples(:),... 5 | 'support', support,... 6 | 'npoints',1000); 7 | 8 | % now calculate the mode 9 | [~,index]=max(F); 10 | estimated_mode = XI(index); 11 | 12 | % normalise 13 | p=F./sum(F); 14 | 15 | ci95 = prctile(samples,[5 95]); 16 | 17 | fprintf('mode=%2.3f (%2.3f - %2.3f)\n',... 18 | estimated_mode, ci95(1), ci95(2)) 19 | 20 | return -------------------------------------------------------------------------------- /funcs/sanityChecks/sanityCheck2AFC.m: -------------------------------------------------------------------------------- 1 | function sanityCheck2AFC(sigma) 2 | % 3 | % sanityCheck2AFC(0.75) 4 | % 5 | 6 | % Double, triple checking SDT equations are correct by comparing analytical 7 | % and monte carlo 8 | 9 | 10 | 11 | figure(1) 12 | clf 13 | 14 | set(0,'DefaultTextInterpreter', 'latex') 15 | 16 | c1=[1 0 0] 17 | c2=[0 0 1] 18 | 19 | K = 5; % axis lim 20 | fs=12; 21 | 22 | muS=logspace(-2,2,50); 23 | muN=0; 24 | % sigma = std, sigma^2 = variance 25 | 26 | 27 | 28 | C = 0; % unbiased criterion 29 | 30 | 31 | %% Analytically 32 | 33 | pc = @(mus,sigma) normcdf( mus ./ sqrt(2*(sigma^2)) ); 34 | 35 | pcA = pc( muS(:), sigma ); 36 | 37 | % for n=1:numel(muS) 38 | % %pcA(n) = pc( dp(muS(n),muN,sigma) ); 39 | % pcA(n) = pc( muS(n), sigma ); 40 | % end 41 | 42 | subplot(1,2,1) 43 | semilogx(muS,pcA) 44 | hold on 45 | 46 | 47 | %% Monte Carlo 48 | 49 | TRIALS=100000; 50 | 51 | 52 | for n=1:numel(muS) 53 | XS = normrnd(muS(n),sigma,[TRIALS 1]); 54 | XN = normrnd(muN,sigma,[TRIALS 1]); 55 | D = XS-XN; 56 | 57 | varD(n) = var(D); 58 | 59 | pcB(n) = sum(D>0)/TRIALS; 60 | end 61 | plot(muS,pcB,'ro') 62 | 63 | subplot(1,2,2) 64 | hist(varD) 65 | 2*(sigma^2) 66 | 67 | hline([], 2*(sigma^2)) 68 | title('variance of D=XS-XN') 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /jagsmodels/model1JAGS.txt: -------------------------------------------------------------------------------- 1 | model{ 2 | v ~ dunif(0,1000) 3 | for (c in 1:length(si)) { 4 | pc[c] <- phi( (si[c])/( sqrt(2*v) ) ) 5 | k[c] ~ dbin(pc[c],T) 6 | predk[c] ~ dbin(pc[c],T) 7 | } 8 | } -------------------------------------------------------------------------------- /jagsmodels/model2JAGS.txt: -------------------------------------------------------------------------------- 1 | model{ 2 | 3 | bprior ~ dnorm(0,1/2000) # sampling from prior distribution 4 | b ~ dnorm(0,1/2000) # prior over decision bias 5 | v ~ dunif(0,1000) # prior over internal variance 6 | lr ~ dbeta(1,1) # uniform prior over lapse rate 7 | 8 | for (s in 1:length(si)){ 9 | for (t in 1:T){ 10 | 11 | L[s,t] ~ dcat(pdist) # prior over signal location 12 | 13 | # location 1 14 | tempMu[s,1,t] <- ifelse(1==L[s,t],si[s],0) 15 | x[s,1,t] ~ dnorm(tempMu[s,1,t] , 1/v) 16 | 17 | # location 2 18 | tempMu[s,2,t] <- ifelse(2==L[s,t],si[s],0) 19 | x[s,2,t] ~ dnorm(tempMu[s,2,t] , 1/v) 20 | 21 | m[s,1,t] <- ifelse(x[s,1,t]-x[s,2,t] > b, 1-(lr/2), (lr/2)) 22 | m[s,2,t] <- ifelse(x[s,1,t]-x[s,2,t] > b, (lr/2), 1-(lr/2)) 23 | 24 | R[s,t] ~ dcat(m[s,,t]) 25 | postpredR[s,t] ~ dcat(m[s,,t]) 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /jagsmodels/model3JAGS.txt: -------------------------------------------------------------------------------- 1 | model{ 2 | v ~ dunif(0,1000) 3 | for (c in 1:length(si)){ 4 | for (t in 1:T){ 5 | L[c,t] ~ dcat(pdist) 6 | for (n in 1:2){ 7 | x[c,n,t] ~ dnorm( ifelse(n==L[c,t],si[c],0) , 1/v) 8 | } 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /model1runme.m: -------------------------------------------------------------------------------- 1 | function model1runme(PARAM_RECOVERY_METHOD) 2 | % This code implements Model 1 in 3 differnet ways. 3 | % 4 | % model1runme('gridApprox') 5 | % model1runme('mcmcCustom') 6 | % model1runme('mcmcJAGS') 7 | % 8 | % For each method, the same basic steps are undertaken: 9 | % Step 1: load dataset, OR generate new data 10 | % Step 2: Conduct parameter recovery 11 | % Step 3: Calculate the model's predictions. What is the models predictive 12 | % distribution over the data, given the parmeter distributions inferred 13 | % given the data in step 1. 14 | % 15 | % Grid Approximation, and the custom MCMC implementation both use the 16 | % function m1jointPosterior.m for the parameter estimation, to evaluate 17 | % the joint probability. 18 | % They also both use m1posteriorPrediction.m in order to calculate the 19 | % posterior distribution 20 | 21 | setup 22 | 23 | DATASET_MODE='load' % {'load','generate'} 24 | PARAM_RECOVERY_METHOD 25 | 26 | %% STEP 1: SIMULATE DATASET. Either load or create a new dataset 27 | switch DATASET_MODE 28 | case{'load'} 29 | load('data/commondata_model1.mat') 30 | 31 | % The fields of data, for commondata_model1.mat are: 32 | % - sioriginal = stimulus intensities (there are 10) 33 | % - koriginal = counts, 1, 2, 3, ? T (there are 10, corresponding to the responses for each stimulus intensity) 34 | % - T = trials = 100 35 | % - the other variables (sii, si, k) also correspond to counts and stimulus intensities, but now we are interpolating more values in between the actual 10 36 | 37 | case{'generate'} 38 | % define known variables 39 | data = define_experiment_params('model1'); 40 | 41 | % GENERATE SIMULATED DATA: sample from P(k|T,si,variance) 42 | data.k = m1posteriorPrediction(data.T, data.sioriginal,... 43 | data.v); 44 | end 45 | 46 | 47 | 48 | %% STEP 2: Parameter Recovery 49 | % Evaluate likelihood over range of variance values. Then obtain posterior 50 | % by combining with a uniform prior. 51 | switch PARAM_RECOVERY_METHOD 52 | case{'gridApprox'} 53 | % 1. Define estimation options 54 | estOpts.V = linspace(10^-2, 3, 10^4)'; % range of variance values 55 | 56 | % 2. Conduct the inference 57 | [posterior_var,vMode,HDI] = m1InferGridApprox(estOpts,data); 58 | 59 | case{'mcmcCustom'} 60 | % 1. Define estimation options 61 | estOpts.initial_variance = 0.1; % initial guess 62 | estOpts.n_samples = 100000; 63 | estOpts.proposalstd = 0.1; 64 | estOpts.pdf = @m1jointPosterior; 65 | 66 | % 2. Conduct the inference 67 | [samples] = mhAlgorithm(estOpts,... 68 | data.sioriginal, data.koriginal, data.T); 69 | 70 | % Calc summary stats 71 | [MAP, xi, p, CI95] = sampleStats(samples, 'positive'); 72 | 73 | case{'mcmcJAGS'} 74 | 75 | % 1. Define estimation options 76 | mcmcparams = define_mcmcparams('model1'); 77 | starting_var = [0.1 1 10 100]; 78 | mcmcparams.infer.nchains = numel(starting_var); 79 | 80 | [samples, stats] = m1inferJAGS(data, starting_var, mcmcparams); 81 | 82 | % Calc summary stats 83 | [MAP, xi, p, CI95] = sampleStats(samples.v(:), 'positive'); 84 | end 85 | 86 | 87 | 88 | %% STEP 3: PREDICTIVE DISTRIBUTION 89 | % Generate a set of predictions for many signal intensity levels, beyond 90 | % that which we have data for (sii). Useful for visualising the model's 91 | % predictions. 92 | 93 | switch PARAM_RECOVERY_METHOD 94 | case{'gridApprox'} 95 | % Drawing MANY samples from the posterior distribution of internal 96 | % variance * REQUIRES STATISTICS TOOLBOX * 97 | nsamples=10^5; 98 | var_samples = randsample(estOpts.V, nsamples, true, posterior_var); 99 | 100 | % predictive distribution 101 | predk=zeros(nsamples,numel(data.sii)); % preallocate 102 | for n=1:nsamples 103 | predk(n,:) = m1posteriorPrediction(data.T, data.sii, var_samples(n)); 104 | end 105 | 106 | % Calculate 95% CI's for each signal level 107 | CI = prctile(predk,[5 95]) ./ data.T; 108 | 109 | case{'mcmcCustom'} 110 | predk=zeros(estOpts.n_samples,numel(data.sii)); % preallocate 111 | for n=1:numel(samples) 112 | predk(n,:) = m1posteriorPrediction(data.T, data.sii, samples(n)); 113 | end 114 | 115 | case{'mcmcJAGS'} 116 | 117 | % Generation of the predictive posterior values of K was already 118 | % done in Step 2. 119 | % 120 | % JAGS is providing samples of predicted number of correct trials out of T. 121 | % Concatenate all the samples from different MCMC chains into one long list 122 | % of MCMC samples 123 | predk = reshape( samples.predk ,... 124 | mcmcparams.infer.nchains*mcmcparams.infer.nsamples,... 125 | numel([data.sioriginal data.sii])); 126 | 127 | predk = predk(:,numel(data.sioriginal)+1:end); 128 | 129 | end 130 | 131 | 132 | %% SAVE 133 | switch PARAM_RECOVERY_METHOD 134 | case{'gridApprox'} 135 | save('output/m1MAPestimate.mat', 'vMode') 136 | save(['~/Dropbox/tempModelOutputs/tempModel1run_gridApprox.mat'], '-v7.3') 137 | 138 | case{'mcmcCustom'} 139 | save(['~/Dropbox/tempModelOutputs/tempModel1run_mcmcCustom.mat'], '-v7.3') 140 | 141 | case{'mcmcJAGS'} 142 | save(['~/Dropbox/tempModelOutputs/tempModel1run_mcmcJAGS.mat'], '-v7.3') 143 | end 144 | 145 | 146 | return -------------------------------------------------------------------------------- /model2runme.m: -------------------------------------------------------------------------------- 1 | %% model2runme.m 2 | 3 | 4 | 5 | DATAMODE = 'load'; % {'load'|'generate'} 6 | 7 | %% Step 1: Either Load common dataset, or Generate simulated dataset 8 | switch DATAMODE 9 | case{'load'} 10 | % Load file containing experiment parameters, and a pre-computed 11 | % dataset of locations (L) and responses (R). 12 | load('data/commondata_model2.mat') 13 | % Define MCMC parameters 14 | mcmcparams = define_mcmcparams('model2',data.T); 15 | 16 | case{'generate'} 17 | data = define_experiment_params('model2'); 18 | % Define MCMC parameters 19 | mcmcparams = define_mcmcparams('model2',data.T); 20 | % Step 1: Generate simulated dataset 21 | [data] = model2generate(data, mcmcparams); 22 | end 23 | 24 | 25 | %% Step 2: Experimenters' inferences 26 | timerStartOfModel2infer = tic; 27 | [samples,stats] = model2infer(data, mcmcparams); 28 | display('inferences done') 29 | min_sec(toc(timerStartOfModel2infer)); 30 | 31 | 32 | %% POSTERIOR PREDICTION for interpolated si values 33 | display('starting model predictions') 34 | [predictions] = model2modelpredictions2(samples, mcmcparams, data); 35 | 36 | 37 | %% SAVE 38 | save(['~/Dropbox/tempModelOutputs/tempModel2run.mat'], '-v7.3') 39 | 40 | 41 | %% RUN MODEL 2 FIGURE GENERATION AND EXPORTING CODE 42 | %model2plot 43 | % ------------------------------------------------- 44 | -------------------------------------------------------------------------------- /model3runme.m: -------------------------------------------------------------------------------- 1 | function model3runme 2 | 3 | 4 | %% set some simulation parameters 5 | % Parameters for grid approximation (parameter estimation) 6 | nGridValues = 50; 7 | varianceGridVals = linspace(0.25, 2, nGridValues)'; 8 | 9 | % the larger this number, the more accurately we are assessing the 10 | % performance of the optimal observer in the limit of many many trials. 11 | paramest.nSimulatedTrials = 10^6; % 10^6 minimum for reliable results 12 | 13 | % Because we are drawing many samples, we run the model each time, so this 14 | % takes a long time to compute! So we will drop down the number of 15 | % simulated trials when evaluating the predictive distribution. 16 | predictive.nSimulatedTrials = 10^5; % 10^5 17 | % Number of samples from the predictive distribtion to draw, each of which 18 | % will result in a predicted psychometric function 19 | predictive.nSamples = 10^3; %10^3 20 | 21 | %% 22 | % open up multiple cores for use 23 | %parpool 24 | 25 | DATAMODE = 'load'; % {'load'|'generate'} 26 | 27 | switch DATAMODE 28 | case{'load'} 29 | % Load file containing experiment parameters, and a pre-computed 30 | % dataset of locations (L) and responses (R). 31 | load('data/commondata_model3.mat') 32 | 33 | end 34 | 35 | display('data loaded') 36 | 37 | %% Define anonymous functions 38 | Lk = @(k,T,pc) binopdf(k , T, pc); 39 | LkTotal = @(L) exp( sum( log(L) ) ); 40 | % % use the SDT equation below to check the accuracy of the Monte Carlo 41 | % % calculations 42 | % pcfunc = @(si,variance) normcdf( si ./ sqrt(2*variance) ); 43 | 44 | 45 | %% Grid approximation: loop over many variance values 46 | dPrior = [0.5 0.5]; % assume an unbiased observer 47 | likelihood=zeros(size(varianceGridVals)); % preallocate 48 | figure(1), clf 49 | for n=1:numel(varianceGridVals) 50 | fprintf('%d of %d',n,numel(varianceGridVals)) 51 | 52 | % Calculate PC over different si values, for this variance parameter 53 | % value 54 | % -------------------------------------------------- 55 | pc = model3nonMCMC(varianceGridVals(n), data.sioriginal,... 56 | paramest.nSimulatedTrials, dPrior); 57 | % -------------------------------------------------- 58 | 59 | % plotting 60 | figure(2) 61 | semilogx(data.sioriginal,pc','k-') 62 | hold on 63 | semilogx(data.sioriginal,... 64 | pcfunc( data.sioriginal, varianceGridVals(n) ),... 65 | 'r:') 66 | 67 | legend('monte carlo','sdt') 68 | 69 | xlabel('signal intensity') 70 | ylabel('k/T') 71 | hold on 72 | % plot data 73 | plot(data.sioriginal, data.koriginal./data.T, 'ko') 74 | hold off 75 | drawnow 76 | 77 | 78 | 79 | % Calculate likelihood 80 | L = Lk(data.koriginal, data.T, pc' ); 81 | %L =L(L~=0) %<------ this was causing problems. Kill it. 82 | likelihood(n) = LkTotal( L ); 83 | 84 | 85 | figure(1), hold off 86 | %plot(varianceGridVals,likelihood) 87 | area(varianceGridVals,likelihood,... 88 | 'FaceColor', [0.7 0.7 0.7], ... 89 | 'LineStyle','none') 90 | xlabel('\sigma^2') 91 | ylabel('likelihood') 92 | drawnow 93 | 94 | 95 | fprintf(' done\n') 96 | end 97 | 98 | %% 99 | % Calculate the posterior. We will use a uniform prior distribution over 100 | % the range of variance values examined. This could be evaluated as 101 | % follows: 102 | %% 103 | % 104 | % prior = ones(numel(varianceGridVals),1)./numel(varianceGridVals); 105 | % posterior_var = likelihood.*prior; 106 | % posterior_var = posterior_var ./sum(posterior_var); 107 | % 108 | % But it's simpler to simply normalise the likelihood... 109 | 110 | prior_over_k = 1/data.T; % discreet uniform prior over k. 111 | prior_over_sigma2 = 1/1000; % continuous uniform prior (range 0-1000) over sigma2 112 | posterior_var = likelihood .* prior_over_k .* prior_over_sigma2; 113 | 114 | % normalise it 115 | posterior_var = posterior_var ./sum(posterior_var); 116 | 117 | % Calculate mode, the MAP value 118 | [~,index]=max(posterior_var); 119 | vMode = varianceGridVals(index); 120 | % Calculate 95% HDI 121 | [HDI] = HDIofGrid(varianceGridVals,posterior_var, 0.95); 122 | 123 | fprintf('Posterior over internal variance: mode=%2.3f (%2.3f - %2.3f)\n',... 124 | vMode, HDI.lower, HDI.upper) 125 | 126 | % save the MAP estimate in a file so that model2MCMCse.m can use it 127 | cd('output') 128 | save('m3MAPestimate.mat', 'vMode') 129 | cd('..') 130 | 131 | 132 | 133 | 134 | %% MODEL PREDICTIONS in data space 135 | % Generate a set of predictions for many signal intensity levels, beyond 136 | % that which we have data for (sii). Useful for visualising the model's 137 | % predictions. 138 | 139 | % Sample from the posterior. * REQUIRES STATISTICS TOOLBOX * 140 | fprintf('Drawing %d samples from the posterior distribution of internal variance...',... 141 | predictive.nSamples) 142 | var_samples = randsample(varianceGridVals,predictive.nSamples,true,posterior_var); 143 | fprintf('done\n') 144 | fprintf('Calculating model predictions in data space for sii...') 145 | % predictive distribution 146 | predk=zeros(predictive.nSamples,numel(data.sii)); % preallocate 147 | for i=1:predictive.nSamples 148 | fprintf('%d of %d',i,predictive.nSamples) 149 | % -------------------------------------------------- 150 | pc = model3nonMCMC(var_samples(i), data.sii, predictive.nSimulatedTrials, dPrior); 151 | % -------------------------------------------------- 152 | predk(i,:) = binornd(data.T, pc ); 153 | end 154 | fprintf('done\n') 155 | % Calculate 95% CI's for each signal level 156 | CI = prctile(predk,[5 95]) ./ data.T; 157 | %clear predk 158 | 159 | 160 | 161 | 162 | %% EXPORT 163 | save(['~/Dropbox/tempModelOutputs/tempModel3run.mat'], '-v7.3') 164 | -------------------------------------------------------------------------------- /output/m1MAPestimate.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/output/m1MAPestimate.mat -------------------------------------------------------------------------------- /output/m3MAPestimate.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbenvincent/bayesian2afc/67699909543f7ee654d9f60a087c1dc643cbad56/output/m3MAPestimate.mat -------------------------------------------------------------------------------- /runAllModels.m: -------------------------------------------------------------------------------- 1 | 2 | setup 3 | 4 | %% MODEL 1 5 | model1runme('gridApprox') 6 | model1plot('tempModel1run_gridApprox') 7 | 8 | model1runme('mcmcCustom') 9 | model1plot('tempModel1run_mcmcCustom') 10 | 11 | model1runme('mcmcJAGS') 12 | model1plot('tempModel1run_mcmcJAGS') 13 | 14 | % model 1 JAGS 15 | model1MCMCconvergence % check chain convergence 16 | 17 | % accuracy of MCMC chain derived MAP estimates of sigma^2 18 | model1MCMCse('calculate') 19 | model1MCMCse('plot') 20 | 21 | %% MODEL 2 22 | model2runme % parameter estimation + model prediction 23 | model2plot 24 | 25 | %% MODEL 3 26 | model3runme % parameter estimation + model prediction 27 | model3plot 28 | 29 | 30 | % calculate psychometric functions 31 | model3psychometric('calculate') 32 | model3psychometric('plot') 33 | 34 | -------------------------------------------------------------------------------- /setup.m: -------------------------------------------------------------------------------- 1 | close all; clc 2 | addpath([cd '/funcs']) 3 | addpath([cd '/funcs/export_fig']) 4 | addpath([cd '/funcs/acf']) 5 | addpath([cd '/funcs/ColorBand']) 6 | addpath([cd '/funcs/latex_fig']) 7 | addpath([cd '/funcs/bensUtils']) 8 | addpath([cd '/funcs/bensPlotFunctions']) 9 | 10 | plot_formatting_setup 11 | 12 | % test for a known issue 13 | try 14 | sum(log( binopdf(1, 2, 0.5 ))); 15 | catch 16 | error(sprintf('ERROR: It is likely that you have another binopdf function on your path. Use \n\t >> which binopdf\n\nto find where this is then use \n\t >> pathtool \n\nand move it down the list')) 17 | end 18 | --------------------------------------------------------------------------------