helper_RW_RPE.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. function ms = helper_RW_RPE(os, varargin)
  2. % helper_RW_RPE Fits neural models to test RW model predictions ~ RPE
  3. % ms = helper_RW_RPE(os, varargin)
  4. % INPUTS
  5. % os: behavioral data structure
  6. % .spikeCount: number of spikes in a particular time bin
  7. % .rewards: 0 for maltodextrin, 1 for sucrose
  8. % .timeLocked: 1 if the animal licked within 2s, 0 if the animal did not (logical)
  9. % timeLocked will always be more than or equal to the number of spike trials
  10. % varargin
  11. % StartingPoints: determines how many points to optimize from
  12. % ParticularModel: cell array of strings of models to use
  13. % RNG: random number generator seed (default = 1)
  14. % OUTPUTS
  15. % ms: model structure of fits
  16. p = inputParser;
  17. p.addParameter('StartingPoints', 1)
  18. p.addParameter('ParticularModel', []);
  19. p.addParameter('RNG', []);
  20. p.parse(varargin{:});
  21. if ~isempty(p.Results.RNG)
  22. rng(p.Results.RNG)
  23. end
  24. % Initialize models
  25. if isempty(p.Results.ParticularModel)
  26. modelNames = {'base', 'curr', 'mean'};
  27. % possible model names
  28. % V: value
  29. % base: RPE
  30. % base_flipped: RPE, sign-flipped
  31. % base_asymm: RPE with asymmetric learning rates
  32. % base_asymm_flipped: RPE with asymmetric learning rates, sign-flipped
  33. %
  34. else
  35. modelNames = p.Results.ParticularModel;
  36. end
  37. % Set up optimization problem
  38. options = optimset('Algorithm', 'interior-point','ObjectiveLimit',...
  39. -1.000000000e+300,'TolFun',1e-15, 'Display','off');
  40. % set boundary conditions
  41. alpha_range = [0 1];
  42. RPErect_range = [-1 1]; % rectify RPEs below this number
  43. slope_range = [0 20]; % reward sensitivity
  44. slope_flipped_range = [-20 0]; % reward sensitivity; flipped
  45. intercept_range = [-20 20]; % baseline spiking
  46. for currMod = modelNames
  47. currMod = currMod{:};
  48. % initialize output variables
  49. runs = p.Results.StartingPoints;
  50. LH = zeros(runs, 1);
  51. if strcmp(currMod, 'V')
  52. paramNames = {'alpha','slope','intercept'};
  53. startValues = [rand(runs, 1)*diff(alpha_range) + alpha_range(1) ...
  54. rand(runs, 1)*diff(slope_range) + slope_range(1) ...
  55. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  56. numParam = size(startValues, 2);
  57. allParams = zeros(runs, numParam);
  58. A=[eye(numParam); -eye(numParam)];
  59. b=[ alpha_range(2); slope_range(2); intercept_range(2);
  60. -alpha_range(1); -slope_range(1); -intercept_range(1)];
  61. parfor r = 1:runs
  62. [allParams(r, :), LH(r, :)] = ...
  63. fmincon(@ott_RW_RPE_V, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  64. end
  65. [~, bestFit] = min(LH);
  66. ms.(currMod).bestParams = allParams(bestFit, :);
  67. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs] = ...
  68. ott_RW_RPE_V(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  69. elseif strcmp(currMod, 'base')
  70. paramNames = {'alpha','slope','intercept'};
  71. startValues = [rand(runs, 1)*diff(alpha_range) + alpha_range(1) ...
  72. rand(runs, 1)*diff(slope_range) + slope_range(1) ...
  73. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  74. numParam = size(startValues, 2);
  75. allParams = zeros(runs, numParam);
  76. A=[eye(numParam); -eye(numParam)];
  77. b=[ alpha_range(2); slope_range(2); intercept_range(2);
  78. -alpha_range(1); -slope_range(1); -intercept_range(1)];
  79. parfor r = 1:runs
  80. [allParams(r, :), LH(r, :)] = ...
  81. fmincon(@ott_RW_RPE_base, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  82. end
  83. [~, bestFit] = min(LH);
  84. ms.(currMod).bestParams = allParams(bestFit, :);
  85. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs] = ...
  86. ott_RW_RPE_base(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  87. elseif strcmp(currMod, 'base_flipped')
  88. paramNames = {'alpha','slope','intercept'};
  89. startValues = [rand(runs, 1)*diff(alpha_range) + alpha_range(1) ...
  90. rand(runs, 1)*diff(slope_flipped_range) + slope_flipped_range(1) ...
  91. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  92. numParam = size(startValues, 2);
  93. allParams = zeros(runs, numParam);
  94. A=[eye(numParam); -eye(numParam)];
  95. b=[ alpha_range(2); slope_flipped_range(2); intercept_range(2);
  96. -alpha_range(1); -slope_flipped_range(1); -intercept_range(1)];
  97. parfor r = 1:runs
  98. [allParams(r, :), LH(r, :)] = ...
  99. fmincon(@ott_RW_RPE_base_flipped, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  100. end
  101. [~, bestFit] = min(LH);
  102. ms.(currMod).bestParams = allParams(bestFit, :);
  103. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs] = ...
  104. ott_RW_RPE_base_flipped(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  105. elseif strcmp(currMod, 'base_asymm')
  106. paramNames = {'alphaPPE','alphaNPE','slope','intercept'};
  107. startValues = [rand(runs, 1)*diff(alpha_range) + alpha_range(1) ... % alphaPPE
  108. rand(runs, 1)*diff(alpha_range) + alpha_range(1) ... % alphaNPE
  109. rand(runs, 1)*diff(slope_range) + slope_range(1) ...
  110. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  111. numParam = size(startValues, 2);
  112. allParams = zeros(runs, numParam);
  113. A=[eye(numParam); -eye(numParam)];
  114. b=[ alpha_range(2); alpha_range(2); slope_range(2); intercept_range(2);
  115. -alpha_range(1); -alpha_range(1); -slope_range(1); -intercept_range(1)];
  116. parfor r = 1:runs
  117. [allParams(r, :), LH(r, :)] = ...
  118. fmincon(@ott_RW_RPE_base_asymm, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  119. end
  120. [~, bestFit] = min(LH);
  121. ms.(currMod).bestParams = allParams(bestFit, :);
  122. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs] = ...
  123. ott_RW_RPE_base_asymm(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  124. elseif strcmp(currMod, 'base_asymm_flipped')
  125. paramNames = {'alphaPPE','alphaNPE','slope','intercept'};
  126. startValues = [rand(runs, 1)*diff(alpha_range) + alpha_range(1) ... % alphaPPE
  127. rand(runs, 1)*diff(alpha_range) + alpha_range(1) ... % alphaNPE
  128. rand(runs, 1)*diff(slope_flipped_range) + slope_flipped_range(1) ...
  129. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  130. numParam = size(startValues, 2);
  131. allParams = zeros(runs, numParam);
  132. A=[eye(numParam); -eye(numParam)];
  133. b=[ alpha_range(2); alpha_range(2); slope_flipped_range(2); intercept_range(2);
  134. -alpha_range(1); -alpha_range(1); -slope_flipped_range(1); -intercept_range(1)];
  135. parfor r = 1:runs
  136. [allParams(r, :), LH(r, :)] = ...
  137. fmincon(@ott_RW_RPE_base_asymm_flipped, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  138. end
  139. [~, bestFit] = min(LH);
  140. ms.(currMod).bestParams = allParams(bestFit, :);
  141. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs] = ...
  142. ott_RW_RPE_base_asymm_flipped(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  143. elseif strcmp(currMod, 'base_rect')
  144. paramNames = {'alpha','RPErect','slope','intercept'};
  145. startValues = [rand(runs, 1)*diff(alpha_range) + alpha_range(1) ...
  146. rand(runs, 1)*diff(RPErect_range) + RPErect_range(1) ...
  147. rand(runs, 1)*diff(slope_range) + slope_range(1) ...
  148. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  149. numParam = size(startValues, 2);
  150. allParams = zeros(runs, numParam);
  151. A=[eye(numParam); -eye(numParam)];
  152. b=[ alpha_range(2); RPErect_range(2); slope_range(2); intercept_range(2);
  153. -alpha_range(1); -RPErect_range(1); -slope_range(1); -intercept_range(1)];
  154. parfor r = 1:runs
  155. [allParams(r, :), LH(r, :)] = ...
  156. fmincon(@ott_RW_RPE_base_rect, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  157. end
  158. [~, bestFit] = min(LH);
  159. ms.(currMod).bestParams = allParams(bestFit, :);
  160. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs] = ...
  161. ott_RW_RPE_base_rect(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  162. elseif strcmp(currMod, 'curr')
  163. paramNames = {'slope','intercept'};
  164. startValues = [rand(runs, 1)*diff(slope_range) + slope_range(1) ...
  165. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  166. numParam = size(startValues, 2);
  167. allParams = zeros(runs, numParam);
  168. A=[eye(numParam); -eye(numParam)];
  169. b=[ slope_range(2); intercept_range(2);
  170. -slope_range(1); -intercept_range(1)];
  171. parfor r = 1:runs
  172. [allParams(r, :), LH(r, :)] = ...
  173. fmincon(@ott_RW_RPE_curr, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  174. end
  175. [~, bestFit] = min(LH);
  176. ms.(currMod).bestParams = allParams(bestFit, :);
  177. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs] = ...
  178. ott_RW_RPE_curr(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  179. elseif strcmp(currMod, 'curr_flipped')
  180. paramNames = {'slope','intercept'};
  181. startValues = [rand(runs, 1)*diff(slope_flipped_range) + slope_flipped_range(1) ...
  182. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  183. numParam = size(startValues, 2);
  184. allParams = zeros(runs, numParam);
  185. A=[eye(numParam); -eye(numParam)];
  186. b=[ slope_flipped_range(2); intercept_range(2);
  187. -slope_flipped_range(1); -intercept_range(1)];
  188. parfor r = 1:runs
  189. [allParams(r, :), LH(r, :)] = ...
  190. fmincon(@ott_RW_RPE_curr_flipped, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  191. end
  192. [~, bestFit] = min(LH);
  193. ms.(currMod).bestParams = allParams(bestFit, :);
  194. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs] = ...
  195. ott_RW_RPE_curr_flipped(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  196. elseif strcmp(currMod, 'mean')
  197. paramNames = {''};
  198. numParam = 0;
  199. [LH, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs] = ...
  200. ott_RW_RPE_mean([], os.spikeCount);
  201. bestFit = 1;
  202. ms.(currMod).bestParams = [];
  203. % test out habit model
  204. elseif strcmp(currMod, 'habit')
  205. paramNames = {'alpha','slope_max','slope_min','int'};
  206. startValues = [rand(runs, 1)*diff(alpha_range) + alpha_range(1) ...
  207. rand(runs, 1)*diff(slope_range) + slope_range(1) ...
  208. rand(runs, 1)*diff(slope_range) + slope_range(1) ...
  209. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  210. numParam = size(startValues, 2);
  211. allParams = zeros(runs, numParam);
  212. A=[eye(numParam); -eye(numParam); 0 -1 1 0]; % constrain slope_max to be less than slope_min
  213. b=[ alpha_range(2); slope_range(2); slope_range(2); intercept_range(2);
  214. -alpha_range(1); -slope_range(1); -slope_range(1); -intercept_range(1); 0];
  215. parfor r = 1:runs
  216. [allParams(r, :), LH(r, :)] = ...
  217. fmincon(@ott_RW_RPE_habit, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  218. end
  219. [~, bestFit] = min(LH);
  220. ms.(currMod).bestParams = allParams(bestFit, :);
  221. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs, ms.(currMod).slope_vec] = ...
  222. ott_RW_RPE_habit(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  223. elseif strcmp(currMod, 'habit_asymm')
  224. paramNames = {'alphaPPE','alphaNPE','slope_max','slope_min','int'};
  225. startValues = [rand(runs, 1)*diff(alpha_range) + alpha_range(1) ...
  226. rand(runs, 1)*diff(alpha_range) + alpha_range(1) ...
  227. rand(runs, 1)*diff(slope_range) + slope_range(1) ...
  228. rand(runs, 1)*diff(slope_range) + slope_range(1) ...
  229. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  230. numParam = size(startValues, 2);
  231. allParams = zeros(runs, numParam);
  232. A=[eye(numParam); -eye(numParam); 0 0 -1 1 0]; % constrain slope_max to be less than slope_min
  233. b=[ alpha_range(2); alpha_range(2); slope_range(2); slope_range(2); intercept_range(2);
  234. -alpha_range(1); -alpha_range(1); -slope_range(1); -slope_range(1); -intercept_range(1); 0];
  235. parfor r = 1:runs
  236. [allParams(r, :), LH(r, :)] = ...
  237. fmincon(@ott_RW_RPE_habit_asymm, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  238. end
  239. [~, bestFit] = min(LH);
  240. ms.(currMod).bestParams = allParams(bestFit, :);
  241. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs, ms.(currMod).slope_vec] = ...
  242. ott_RW_RPE_habit_asymm(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  243. elseif strcmp(currMod, 'adapt')
  244. paramNames = {'alpha','slope','int_max','int_min'};
  245. startValues = [rand(runs, 1)*diff(alpha_range) + alpha_range(1) ...
  246. rand(runs, 1)*diff(slope_range) + slope_range(1) ...
  247. rand(runs, 1)*diff(intercept_range) + intercept_range(1) ...
  248. rand(runs, 1)*diff(intercept_range) + intercept_range(1)];
  249. numParam = size(startValues, 2);
  250. allParams = zeros(runs, numParam);
  251. A=[eye(numParam); -eye(numParam); 0 0 -1 1]; % constrain int_max to be less than int_min
  252. b=[ alpha_range(2); slope_range(2); intercept_range(2); intercept_range(2);
  253. -alpha_range(1); -slope_range(1); -intercept_range(1); -intercept_range(1); 0];
  254. parfor r = 1:runs
  255. [allParams(r, :), LH(r, :)] = ...
  256. fmincon(@ott_RW_RPE_adapt, startValues(r, :), A, b, [], [], [], [], [], options, os.spikeCount, os.rewards, os.timeLocked);
  257. end
  258. [~, bestFit] = min(LH);
  259. ms.(currMod).bestParams = allParams(bestFit, :);
  260. [~, ms.(currMod).probSpike, ms.(currMod).V, ms.(currMod).mean_predictedSpikes, ms.(currMod).RPEs, ms.(currMod).slope_vec] = ...
  261. ott_RW_RPE_adapt(ms.(currMod).bestParams, os.spikeCount, os.rewards, os.timeLocked);
  262. else
  263. error('RW model: Model name not found')
  264. end
  265. ms.(currMod).paramNames = paramNames;
  266. ms.(currMod).LH = -1 * LH(bestFit, :);
  267. ms.(currMod).BIC = log(length(os.spikeCount))*numParam - 2*ms.(currMod).LH;
  268. ms.(currMod).AIC = 2*numParam - 2*ms.(currMod).LH;
  269. ms.(currMod).AICc = ms.(currMod).AIC + (2*numParam^2 + 2*numParam)/(length(os.spikeCount) - numParam - 1);
  270. end