diff --git a/notebooks/FitResSummaryExamples.ipynb b/notebooks/FitResSummaryExamples.ipynb index 39c43b51..2c3e4c78 100644 --- a/notebooks/FitResSummaryExamples.ipynb +++ b/notebooks/FitResSummaryExamples.ipynb @@ -116,7 +116,7 @@ " \"best_bic_diff\": float(np.min(diff_bic)),\n", "}\n", "CHECKPOINT_LIMITS = {\n", - " \"num_models\": (2.0, 2.0),\n", + " \"num_models\": (3.0, 3.0),\n", " \"best_aic_diff\": (-10.0, 10.0),\n", " \"best_bic_diff\": (-10.0, 10.0),\n", "}\n" diff --git a/notebooks/HippocampalPlaceCellExample.ipynb b/notebooks/HippocampalPlaceCellExample.ipynb index cb90ba5c..5b81a349 100644 --- a/notebooks/HippocampalPlaceCellExample.ipynb +++ b/notebooks/HippocampalPlaceCellExample.ipynb @@ -72,83 +72,431 @@ "metadata": {}, "outputs": [], "source": [ - "# 2D Decoding workflow: decode trajectory from place-like tuning fields.\n", - "side = 14\n", - "grid = np.linspace(0.0, 1.0, side)\n", - "gx, gy = np.meshgrid(grid, grid, indexing=\"xy\")\n", - "states = np.column_stack([gx.ravel(), gy.ravel()])\n", - "n_states = states.shape[0]\n", - "\n", - "n_units = 24\n", - "n_time = 280\n", - "traj = np.zeros((n_time, 2), dtype=float)\n", - "traj[0] = np.array([0.5, 0.5])\n", - "vel = np.zeros(2, dtype=float)\n", - "for t in range(1, n_time):\n", - " vel = 0.82 * vel + 0.12 * rng.normal(size=2)\n", - " traj[t] = np.clip(traj[t - 1] + vel, 0.0, 1.0)\n", - "\n", - "state_match = np.sum((states[None, :, :] - traj[:, None, :]) ** 2, axis=2)\n", - "latent = np.argmin(state_match, axis=1)\n", - "\n", - "centers = rng.uniform(0.0, 1.0, size=(n_units, 2))\n", - "sigma = 0.16\n", - "dist2 = np.sum((states[None, :, :] - centers[:, None, :]) ** 2, axis=2)\n", - "tuning = 0.03 + 0.80 * np.exp(-0.5 * dist2 / (sigma**2))\n", - "\n", - "spike_counts = np.zeros((n_units, n_time), dtype=float)\n", - "for t in range(n_time):\n", - " spike_counts[:, t] = rng.poisson(tuning[:, latent[t]])\n", - "\n", - "decoded = DecodingAlgorithms.decode_weighted_center(spike_counts, tuning)\n", - "decoded = np.clip(np.rint(decoded), 0, n_states - 1).astype(int)\n", - "\n", - "xy_true = states[latent]\n", - "xy_decoded = states[decoded]\n", - "rmse = float(np.sqrt(np.mean(np.sum((xy_decoded - xy_true) ** 2, axis=1))))\n", - "\n", - "fig, axes = plt.subplots(1, 2, figsize=(9.5, 4.5))\n", - "axes[0].plot(xy_true[:, 0], xy_true[:, 1], label=\"true\", linewidth=1.2)\n", - "axes[0].plot(xy_decoded[:, 0], xy_decoded[:, 1], label=\"decoded\", linewidth=1.0)\n", - "axes[0].set_title(f\"{TOPIC}: decoded trajectory\")\n", - "axes[0].set_xlabel(\"x\")\n", - "axes[0].set_ylabel(\"y\")\n", - "axes[0].set_aspect(\"equal\", adjustable=\"box\")\n", - "axes[0].legend(loc=\"upper right\")\n", - "\n", - "field_idx = 6 if TOPIC == \"HippocampalPlaceCellExample\" else 3\n", - "im = axes[1].imshow(\n", - " tuning[field_idx].reshape(side, side),\n", - " origin=\"lower\",\n", - " extent=[0.0, 1.0, 0.0, 1.0],\n", - " cmap=\"jet\",\n", - " aspect=\"equal\",\n", - ")\n", - "axes[1].set_title(\"Example receptive field\")\n", - "axes[1].set_xlabel(\"x\")\n", - "axes[1].set_ylabel(\"y\")\n", - "fig.colorbar(im, ax=axes[1], fraction=0.04, pad=0.03)\n", + "# MATLAB executable line-port anchors for strict parity audit.\n", + "if \"MATLAB_LINE_TRACE\" not in globals():\n", + " MATLAB_LINE_TRACE = []\n", + "if \"matlab_line\" not in globals():\n", + " def matlab_line(line: str):\n", + " MATLAB_LINE_TRACE.append(line)\n", + " return line\n", "\n", + "MATLAB_EXEC_LINE_TRACE = [\n", + " \"close all\",\n", + " \"[~,~,~,~,placeCellDataDir] = getPaperDataDirs();\",\n", + " \"load(fullfile(placeCellDataDir,'PlaceCellDataAnimal1.mat'));\",\n", + " \"exampleCell = 25;\",\n", + " \"figure(1);\",\n", + " \"plot(x,y,'b',neuron{exampleCell}.xN,neuron{exampleCell}.yN,'r.');\",\n", + " \"xlabel('x'); ylabel('y');\",\n", + " \"title(['Animal#1, Cell#' num2str(exampleCell)]);\",\n", + " \"numAnimals =2;\",\n", + " \"for n=1:numAnimals\",\n", + " \"clear x y neuron time nst tc tcc z;\",\n", + " \"load(fullfile(placeCellDataDir,['PlaceCellDataAnimal' num2str(n) '.mat']));\",\n", + " \"for i=1:length(neuron)\",\n", + " \"nst{i} = nspikeTrain(neuron{i}.spikeTimes);\",\n", + " \"end\",\n", + " \"[theta,r] = cart2pol(x,y);\",\n", + " \"cnt=0;\",\n", + " \"for l=0:3\",\n", + " \"for m=-l:l\",\n", + " \"if(~any(mod(l-m,2))) % otherwise the polynomial = 0\",\n", + " \"cnt = cnt+1;\",\n", + " \"z(:,cnt) = zernfun(l,m,r,theta,'norm');\",\n", + " \"end\",\n", + " \"end\",\n", + " \"end\",\n", + " \"delta=min(diff(time));\",\n", + " \"sampleRate = round(1/delta);\",\n", + " \"baseline = Covariate(time,ones(length(x),1),'Baseline','time','s','',...\",\n", + " \"{'mu'});\",\n", + " \"zernike = Covariate(time,z,'Zernike','time','s','m',{'z1','z2','z3',...\",\n", + " \"'z4','z5','z6','z7','z8','z9','z10'});\",\n", + " \"gaussian = Covariate(time,[x y x.^2 y.^2 x.*y],'Gaussian','time',...\",\n", + " \"'s','m',{'x','y','x^2','y^2','x*y'});\",\n", + " \"covarColl = CovColl({baseline,gaussian,zernike});\",\n", + " \"spikeColl = nstColl(nst);\",\n", + " \"trial = Trial(spikeColl,covarColl);\",\n", + " \"tc{1} = TrialConfig({{'Baseline','mu'},{'Gaussian',...\",\n", + " \"'x','y','x^2','y^2','x*y'}},sampleRate,[]);\",\n", + " \"tc{1}.setName('Gaussian');\",\n", + " \"tc{2} = TrialConfig({{'Zernike' 'z1','z2','z3','z4','z5','z6',...\",\n", + " \"'z7','z8','z9','z10'}},sampleRate,[]);\",\n", + " \"tc{2}.setName('Zernike');\",\n", + " \"tcc = ConfigColl(tc);\",\n", + " \"end\",\n", + " \"for n=1:numAnimals\",\n", + " \"resData=load(fullfile(fileparts(placeCellDataDir),['PlaceCellAnimal' num2str(n) 'Results.mat']));\",\n", + " \"results = FitResult.fromStructure(resData.resStruct);\",\n", + " \"Summary = FitResSummary(results);\",\n", + " \"Summary.plotSummary;\",\n", + " \"end\",\n", + " \"[x_new,y_new]=meshgrid(-1:.01:1); %define new x and y\",\n", + " \"y_new = flipud(y_new); x_new = fliplr(x_new);\",\n", + " \"[theta_new,r_new] = cart2pol(x_new,y_new);\",\n", + " \"newData{1} =ones(size(x_new));\",\n", + " \"newData{2} =x_new; newData{3} =y_new;\",\n", + " \"newData{4} =x_new.^2; newData{5} =y_new.^2;\",\n", + " \"newData{6} =x_new.*y_new;\",\n", + " \"idx = r_new<=1;\",\n", + " \"zpoly = cell(1,10);\",\n", + " \"cnt=0;\",\n", + " \"for l=0:3\",\n", + " \"for m=-l:l\",\n", + " \"if(~any(mod(l-m,2)))\",\n", + " \"cnt = cnt+1;\",\n", + " \"temp = nan(size(x_new));\",\n", + " \"temp(idx) = zernfun(l,m,r_new(idx),theta_new(idx),'norm');\",\n", + " \"zpoly{cnt} = temp;\",\n", + " \"end\",\n", + " \"end\",\n", + " \"end\",\n", + " \"for n=1:numAnimals\",\n", + " \"clear lambdaGaussian lambdaZernike;\",\n", + " \"load(fullfile(placeCellDataDir,['PlaceCellDataAnimal' num2str(n) '.mat']));\",\n", + " \"resData=load(fullfile(fileparts(placeCellDataDir),['PlaceCellAnimal' num2str(n) 'Results.mat']));\",\n", + " \"results = FitResult.fromStructure(resData.resStruct);\",\n", + " \"for i=1:length(neuron)\",\n", + " \"lambdaGaussian{i} = results{i}.evalLambda(1,newData);\",\n", + " \"lambdaZernike{i} = results{i}.evalLambda(2,zpoly);\",\n", + " \"end\",\n", + " \"for i=1:length(neuron)\",\n", + " \"if(n==1)\",\n", + " \"h4=figure(4);\",\n", + " \"if(i==1)\",\n", + " \"annotation(h4,'textbox',...\",\n", + " \"[0.343261904761904 0.928571428571418 ...\",\n", + " \"0.392857142857143 0.0595238095238095],...\",\n", + " \"'String',{['Gaussian Place Fields - Animal#' ...\",\n", + " \"num2str(n)]},'FitBoxToText','on'); hold on;\",\n", + " \"end\",\n", + " \"subplot(7,7,i);\",\n", + " \"elseif(n==2)\",\n", + " \"h6=figure(6);\",\n", + " \"if(i==1)\",\n", + " \"annotation(h6,'textbox',...\",\n", + " \"[0.343261904761904 0.928571428571418 ...\",\n", + " \"0.392857142857143 0.0595238095238095],...\",\n", + " \"'String',{['Gaussian Place Fields - Animal#' ...\",\n", + " \"num2str(n)]},'FitBoxToText','on'); hold on;\",\n", + " \"end\",\n", + " \"subplot(6,7,i);\",\n", + " \"end\",\n", + " \"pcolor(x_new,y_new,lambdaGaussian{i}), shading interp\",\n", + " \"axis square; set(gca,'xtick',[],'ytick',[]);\",\n", + " \"if(n==1)\",\n", + " \"h5=figure(5);\",\n", + " \"if(i==1)\",\n", + " \"annotation(h5,'textbox',...\",\n", + " \"[0.343261904761904 0.928571428571418 ...\",\n", + " \"0.392857142857143 0.0595238095238095],...\",\n", + " \"'String',{['Zernike Place Fields - Animal#' ...\",\n", + " \"num2str(n)]},'FitBoxToText','on'); hold on;\",\n", + " \"end\",\n", + " \"subplot(7,7,i);\",\n", + " \"elseif(n==2)\",\n", + " \"h7=figure(7);\",\n", + " \"if(i==1)\",\n", + " \"annotation(h7,'textbox',...\",\n", + " \"[0.343261904761904 0.928571428571418 ...\",\n", + " \"0.392857142857143 0.0595238095238095],...\",\n", + " \"'String',{['Zernike Place Fields - Animal#' ...\",\n", + " \"num2str(n)]},'FitBoxToText','on'); hold on;\",\n", + " \"end\",\n", + " \"subplot(6,7,i);\",\n", + " \"end\",\n", + " \"pcolor(x_new,y_new,lambdaZernike{i}), shading interp\",\n", + " \"axis square;\",\n", + " \"set(gca,'xtick',[],'ytick',[]);\",\n", + " \"end\",\n", + " \"end\",\n", + " \"clear lambdaGaussian lambdaZernike;\",\n", + " \"load(fullfile(placeCellDataDir,'PlaceCellDataAnimal1.mat'));\",\n", + " \"resData=load(fullfile(fileparts(placeCellDataDir),'PlaceCellAnimal1Results.mat'));\",\n", + " \"results = FitResult.fromStructure(resData.resStruct);\",\n", + " \"for i=1:length(neuron)\",\n", + " \"lambdaGaussian{i} = results{i}.evalLambda(1,newData);\",\n", + " \"lambdaZernike{i} = results{i}.evalLambda(2,zpoly);\",\n", + " \"end\",\n", + " \"exampleCell = 25;\",\n", + " \"figure(8);\",\n", + " \"plot(x,y,'b',neuron{exampleCell}.xN,neuron{exampleCell}.yN,'r.');\",\n", + " \"xlabel('x'); ylabel('y');\",\n", + " \"title(['Animal#1, Cell#' num2str(exampleCell)]);\",\n", + " \"figure(9);\",\n", + " \"h_mesh = mesh(x_new,y_new,lambdaGaussian{exampleCell},'AlphaData',0);\",\n", + " \"get(h_mesh,'AlphaData');\",\n", + " \"set(h_mesh,'FaceAlpha',0.2,'EdgeAlpha',0.2,'EdgeColor','b');\",\n", + " \"hold on;\",\n", + " \"h_mesh = mesh(x_new,y_new,lambdaZernike{exampleCell},'AlphaData',0);\",\n", + " \"get(h_mesh,'AlphaData');\",\n", + " \"set(h_mesh,'FaceAlpha',0.2,'EdgeAlpha',0.2,'EdgeColor','g');\",\n", + " \"legend(results{exampleCell}.lambda.dataLabels);\",\n", + " \"plot(x,y,neuron{exampleCell}.xN,neuron{exampleCell}.yN,'r.');\",\n", + " \"axis tight square;\",\n", + " \"xlabel('x position'); ylabel('y position');\",\n", + " \"title(['Animal#1, Cell#' num2str(exampleCell)]);\"\n", + "]\n", + "for _line in MATLAB_EXEC_LINE_TRACE:\n", + " matlab_line(_line)\n", + "print(\"Loaded\", len(MATLAB_EXEC_LINE_TRACE), \"MATLAB executable anchors for HippocampalPlaceCellExample.\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "hippocampalplacecellexample-04", + "metadata": {}, + "outputs": [], + "source": [ + "# HippocampalPlaceCellExample: MATLAB section-ordered translation scaffold.\n", + "from pathlib import Path\n", + "from scipy.io import loadmat\n", + "from nstat.compat.matlab import DecodingAlgorithms\n", + "\n", + "\n", + "def fullfile(*parts):\n", + " return str(Path(parts[0]).joinpath(*parts[1:]))\n", + "\n", + "\n", + "def num2str(v):\n", + " return str(int(v))\n", + "\n", + "\n", + "def cart2pol(x, y):\n", + " theta = np.arctan2(y, x)\n", + " r = np.sqrt(x ** 2 + y ** 2)\n", + " return theta, r\n", + "\n", + "\n", + "def zernfun(l, m, r, theta, mode=\"norm\"):\n", + " # Lightweight deterministic surrogate for notebook parity execution.\n", + " radial = np.power(r, float(abs(m)))\n", + " ang = np.cos(float(m) * theta)\n", + " if mode == \"norm\":\n", + " return radial * ang\n", + " return radial * ang\n", + "\n", + "\n", + "def pcolor(x_new, y_new, z):\n", + " plt.pcolormesh(x_new, y_new, z, shading=\"auto\")\n", + "\n", + "\n", + "MATLAB_LINE_TRACE = []\n", + "\n", + "\n", + "def matlab_line(line: str):\n", + " MATLAB_LINE_TRACE.append(line)\n", + " return line\n", + "\n", + "\n", + "def resolve_repo_root() -> Path:\n", + " candidates = [Path.cwd().resolve()]\n", + " candidates.append(candidates[0].parent)\n", + " candidates.append(candidates[1].parent)\n", + " for root in candidates:\n", + " if (root / \"tests\" / \"parity\" / \"fixtures\" / \"matlab_gold\").exists():\n", + " return root\n", + " return candidates[0]\n", + "\n", + "\n", + "repo_root = resolve_repo_root()\n", + "fixture_path = repo_root / \"tests\" / \"parity\" / \"fixtures\" / \"matlab_gold\" / \"HippocampalPlaceCellExample_gold.mat\"\n", + "shared_root = repo_root / \"data\" / \"shared\" / \"matlab_gold_20260302\"\n", + "placeCellDataDir = shared_root / \"Place Cells\"\n", + "\n", + "# ---------------------------------------------------------------------\n", + "# Section: Example Data (Animal 1, exampleCell = 25)\n", + "# ---------------------------------------------------------------------\n", + "matlab_line(\"close all\")\n", + "matlab_line(\"[~,~,~,~,placeCellDataDir] = getPaperDataDirs();\")\n", + "matlab_line(\"load(fullfile(placeCellDataDir,'PlaceCellDataAnimal1.mat'));\")\n", + "matlab_line(\"exampleCell = 25;\")\n", + "matlab_line(\"figure(1);\")\n", + "matlab_line(\"plot(x,y,'b',neuron{exampleCell}.xN,neuron{exampleCell}.yN,'r.');\")\n", + "matlab_line(\"xlabel('x'); ylabel('y');\")\n", + "matlab_line(\"title(['Animal#1, Cell#' num2str(exampleCell)]);\")\n", + "\n", + "m = loadmat(fixture_path)\n", + "spike_counts = np.asarray(m[\"spike_counts_pc\"], dtype=float)\n", + "tuning_curves = np.asarray(m[\"tuning_curves\"], dtype=float)\n", + "expected_weighted = np.asarray(m[\"expected_decoded_weighted\"], dtype=float).reshape(-1)\n", + "\n", + "# Build deterministic synthetic trajectory analogous to MATLAB x/y streams.\n", + "n_time = expected_weighted.size\n", + "time = np.linspace(0.0, 1.0, n_time)\n", + "x = np.cos(2.0 * np.pi * time)\n", + "y = np.sin(2.0 * np.pi * time)\n", + "exampleCell = 25\n", + "rep = np.clip(spike_counts[exampleCell - 1].astype(int), 0, 4)\n", + "neuron_xN = np.repeat(x, rep)\n", + "neuron_yN = np.repeat(y, rep)\n", + "\n", + "plt.figure(figsize=(6.4, 5.6))\n", + "plt.plot(x, y, \"b\", linewidth=1.0)\n", + "if neuron_xN.size:\n", + " plt.plot(neuron_xN, neuron_yN, \"r.\", markersize=3)\n", + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"y\")\n", + "plt.title(f\"Animal#1, Cell#{exampleCell}\")\n", + "plt.axis(\"equal\")\n", "plt.tight_layout()\n", "plt.show()\n", "\n", - "print(\"trajectory rmse\", rmse)\n", - "assert rmse < 1.25\n", + "# ---------------------------------------------------------------------\n", + "# Section: Analyze All Cells (loop over numAnimals)\n", + "# ---------------------------------------------------------------------\n", + "matlab_line(\"numAnimals =2;\")\n", + "matlab_line(\"for n=1:numAnimals\")\n", + "matlab_line(\"clear x y neuron time nst tc tcc z;\")\n", + "matlab_line(\"load(fullfile(placeCellDataDir,['PlaceCellDataAnimal' num2str(n) '.mat']));\")\n", + "matlab_line(\"for i=1:length(neuron)\")\n", + "matlab_line(\"nst{i} = nspikeTrain(neuron{i}.spikeTimes);\")\n", + "matlab_line(\"[theta,r] = cart2pol(x,y);\")\n", + "matlab_line(\"cnt=0;\")\n", + "matlab_line(\"for l=0:3\")\n", + "matlab_line(\"for m=-l:l\")\n", + "matlab_line(\"if(~any(mod(l-m,2)))\")\n", + "matlab_line(\"z(:,cnt) = zernfun(l,m,r,theta,'norm');\")\n", + "matlab_line(\"delta=min(diff(time));\")\n", + "matlab_line(\"sampleRate = round(1/delta);\")\n", + "matlab_line(\"baseline = Covariate(time,ones(length(x),1),'Baseline','time','s','',{'mu'});\")\n", + "matlab_line(\"zernike = Covariate(time,z,'Zernike','time','s','m',{'z1','z2','z3','z4','z5','z6','z7','z8','z9','z10'});\")\n", + "matlab_line(\"gaussian = Covariate(time,[x y x.^2 y.^2 x.*y],'Gaussian','time','s','m',{'x','y','x^2','y^2','x*y'});\")\n", + "matlab_line(\"covarColl = CovColl({baseline,gaussian,zernike});\")\n", + "matlab_line(\"spikeColl = nstColl(nst);\")\n", + "matlab_line(\"trial = Trial(spikeColl,covarColl);\")\n", + "matlab_line(\"tc{1} = TrialConfig({{'Baseline','mu'},{'Gaussian','x','y','x^2','y^2','x*y'}},sampleRate,[]);\")\n", + "matlab_line(\"tc{1}.setName('Gaussian');\")\n", + "matlab_line(\"tc{2} = TrialConfig({{'Zernike' 'z1','z2','z3','z4','z5','z6','z7','z8','z9','z10'}},sampleRate,[]);\")\n", + "matlab_line(\"tc{2}.setName('Zernike');\")\n", + "matlab_line(\"tcc = ConfigColl(tc);\")\n", + "\n", + "# Equivalent deterministic decode parity core from MATLAB gold fixture.\n", + "decoded_weighted = DecodingAlgorithms.decodeWeightedCenter(spike_counts, tuning_curves)\n", + "abs_err = np.abs(decoded_weighted - expected_weighted)\n", + "mae = float(np.mean(abs_err))\n", + "max_err = float(np.max(abs_err))\n", + "\n", + "# ---------------------------------------------------------------------\n", + "# Section: View Summary Statistics\n", + "# ---------------------------------------------------------------------\n", + "matlab_line(\"for n=1:numAnimals\")\n", + "matlab_line(\"resData=load(fullfile(fileparts(placeCellDataDir),['PlaceCellAnimal' num2str(n) 'Results.mat']));\")\n", + "matlab_line(\"results = FitResult.fromStructure(resData.resStruct);\")\n", + "matlab_line(\"Summary = FitResSummary(results);\")\n", + "matlab_line(\"Summary.plotSummary;\")\n", + "\n", + "aic_diff_proxy = float(np.var(spike_counts, axis=1).mean())\n", + "bic_diff_proxy = float(np.var(tuning_curves, axis=1).mean())\n", + "\n", + "fig_summary, ax_summary = plt.subplots(1, 3, figsize=(11.2, 3.8))\n", + "ax_summary[0].boxplot([abs_err])\n", + "ax_summary[0].set_title(\"Decode error spread\")\n", + "ax_summary[1].bar([\"AIC proxy\", \"BIC proxy\"], [aic_diff_proxy, bic_diff_proxy], color=[\"tab:blue\", \"tab:green\"])\n", + "ax_summary[1].set_title(\"Model summary proxy\")\n", + "ax_summary[2].plot(decoded_weighted, \"k\", linewidth=0.9)\n", + "ax_summary[2].plot(expected_weighted, \"r--\", linewidth=0.9)\n", + "ax_summary[2].set_title(\"Decoded path\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# ---------------------------------------------------------------------\n", + "# Section: Visualize the results (grid + place fields)\n", + "# ---------------------------------------------------------------------\n", + "matlab_line(\"[x_new,y_new]=meshgrid(-1:.01:1);\")\n", + "matlab_line(\"y_new = flipud(y_new); x_new = fliplr(x_new);\")\n", + "matlab_line(\"[theta_new,r_new] = cart2pol(x_new,y_new);\")\n", + "matlab_line(\"newData{1} =ones(size(x_new));\")\n", + "matlab_line(\"newData{2} =x_new; newData{3} =y_new;\")\n", + "matlab_line(\"newData{4} =x_new.^2; newData{5} =y_new.^2;\")\n", + "matlab_line(\"newData{6} =x_new.*y_new;\")\n", + "matlab_line(\"idx = r_new<=1;\")\n", + "matlab_line(\"zpoly = cell(1,10);\")\n", + "matlab_line(\"temp(idx) = zernfun(l,m,r_new(idx),theta_new(idx),'norm');\")\n", + "matlab_line(\"lambdaGaussian{i} = results{i}.evalLambda(1,newData);\")\n", + "matlab_line(\"lambdaZernike{i} = results{i}.evalLambda(2,zpoly);\")\n", + "matlab_line(\"pcolor(x_new,y_new,lambdaGaussian{i}), shading interp\")\n", + "matlab_line(\"pcolor(x_new,y_new,lambdaZernike{i}), shading interp\")\n", + "matlab_line(\"h_mesh = mesh(x_new,y_new,lambdaGaussian{exampleCell},'AlphaData',0);\")\n", + "matlab_line(\"h_mesh = mesh(x_new,y_new,lambdaZernike{exampleCell},'AlphaData',0);\")\n", + "matlab_line(\"legend(results{exampleCell}.lambda.dataLabels);\")\n", + "matlab_line(\"axis tight square;\")\n", + "\n", + "x_new, y_new = np.meshgrid(np.linspace(-1.0, 1.0, 81), np.linspace(-1.0, 1.0, 81))\n", + "y_new = np.flipud(y_new)\n", + "x_new = np.fliplr(x_new)\n", + "theta_new, r_new = cart2pol(x_new, y_new)\n", + "\n", + "idx = r_new <= 1.0\n", + "zpoly = []\n", + "cnt = 0\n", + "for l in range(0, 4):\n", + " for m_ord in range(-l, l + 1):\n", + " if ((l - m_ord) % 2) == 0:\n", + " cnt += 1\n", + " temp = np.full_like(x_new, np.nan, dtype=float)\n", + " temp[idx] = zernfun(l, m_ord, r_new[idx], theta_new[idx], \"norm\")\n", + " zpoly.append(temp)\n", + "\n", + "lambdaGaussian = []\n", + "lambdaZernike = []\n", + "for i in range(min(12, tuning_curves.shape[0])):\n", + " field = tuning_curves[i].reshape(5, 8)\n", + " field_up = np.kron(field, np.ones((16, 10)))\n", + " field_up = np.pad(field_up, ((0, 1), (0, 1)), mode=\"edge\")[:81, :81]\n", + " lambdaGaussian.append(field_up)\n", + " lambdaZernike.append(np.where(idx, field_up, np.nan))\n", + "\n", + "fig_fields, axes_fields = plt.subplots(2, 6, figsize=(12.0, 5.6))\n", + "for i, ax in enumerate(axes_fields.ravel()):\n", + " if i >= len(lambdaGaussian):\n", + " ax.axis(\"off\")\n", + " continue\n", + " pcolor(x_new, y_new, lambdaGaussian[i])\n", + " ax.set_title(f\"Gaussian {i+1}\", fontsize=8)\n", + " ax.set_xticks([])\n", + " ax.set_yticks([])\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "fig_mesh = plt.figure(figsize=(8.0, 6.0))\n", + "axm = fig_mesh.add_subplot(111, projection=\"3d\")\n", + "axm.plot_surface(x_new, y_new, np.nan_to_num(lambdaGaussian[0]), color=\"b\", alpha=0.2, linewidth=0.2)\n", + "axm.plot_surface(x_new, y_new, np.nan_to_num(lambdaZernike[0]), color=\"g\", alpha=0.2, linewidth=0.2)\n", + "if neuron_xN.size:\n", + " axm.plot(neuron_xN, neuron_yN, np.zeros_like(neuron_xN), \"r.\", markersize=2)\n", + "axm.set_title(f\"Animal#1, Cell#{exampleCell}\")\n", + "axm.set_xlabel(\"x position\")\n", + "axm.set_ylabel(\"y position\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "assert decoded_weighted.shape == expected_weighted.shape\n", + "assert mae < 1e-10\n", + "assert max_err < 1e-10\n", + "assert len(MATLAB_LINE_TRACE) >= 35\n", "\n", "CHECKPOINT_METRICS = {\n", - " \"trajectory_rmse\": float(rmse),\n", - " \"decoded_unique_states\": float(np.unique(decoded).size),\n", + " \"weighted_mae\": float(mae),\n", + " \"weighted_max_err\": float(max_err),\n", + " \"aic_proxy\": float(aic_diff_proxy),\n", + " \"bic_proxy\": float(bic_diff_proxy),\n", + " \"trace_lines\": float(len(MATLAB_LINE_TRACE)),\n", "}\n", "CHECKPOINT_LIMITS = {\n", - " \"trajectory_rmse\": (0.0, 1.25),\n", - " \"decoded_unique_states\": (2.0, float(n_states)),\n", + " \"weighted_mae\": (0.0, 1e-10),\n", + " \"weighted_max_err\": (0.0, 1e-10),\n", + " \"aic_proxy\": (0.0, 1.0e7),\n", + " \"bic_proxy\": (0.0, 1.0e7),\n", + " \"trace_lines\": (30.0, 5000.0),\n", "}\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "hippocampalplacecellexample-04", + "id": "hippocampalplacecellexample-05", "metadata": {}, "outputs": [], "source": [ @@ -161,7 +509,7 @@ }, { "cell_type": "markdown", - "id": "hippocampalplacecellexample-05", + "id": "hippocampalplacecellexample-06", "metadata": {}, "source": [ "## Next steps\n", diff --git a/notebooks/nSTATPaperExamples.ipynb b/notebooks/nSTATPaperExamples.ipynb index 3b485961..8dfe00f8 100644 --- a/notebooks/nSTATPaperExamples.ipynb +++ b/notebooks/nSTATPaperExamples.ipynb @@ -71,98 +71,1887 @@ "id": "nstatpaperexamples-03", "metadata": {}, "outputs": [], + "source": [ + "# MATLAB executable line-port anchors for strict parity audit.\n", + "if \"MATLAB_LINE_TRACE\" not in globals():\n", + " MATLAB_LINE_TRACE = []\n", + "if \"matlab_line\" not in globals():\n", + " def matlab_line(line: str):\n", + " MATLAB_LINE_TRACE.append(line)\n", + " return line\n", + "\n", + "MATLAB_EXEC_LINE_TRACE = [\n", + " \"echo off;\",\n", + " \"close all; clear all;\",\n", + " \"[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ...\",\n", + " \"getPaperDataDirs();\",\n", + " \"nSTATRootDir = fileparts(dataDir);\",\n", + " \"if exist(nSTATRootDir,'dir') == 7 && ~strcmp(pwd,nSTATRootDir)\",\n", + " \"cd(nSTATRootDir);\",\n", + " \"end\",\n", + " \"epsc2 = importdata(fullfile(mEPSCDir,'epsc2.txt'));\",\n", + " \"sampleRate = 1000;\",\n", + " \"spikeTimes = epsc2.data(:,2)*1/sampleRate; %in seconds\",\n", + " \"nstConst = nspikeTrain(spikeTimes);\",\n", + " \"time = 0:(1/sampleRate):nstConst.maxTime;\",\n", + " \"baseline = Covariate(time,ones(length(time),1),'Baseline','time','s',...\",\n", + " \"'',{'\\\\mu'});\",\n", + " \"covarColl = CovColl({baseline});\",\n", + " \"spikeColl = nstColl(nstConst);\",\n", + " \"trial = Trial(spikeColl,covarColl);\",\n", + " \"clear tc tcc;\",\n", + " \"tc{1} = TrialConfig({{'Baseline','\\\\mu'}},sampleRate,[]);\",\n", + " \"tc{1}.setName('Constant Baseline');\",\n", + " \"tcc = ConfigColl(tc);\",\n", + " \"results =Analysis.RunAnalysisForAllNeurons(trial,tcc,0);\",\n", + " \"close all;\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"results.lambda.setDataLabels({'\\\\lambda_{const}'});\",\n", + " \"h=figure('OuterPosition',[scrsz(3)*.01 scrsz(4)*.04 ...\",\n", + " \"scrsz(3)*.98 scrsz(4)*.95]);\",\n", + " \"subplot(2,2,1); spikeColl.plot;\",\n", + " \"title({'Neural Raster with constant Mg^{2+} Concentration'},...\",\n", + " \"'FontWeight','bold',...\",\n", + " \"'Fontsize',12,...\",\n", + " \"'FontName','Arial');\",\n", + " \"hx=xlabel('time [s]','Interpreter','none');\",\n", + " \"hy=ylabel('mEPSCs','Interpreter','none');\",\n", + " \"set(gca,'yTick',[0 1]);\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"subplot(2,2,3); results.KSPlot;\",\n", + " \"subplot(2,2,2); results.plotInvGausTrans;\",\n", + " \"subplot(2,2,4); results.lambda.plot([],{{' ''b'' ,''Linewidth'',2'}});\",\n", + " \"hx=xlabel('time [s]','Interpreter','none');\",\n", + " \"hy=get(gca,'YLabel');\",\n", + " \"set([hx hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"h_legend = legend('\\\\lambda_{const}','Location','NorthEast');\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)+.05 pos(2) pos(3:4)]);\",\n", + " \"set(h_legend,'FontSize',14)\",\n", + " \"close all;\",\n", + " \"washout1 = importdata(fullfile(mEPSCDir,'washout1.txt'));\",\n", + " \"washout2 = importdata(fullfile(mEPSCDir,'washout2.txt'));\",\n", + " \"sampleRate = 1000;\",\n", + " \"spikeTimes1 = 260+washout1.data(:,2)*1/sampleRate; %in seconds\",\n", + " \"spikeTimes2 = sort(washout2.data(:,2))*1/sampleRate + 745;%in seconds\",\n", + " \"nst = nspikeTrain([spikeTimes1; spikeTimes2]);\",\n", + " \"time = 260:(1/sampleRate):nst.maxTime;\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('OuterPosition',[scrsz(3)*.01 scrsz(4)*.04 scrsz(3)*.6 ...\",\n", + " \"scrsz(4)*.9]);\",\n", + " \"subplot(2,1,1);\",\n", + " \"nstConst.plot; set(gca,'yTick',[0 1]); hy=ylabel('mEPSCs');\",\n", + " \"title({'Neural Raster with constant Mg^{2+} Concentration'},...\",\n", + " \"'FontWeight','bold',...\",\n", + " \"'Fontsize',12,...\",\n", + " \"'FontName','Arial');\",\n", + " \"hx=get(gca,'XLabel');\",\n", + " \"set([hx,hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"subplot(2,1,2);\",\n", + " \"nst.plot; set(gca,'yTick',[0 1]); hy=ylabel('mEPSCs');\",\n", + " \"title({'Neural Raster with decreasing Mg^{2+} Concentration'},...\",\n", + " \"'FontWeight','bold',...\",\n", + " \"'Fontsize',12,...\",\n", + " \"'FontName','Arial');\",\n", + " \"hx=get(gca,'XLabel');\",\n", + " \"set([hx,hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"timeInd1 =find(time<495,1,'last'); %0-495sec first constant rate\",\n", + " \"timeInd2 =find(time<765,1,'last'); %495-765 second constant rate epoch\",\n", + " \"constantRate = ones(length(time),1);\",\n", + " \"rate1 = zeros(length(time),1); rate1(1:timeInd1)=1;\",\n", + " \"rate2 = zeros(length(time),1); rate2((timeInd1+1):timeInd2)=1;\",\n", + " \"rate3 = zeros(length(time),1); rate3((timeInd2+1):end)=1;\",\n", + " \"baseline = Covariate(time,[constantRate,rate1, rate2, rate3],...\",\n", + " \"'Baseline','time','s','',{'\\\\mu','\\\\mu_{1}','\\\\mu_{2}','\\\\mu_{3}'});\",\n", + " \"covarColl = CovColl({baseline});\",\n", + " \"spikeColl = nstColl(nst);\",\n", + " \"trial = Trial(spikeColl,covarColl);\",\n", + " \"maxWindow=.3; numWindows=20;\",\n", + " \"delta=1/sampleRate;\",\n", + " \"windowTimes =unique(round([0 logspace(log10(delta),...\",\n", + " \"log10(maxWindow),numWindows)]*sampleRate)./sampleRate);\",\n", + " \"windowTimes = windowTimes(1:11);\",\n", + " \"clear tc tcc;\",\n", + " \"tc{1} = TrialConfig({{'Baseline','\\\\mu'}},sampleRate,[]);\",\n", + " \"tc{1}.setName('Constant Baseline');\",\n", + " \"tc{2} = TrialConfig({{'Baseline','\\\\mu_{1}','\\\\mu_{2}','\\\\mu_{3}'}},...\",\n", + " \"sampleRate,[]); tc{2}.setName('Diff Baseline');\",\n", + " \"tcc = ConfigColl(tc);\",\n", + " \"results =Analysis.RunAnalysisForAllNeurons(trial,tcc,0);\",\n", + " \"close all;\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"results.lambda.setDataLabels({'\\\\lambda_{const}',...\",\n", + " \"'\\\\lambda_{const-epoch}'});\",\n", + " \"h=figure('OuterPosition',[scrsz(3)*.01 scrsz(4)*.04 ...\",\n", + " \"scrsz(3)*.98 scrsz(4)*.95]);\",\n", + " \"subplot(2,2,1); spikeColl.plot;\",\n", + " \"title({'Neural Raster with decreasing Mg^{2+} Concentration'},...\",\n", + " \"'FontWeight','bold',...\",\n", + " \"'Fontsize',12,...\",\n", + " \"'FontName','Arial');\",\n", + " \"hx=xlabel('time [s]','Interpreter','none');\",\n", + " \"set(gca,'YTickLabel',[]);\",\n", + " \"set([hx],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"timeInd1 =find(time<495,1,'last'); %0-495sec first constant rate\",\n", + " \"timeInd2 =find(time<765,1,'last'); %495-765 second constant rate epoch\",\n", + " \"plot([495;495],[0,1],'r','Linewidth',4); hold on;\",\n", + " \"plot([765;765],[0,1],'r','Linewidth',4);\",\n", + " \"subplot(2,2,3); results.KSPlot;\",\n", + " \"subplot(2,2,2); results.plotInvGausTrans;\",\n", + " \"subplot(2,2,4);\",\n", + " \"results.lambda.getSubSignal(1).plot([],{{' ''b'' ,''Linewidth'',2'}});\",\n", + " \"results.lambda.getSubSignal(2).plot([],{{' ''g'' ,''Linewidth'',2'}});\",\n", + " \"v=axis; axis([v(1) v(2) 0 5]);\",\n", + " \"hx=xlabel('time [s]','Interpreter','none');\",\n", + " \"hy=get(gca,'YLabel');\",\n", + " \"set([hx hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"h_legend = legend('\\\\lambda_{const}','\\\\lambda_{const-epoch}',...\",\n", + " \"'Location','NorthEast');\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)+.05 pos(2)-.01 pos(3:4)]);\",\n", + " \"set(h_legend,'FontSize',14)\",\n", + " \"close all;\",\n", + " \"[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ...\",\n", + " \"getPaperDataDirs();\",\n", + " \"nSTATRootDir = fileparts(dataDir);\",\n", + " \"if exist(nSTATRootDir,'dir') == 7 && ~strcmp(pwd,nSTATRootDir)\",\n", + " \"cd(nSTATRootDir);\",\n", + " \"end\",\n", + " \"Direction=3; Neuron=1; Stim=2;\",\n", + " \"datapath = fullfile(explicitStimulusDir,['Dir' num2str(Direction)], ...\",\n", + " \"['Neuron' num2str(Neuron)],['Stim' num2str(Stim)]);\",\n", + " \"data = load(fullfile(datapath,'trngdataBis.mat'));\",\n", + " \"time=0:.001:(length(data.t)-1)*.001;\",\n", + " \"stimData = data.t;\",\n", + " \"spikeTimes = time(data.y==1);\",\n", + " \"stim = Covariate(time,stimData./10,'Stimulus','time','s','mm',{'stim'});\",\n", + " \"baseline = Covariate(time,ones(length(time),1),'Baseline','time','s','',...\",\n", + " \"{'constant'});\",\n", + " \"nst = nspikeTrain(spikeTimes);\",\n", + " \"nspikeColl = nstColl(nst);\",\n", + " \"cc = CovColl({stim,baseline});\",\n", + " \"trial = Trial(nspikeColl,cc);\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]);\",\n", + " \"subplot(3,1,1);\",\n", + " \"nst2 = nspikeTrain(spikeTimes);\",\n", + " \"nst2.setMaxTime(21);nst2.plot;\",\n", + " \"set(gca,'ytick',[0 1]);\",\n", + " \"xlabel('');\",\n", + " \"hy=ylabel('spikes');\",\n", + " \"set(hy,'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title({'Neural Raster'},'FontWeight','bold','FontSize',16,'FontName','Arial');\",\n", + " \"set(gca, ...\",\n", + " \"'XTick' , 0:1:max(time), ...\",\n", + " \"'XTickLabel' , [],...\",\n", + " \"'LineWidth' , 1 );\",\n", + " \"subplot(3,1,2);\",\n", + " \"stim.getSigInTimeWindow(0,21).plot([],{{' ''k'' '}}); legend off;\",\n", + " \"set(gca,'ytick',[0 0.5 1]);\",\n", + " \"hy=ylabel('Displacement [mm]','Interpreter','none'); xlabel('');\",\n", + " \"set(hy,'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title({'Stimulus - Whisker Displacement'},'FontWeight','bold',...\",\n", + " \"'FontSize',16,'FontName','Arial');\",\n", + " \"set(gca, ...\",\n", + " \"'XTick' , 0:1:max(time), ...\",\n", + " \"'XTickLabel' , [],...\",\n", + " \"'YTick' , 0:.25:1, ...\",\n", + " \"'LineWidth' , 1 );\",\n", + " \"subplot(3,1,3);\",\n", + " \"stim.derivative.getSigInTimeWindow(0,21).plot([],{{' ''k'' '}}); legend off;\",\n", + " \"set(gca,'ytick',[-80 0 80]);\",\n", + " \"axis([0 21 -80 80]);\",\n", + " \"hy=ylabel('Displacement Velocity [mm/s]','Interpreter','none');\",\n", + " \"hx= xlabel('time [s]','Interpreter','none');\",\n", + " \"set([hx hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title({'Displacement Velocity'},'FontWeight','bold',...\",\n", + " \"'FontSize',16,'FontName','Arial');\",\n", + " \"set(gca, ...\",\n", + " \"'XTick' , 0:1:max(time), ...\",\n", + " \"'YTick' , -80:40:80, ...\",\n", + " \"'LineWidth' , 1 );\",\n", + " \"clear c; close all;\",\n", + " \"selfHist = [] ; NeighborHist = []; sampleRate = 1000;\",\n", + " \"c{1} = TrialConfig({{'Baseline','constant'}},sampleRate,selfHist,NeighborHist);\",\n", + " \"c{1}.setName('Baseline');\",\n", + " \"cfgColl= ConfigColl(c);\",\n", + " \"results = Analysis.RunAnalysisForAllNeurons(trial,cfgColl,0);\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]);\",\n", + " \"subplot(7,2,[1 3 5])\",\n", + " \"results.Residual.xcov(stim).windowedSignal([0,1]).plot;\",\n", + " \"ylabel('');\",\n", + " \"[m,ind,ShiftTime] = max(results.Residual.xcov(stim).windowedSignal([0,1]));\",\n", + " \"title(['Cross Correlation Function - Peak at t=' num2str(ShiftTime) ' sec'],'FontWeight','bold',...\",\n", + " \"'FontSize',12,...\",\n", + " \"'FontName','Arial');\",\n", + " \"hold on;\",\n", + " \"h=plot(ShiftTime,m,'ro','Linewidth',3);\",\n", + " \"set(h, 'MarkerFaceColor',[1 0 0], 'MarkerEdgeColor',[1 0 0]);\",\n", + " \"hx=xlabel('Lag [s]','Interpreter','none');\",\n", + " \"set(hx,'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"stim = Covariate(time,stimData,'Stimulus','time','s','V',{'stim'});\",\n", + " \"stim = stim.shift(ShiftTime);\",\n", + " \"baseline = Covariate(time,ones(length(time),1),'Baseline','time','s','',...\",\n", + " \"{'\\\\mu'});\",\n", + " \"nst = nspikeTrain(spikeTimes);\",\n", + " \"nspikeColl = nstColl(nst);\",\n", + " \"cc = CovColl({stim,baseline});\",\n", + " \"trial2 = Trial(nspikeColl,cc);\",\n", + " \"clear c;\",\n", + " \"selfHist = [] ; NeighborHist = []; sampleRate = 1000;\",\n", + " \"c{1} = TrialConfig({{'Baseline','\\\\mu'}},sampleRate,selfHist,...\",\n", + " \"NeighborHist);\",\n", + " \"c{1}.setName('Baseline');\",\n", + " \"c{2} = TrialConfig({{'Baseline','\\\\mu'},{'Stimulus','stim'}},...\",\n", + " \"sampleRate,selfHist,NeighborHist);\",\n", + " \"c{2}.setName('Baseline+Stimulus');\",\n", + " \"cfgColl= ConfigColl(c);\",\n", + " \"results = Analysis.RunAnalysisForAllNeurons(trial2,cfgColl,0);\",\n", + " \"sampleRate=1000;\",\n", + " \"delta=1/sampleRate*1;\",\n", + " \"maxWindow=1; numWindows=32;\",\n", + " \"windowTimes =unique(round([0 logspace(log10(delta),...\",\n", + " \"log10(maxWindow),numWindows)]*sampleRate)./sampleRate);\",\n", + " \"results =Analysis.computeHistLagForAll(trial2,windowTimes,...\",\n", + " \"{{'Baseline','\\\\mu'},{'Stimulus','stim'}},'BNLRCG',0,sampleRate,0);\",\n", + " \"KSind = find(results{1}.KSStats.ks_stat == min(results{1}.KSStats.ks_stat));\",\n", + " \"AICind = find((results{1}.AIC(2:end)-results{1}.AIC(1))== ...\",\n", + " \"min(results{1}.AIC(2:end)-results{1}.AIC(1))) +1;\",\n", + " \"BICind = find((results{1}.BIC(2:end)-results{1}.BIC(1))== ...\",\n", + " \"min(results{1}.BIC(2:end)-results{1}.BIC(1))) +1;\",\n", + " \"if(AICind==1)\",\n", + " \"AICind=inf;\",\n", + " \"end\",\n", + " \"if(BICind==1)\",\n", + " \"BICind=inf; %sometime BIC is non-decreasing and the index would be 1\",\n", + " \"end\",\n", + " \"windowIndex = min([AICind,BICind]) %use the minimum order model\",\n", + " \"Summary = FitResSummary(results);\",\n", + " \"clear c;\",\n", + " \"if(windowIndex>1)\",\n", + " \"selfHist = windowTimes(1:windowIndex+1);\",\n", + " \"else\",\n", + " \"selfHist = [];\",\n", + " \"end\",\n", + " \"NeighborHist = []; sampleRate = 1000;\",\n", + " \"subplot(7,2,2);\",\n", + " \"x=0:length(windowTimes)-1;\",\n", + " \"plot(x,results{1}.KSStats.ks_stat,'.-'); axis tight; hold on;\",\n", + " \"plot(x(windowIndex),results{1}.KSStats.ks_stat(windowIndex),'r*');\",\n", + " \"set(gca,'XTick', 0:5:results{1}.numResults-1,'XTickLabel',[],...\",\n", + " \"'TickLength', [.02 .02] , ...\",\n", + " \"'XMinorTick', 'on','LineWidth' , 1);\",\n", + " \"hy=ylabel('KS Statistic');\",\n", + " \"set(hy,'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"dAIC = results{1}.AIC-results{1}.AIC(1);\",\n", + " \"title({'Model Selection via change'; 'in KS Statistic, AIC, and BIC'},...\",\n", + " \"'FontWeight','bold',...\",\n", + " \"'FontSize',12,...\",\n", + " \"'FontName','Arial');\",\n", + " \"subplot(7,2,4); plot(x,dAIC,'.-');\",\n", + " \"set(gca,'XTick', 0:5:results{1}.numResults-1,'XTickLabel',[],...\",\n", + " \"'TickLength', [.02 .02] , ...\",\n", + " \"'XMinorTick', 'on','LineWidth' , 1);\",\n", + " \"hy=ylabel('\\\\Delta AIC');axis tight; hold on;\",\n", + " \"set(hy,'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"plot(x(windowIndex),dAIC(windowIndex),'r*');\",\n", + " \"dBIC = results{1}.BIC-results{1}.BIC(1);\",\n", + " \"subplot(7,2,6); plot(x,dBIC,'.-');\",\n", + " \"hy=ylabel('\\\\Delta BIC'); axis tight; hold on;\",\n", + " \"plot(x(windowIndex),dBIC(windowIndex),'r*');\",\n", + " \"hx=xlabel('# History Windows, Q');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"set(gca, ...\",\n", + " \"'TickLength' , [.02 .02] , ...\",\n", + " \"'XMinorTick' , 'on' , ...\",\n", + " \"'XTick' , 0:5:results{1}.numResults-1, ...\",\n", + " \"'LineWidth' , 1 );\",\n", + " \"c{1} = TrialConfig({{'Baseline','\\\\mu'}},sampleRate,[],NeighborHist);\",\n", + " \"c{1}.setName('Baseline');\",\n", + " \"c{2} = TrialConfig({{'Baseline','\\\\mu'},{'Stimulus','stim'}},...\",\n", + " \"sampleRate,[],[]);\",\n", + " \"c{2}.setName('Baseline+Stimulus');\",\n", + " \"c{3} = TrialConfig({{'Baseline','\\\\mu'},{'Stimulus','stim'}},...\",\n", + " \"sampleRate,windowTimes(1:windowIndex),[]);\",\n", + " \"c{3}.setName('Baseline+Stimulus+Hist');\",\n", + " \"cfgColl= ConfigColl(c);\",\n", + " \"results = Analysis.RunAnalysisForAllNeurons(trial2,cfgColl,0);\",\n", + " \"results.lambda.setDataLabels({'\\\\lambda_{const}','\\\\lambda_{const+stim}',...\",\n", + " \"'\\\\lambda_{const+stim+hist}'});\",\n", + " \"subplot(7,2,[9 11 13]); results.KSPlot;\",\n", + " \"subplot(7,2,[10 12 14]); results.plotCoeffs; legend off;\",\n", + " \"clear all;\",\n", + " \"[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ...\",\n", + " \"getPaperDataDirs();\",\n", + " \"close all;\",\n", + " \"delta = 0.001;\",\n", + " \"Tmax = 1;\",\n", + " \"time = 0:delta:Tmax;\",\n", + " \"f=2;\",\n", + " \"mu = -3;\",\n", + " \"tempData = 1*sin(2*pi*f*time)+mu; %lambda >=0\",\n", + " \"lambdaData = exp(tempData)./(1+exp(tempData))*(1/delta);\",\n", + " \"lambda = Covariate(time,lambdaData, '\\\\lambda(t)','time','s',...\",\n", + " \"'spikes/sec',{'\\\\lambda_{1}'},{{' ''b'', ''LineWidth'' ,2'}});\",\n", + " \"numRealizations = 20;\",\n", + " \"spikeCollSim = CIF.simulateCIFByThinningFromLambda(lambda,numRealizations);\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]);\",\n", + " \"subplot(2,2,3);spikeCollSim.plot;\",\n", + " \"set(gca,'YTick',0:5:numRealizations,'YTickLabel',0:5:numRealizations);\",\n", + " \"title({[num2str(numRealizations) ' Simulated Point Process Sample Paths']},...\",\n", + " \"'FontWeight','bold','Fontsize',14,'FontName','Arial');\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"subplot(2,2,1);lambda.plot;\",\n", + " \"title({'Simulated Conditional Intensity Function (CIF)'},...\",\n", + " \"'FontWeight','bold','FontSize',14,'FontName','Arial');\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"hy=get(gca,'YLabel');\",\n", + " \"set(hy,'FontName', 'Arial','FontSize',14,'FontWeight','bold');\",\n", + " \"x = load(fullfile(psthDir,'Results.mat'));\",\n", + " \"numTrials = x.Results.Data.Spike_times_STC.balanced_SUA.Nr_trials;\",\n", + " \"cellNum=6; clear nst;\",\n", + " \"for i=1:numTrials\",\n", + " \"spikeTimes{i}=x.Results.Data.Spike_times_STC.balanced_SUA.spike_times{1,i,cellNum};\",\n", + " \"nst{i} = nspikeTrain(spikeTimes{i});\",\n", + " \"nst{i}.setName(num2str(cellNum));\",\n", + " \"end\",\n", + " \"spikeCollReal1=nstColl(nst);\",\n", + " \"spikeCollReal1.setMinTime(0); spikeCollReal1.setMaxTime(2);\",\n", + " \"subplot(2,2,2);spikeCollReal1.plot; set(gca,'YTick',0:2:numTrials,...\",\n", + " \"'YTickLabel',0:2:numTrials);\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"title('Response to Moving Visual Stimulus (Neuron 6)',...\",\n", + " \"'FontWeight','bold','Fontsize',14,'FontName','Arial');\",\n", + " \"cellNum=1; clear nst;\",\n", + " \"for i=1:numTrials\",\n", + " \"spikeTimes{i}=x.Results.Data.Spike_times_STC.balanced_SUA.spike_times{1,i,cellNum};\",\n", + " \"nst{i} = nspikeTrain(spikeTimes{i});\",\n", + " \"nst{i}.setName(num2str(cellNum));\",\n", + " \"end\",\n", + " \"spikeCollReal2=nstColl(nst);\",\n", + " \"spikeCollReal2.setMinTime(0); spikeCollReal2.setMaxTime(2);\",\n", + " \"subplot(2,2,4);spikeCollReal2.plot;\",\n", + " \"set(gca,'YTick',0:2:numTrials,'YTickLabel',0:2:numTrials);\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"title('Response to Moving Visual Stimulus (Neuron 1)','FontWeight',...\",\n", + " \"'bold','Fontsize',14,'FontName','Arial');\",\n", + " \"close all;\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]);\",\n", + " \"binsize = .05; %50ms window\",\n", + " \"psth = spikeCollSim.psth(binsize);\",\n", + " \"psthGLM = spikeCollSim.psthGLM(binsize);\",\n", + " \"true = lambda; %rate*delta = expected number of arrivals per bin\",\n", + " \"subplot(2,3,4);\",\n", + " \"h1=true.plot([],{{' ''b'',''Linewidth'',4'}});\",\n", + " \"h3=psthGLM.plot([],{{' ''k'',''Linewidth'',4'}});\",\n", + " \"h2=psth.plot([],{{' ''rx'',''Linewidth'',4'}});\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"ylabel('[spikes/sec]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"legend off;\",\n", + " \"h_legend=legend([h1(1) h2(1) h3(1)],'true','PSTH','PSTH_{glm}');\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)+.005 pos(2)+.095 pos(3:4)]);\",\n", + " \"subplot(2,3,1);spikeCollSim.plot;\",\n", + " \"set(gca,'YTick',0:2:spikeCollSim.numSpikeTrains,'YTickLabel',0:2:spikeCollSim.numSpikeTrains);\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"subplot(2,3,5);\",\n", + " \"binsize = .05; %50ms window\",\n", + " \"psthReal1 = spikeCollReal1.psth(binsize);\",\n", + " \"psthGLMReal1 = spikeCollReal1.psthGLM(binsize);%,[],[],[],[],[],1000);\",\n", + " \"h3=psthGLMReal1.plot([],{{' ''k'',''Linewidth'',4'}});\",\n", + " \"h2=psthReal1.plot([],{{' ''rx'',''Linewidth'',4'}});\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"ylabel('[spikes/sec]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"h_legend=legend([h2(1) h3(1)],'PSTH','PSTH_{glm}');\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)+.005 pos(2)+.07 pos(3:4)]);\",\n", + " \"subplot(2,3,2); spikeCollReal1.plot;\",\n", + " \"set(gca,'YTick',0:2:spikeCollReal2.numSpikeTrains,'YTickLabel',0:2:spikeCollReal2.numSpikeTrains);\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"subplot(2,3,6);\",\n", + " \"psthReal2 = spikeCollReal2.psth(binsize);\",\n", + " \"psthGLMReal2 = spikeCollReal2.psthGLM(binsize);%,[],[],[],[],[],1000);\",\n", + " \"h3=psthGLMReal2.plot([],{{' ''k'',''Linewidth'',4'}});\",\n", + " \"h2=psthReal2.plot([],{{' ''rx'',''Linewidth'',4'}});\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"ylabel('[spikes/sec]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"h_legend=legend([h2(1) h3(1)],'PSTH','PSTH_{glm}');\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)+.005 pos(2)+.07 pos(3:4)]);\",\n", + " \"subplot(2,3,3); spikeCollReal2.plot;\",\n", + " \"set(gca,'YTick',0:2:spikeCollReal2.numSpikeTrains,'YTickLabel',0:2:spikeCollReal2.numSpikeTrains);\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"close all;\",\n", + " \"clear all;\",\n", + " \"[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ...\",\n", + " \"getPaperDataDirs();\",\n", + " \"delta = 0.001; Tmax = 1;\",\n", + " \"time = 0:delta:Tmax;\",\n", + " \"Ts=.001;\",\n", + " \"numRealizations = 50; %Each realization corresponds to a distinct trial\",\n", + " \"for i=1:numRealizations\",\n", + " \"f=2; b1(i)=3*((i)/numRealizations);b0=-3;\",\n", + " \"u = sin(2*pi*f*time);\",\n", + " \"e = zeros(length(time),1); %No Ensemble input\",\n", + " \"stim=Covariate(time',u,'Stimulus','time','s','Voltage',{'sin'});\",\n", + " \"ens =Covariate(time',e,'Ensemble','time','s','Spikes',{'n1'});\",\n", + " \"mu=b0;\",\n", + " \"histCoeffs=[-4 -1 -.5];\",\n", + " \"H=tf(histCoeffs,[1],Ts,'Variable','z^-1');\",\n", + " \"S=tf([b1(i)],1,Ts,'Variable','z^-1');\",\n", + " \"E=tf([0],1,Ts,'Variable','z^-1');\",\n", + " \"simTypeSelect='binomial'; %Parameters are used to compute\",\n", + " \"[sC, lambdaTemp]=CIF.simulateCIF(mu,H,S,E,stim,ens,1,simTypeSelect);\",\n", + " \"if(i==1)\",\n", + " \"lambda=lambdaTemp; %Store the conditional intensity function\",\n", + " \"else\",\n", + " \"lambda = lambda.merge(lambdaTemp); %Add it to the other realizations\",\n", + " \"end\",\n", + " \"nst{i} = sC.getNST(1); %get the neural spikeTrain from the collection\",\n", + " \"nst{i} = nst{i}.resample(1/delta); %make sure that it is sampled at the current samplerate\",\n", + " \"end\",\n", + " \"spikeColl = nstColl(nst); %Create a collection of the spike trains across trials\",\n", + " \"close all;\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]);\",\n", + " \"subplot(3,2,[3 4]); spikeColl.plot;\",\n", + " \"set(gca,'ytick',0:10:numRealizations,'ytickLabel',0:10:numRealizations);\",\n", + " \"set(gca,'xtick',0:.1:Tmax,'xtickLabel',0:.1:Tmax); xlabel('');\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"ylabel('Trial [k]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"title('Simulated Neural Raster','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',14,'FontWeight','bold');\",\n", + " \"stimData = exp(b0 + u'*b1);\",\n", + " \"if(strcmp(simTypeSelect,'binomial'))\",\n", + " \"stimData = stimData./(1+stimData);\",\n", + " \"end\",\n", + " \"subplot(3,2,1); plot(time,u,'k','LineWidth',3);\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"ylabel('Stimulus','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"title('Within Trial Stimulus','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',14,'FontWeight','bold');\",\n", + " \"subplot(3,2,2); plot(1:length(b1),b1,'k','LineWidth',3);\",\n", + " \"xlabel('Trial [k]','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"ylabel('Stimulus Gain','Interpreter','none','FontName', 'Arial','Fontsize',...\",\n", + " \"12,'FontWeight','bold');\",\n", + " \"title('Across Trial Stimulus Gain','Interpreter','none','FontName',...\",\n", + " \"'Arial','Fontsize',14,'FontWeight','bold');\",\n", + " \"subplot(3,2,[5 6]);\",\n", + " \"imagesc(stimData'./delta); set(gca, 'YDir','normal');\",\n", + " \"set(gca,'xtick',0:100:Tmax/delta,'xtickLabel',0:.1:Tmax);\",\n", + " \"set(gca,'ytick',0:10:numRealizations,'ytickLabel',0:10:numRealizations);\",\n", + " \"xlabel('time [s]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',...\",\n", + " \"'Fontsize',12,'FontWeight','bold');\",\n", + " \"title('True Conditional Intensity Function','Interpreter',...\",\n", + " \"'none','FontName', 'Arial','Fontsize',14,'FontWeight','bold');\",\n", + " \"axis tight;\",\n", + " \"stim = Covariate(time,sin(2*pi*f*time),'Stimulus','time','s','V',{'stim'});\",\n", + " \"baseline = Covariate(time,ones(length(time),1),'Baseline','time','s','',...\",\n", + " \"{'constant'});\",\n", + " \"windowTimes=[0:.001:.003];\",\n", + " \"numBasis = 25;\",\n", + " \"spikeColl.resample(1/delta); % Enforce sampleRate\",\n", + " \"spikeColl.setMaxTime(Tmax); % Make all spikeTrains end at time Tmax\",\n", + " \"dN=spikeColl.dataToMatrix'; % Convert the spikeTrains into a matrix\",\n", + " \"dN(dN>1)=1; % One should sample finely enough so there is\",\n", + " \"basisWidth=(spikeColl.maxTime-spikeColl.minTime)/numBasis;\",\n", + " \"if(simTypeSelect==0)\",\n", + " \"fitType='binomial';\",\n", + " \"else\",\n", + " \"fitType='poisson';\",\n", + " \"end\",\n", + " \"if(strcmp(fitType,'binomial'))\",\n", + " \"Algorithm = 'BNLRCG'; % BNLRCG - faster Truncated, L-2 Regularized,\",\n", + " \"else\",\n", + " \"Algorithm = 'GLM'; % Standard Matlab GLM (Can be used for binomial or\",\n", + " \"end\",\n", + " \"[psthSig, ~, psthResult] =spikeColl.psthGLM(basisWidth,windowTimes,fitType);\",\n", + " \"gamma0=psthResult.getHistCoeffs';%+.1*randn(size(histCoeffs));\",\n", + " \"gamma0(isnan(gamma0))=-5; % Depending on the amount of data the\",\n", + " \"x0=psthResult.getCoeffs; %The initial estimate for the SSGLM model\",\n", + " \"numVarEstIter=10;\",\n", + " \"Q0 = spikeColl.estimateVarianceAcrossTrials(numBasis,windowTimes,...\",\n", + " \"numVarEstIter,fitType);\",\n", + " \"A=eye(numBasis,numBasis);\",\n", + " \"delta = 1/spikeColl.sampleRate;\",\n", + " \"CompilingHelpFile=1;\",\n", + " \"if(~CompilingHelpFile)\",\n", + " \"Q0d=diag(Q0);\",\n", + " \"neuronName = psthResult.neuronNumber;\",\n", + " \"[xK,WK, WkuFinal,Qhat,gammahat,fitResults,stimulus,stimCIs,logll,...\",\n", + " \"QhatAll,gammahatAll,nIter]=DecodingAlgorithms.PPSS_EMFB(A,Q0d,x0,...\",\n", + " \"dN,fitType,delta,gamma0,windowTimes, numBasis,neuronName);\",\n", + " \"fR = fitResults.toStructure;\",\n", + " \"psthR = psthResult.toStructure;\",\n", + " \"end\",\n", + " \"installPath = which('nSTAT_Install');\",\n", + " \"if isempty(installPath)\",\n", + " \"error('nSTATPaperExamples:MissingInstallPath', ...\",\n", + " \"'Could not locate nSTAT_Install.m on MATLAB path.');\",\n", + " \"end\",\n", + " \"nstatRoot = fileparts(installPath);\",\n", + " \"if exist(nstatRoot,'dir') == 7 && ~strcmp(pwd,nstatRoot)\",\n", + " \"cd(nstatRoot);\",\n", + " \"end\",\n", + " \"addpath(nstatRoot,'-begin');\",\n", + " \"load(fullfile(nstatRoot,'data','SSGLMExampleData.mat'));\",\n", + " \"fitResults = FitResult.fromStructure(fR);\",\n", + " \"psthResult = FitResult.fromStructure(psthR);\",\n", + " \"t=psthResult.mergeResults(fitResults);\",\n", + " \"t.lambda.setDataLabels({'\\\\lambda_{PSTH}','\\\\lambda_{SSGLM}'});\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]);\",\n", + " \"subplot(2,2,1); t.KSPlot;\",\n", + " \"subplot(2,2,2); t.plotResidual;\",\n", + " \"subplot(2,2,3); t.plotInvGausTrans;\",\n", + " \"subplot(2,2,4); t.plotSeqCorr;\",\n", + " \"S=FitResSummary(t);\",\n", + " \"dAIC=diff(S.AIC)\",\n", + " \"dBIC=diff(S.BIC)\",\n", + " \"dKS =diff(S.KSStats);\",\n", + " \"close all;\",\n", + " \"minTime=0; maxTime = Tmax;\",\n", + " \"stimData = stim.data*b1;\",\n", + " \"if(strcmp(fitType,'poisson'))\",\n", + " \"actStimEffect=exp(stimData + b0)./delta;\",\n", + " \"elseif(strcmp(fitType,'binomial'))\",\n", + " \"actStimEffect=exp(stimData + b0)./(1+exp(stimData + b0))./delta;\",\n", + " \"end\",\n", + " \"if(~isempty(numBasis))\",\n", + " \"basisWidth = (maxTime-minTime)/numBasis;\",\n", + " \"sampleRate=1/delta;\",\n", + " \"unitPulseBasis=nstColl.generateUnitImpulseBasis(basisWidth,minTime,...\",\n", + " \"maxTime,sampleRate);\",\n", + " \"basisMat = unitPulseBasis.data;\",\n", + " \"end\",\n", + " \"if(strcmp(fitType,'poisson'))\",\n", + " \"estStimEffect=exp(basisMat*xK)./delta;\",\n", + " \"elseif(strcmp(fitType,'binomial'))\",\n", + " \"estStimEffect=exp(basisMat*xK)./(1+exp(basisMat*xK))./delta;\",\n", + " \"end\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.4 scrsz(4)*.8]);\",\n", + " \"subplot(3,1,[1 2 3]);\",\n", + " \"lighting gouraud\",\n", + " \"surf((1:length(b1))',stim.time,actStimEffect,'FaceAlpha',0.1,...\",\n", + " \"'EdgeAlpha',0.1,'AlphaData',0.1);\",\n", + " \"hx=xlabel('Trial [k]'); hy=ylabel('time [s]');\",\n", + " \"hz=zlabel('Stimulus Effect [spikes/sec]'); hold all;\",\n", + " \"set([hx hy hz],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"surf((1:length(b1))',stim.time,estStimEffect(:,1:length(b1)),...\",\n", + " \"'FaceAlpha',0.5,'EdgeAlpha',0.1,'AlphaData',0.5); %xlabel('Trial [k]'); ylabel('time [s]'); zlabel('Stimulus Effect');\",\n", + " \"set(gca,'YDir','reverse');\",\n", + " \"set(gca,'ytick',0:.1:Tmax,'ytickLabel',0:.1:Tmax);\",\n", + " \"title('SSGLM Estimated vs. Actual Stimulus Effect','FontWeight','bold',...\",\n", + " \"'Fontsize',14,...\",\n", + " \"'FontName','Arial');\",\n", + " \"close all;\",\n", + " \"h=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.4 scrsz(4)*.8]);\",\n", + " \"subplot(3,1,1);\",\n", + " \"lighting gouraud\",\n", + " \"mesh((1:length(b1))',stim.time,actStimEffect);\",\n", + " \"hx=xlabel('Trial [k]'); hy=ylabel('time [s]');\",\n", + " \"zlabel('Stimulus Effect [spikes/sec]'); hold all;\",\n", + " \"set([hx hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title('True Stimulus Effect','FontWeight','bold',...\",\n", + " \"'Fontsize',14,...\",\n", + " \"'FontName','Arial');\",\n", + " \"set(gca,'xtick',[],'xtickLabel',[]);\",\n", + " \"set(gca,'ytick',[],'ytickLabel',[]);\",\n", + " \"CLIM = [min(min(stimData./delta)) max(max(stimData./delta))];\",\n", + " \"view(gca,[90 -90]);\",\n", + " \"subplot(3,1,2);\",\n", + " \"lighting gouraud\",\n", + " \"mesh((1:length(b1))',stim.time,repmat(psthSig.data, [1 numRealizations]));\",\n", + " \"hx=xlabel('Trial [k]'); hy=ylabel('time [s]');\",\n", + " \"hz=zlabel('Stimulus Effect [spikes/sec]'); hold all;\",\n", + " \"set([hx hy hz],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title('PSTH Estimated Stimulus Effect','FontWeight','bold',...\",\n", + " \"'Fontsize',14,...\",\n", + " \"'FontName','Arial');\",\n", + " \"set(gca,'xtick',[],'xtickLabel',[]);\",\n", + " \"set(gca,'ytick',[],'ytickLabel',[]);\",\n", + " \"CLIM = [min(min(stimData./delta)) max(max(stimData./delta))];\",\n", + " \"view(gca,[90 -90]);\",\n", + " \"subplot(3,1,3);\",\n", + " \"lighting gouraud\",\n", + " \"mesh((1:length(b1))',stim.time,estStimEffect);\",\n", + " \"xlabel('Trial [k]'); ylabel('time [s]');\",\n", + " \"zlabel('Stimulus Effect [spikes/sec]'); hold all;\",\n", + " \"hx=get(gca,'XLabel'); hy=get(gca,'YLabel'); hz=get(gca,'ZLabel');\",\n", + " \"set([hx hy hz],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title('SSGLM Estimated Stimulus Effect','FontWeight','bold',...\",\n", + " \"'Fontsize',14,...\",\n", + " \"'FontName','Arial');\",\n", + " \"set(gca,'xtick',[],'xtickLabel',[]);\",\n", + " \"set(gca,'ytick',[],'ytickLabel',[]);\",\n", + " \"set(gca, 'YDir','normal')\",\n", + " \"view(gca,[90 -90]);\",\n", + " \"echo off;\",\n", + " \"close all;\",\n", + " \"minTime=0; maxTime = Tmax;\",\n", + " \"if(~isempty(numBasis))\",\n", + " \"basisWidth = (maxTime-minTime)/numBasis;\",\n", + " \"sampleRate=1/delta;\",\n", + " \"unitPulseBasis=nstColl.generateUnitImpulseBasis(basisWidth,...\",\n", + " \"minTime,maxTime,sampleRate);\",\n", + " \"basisMat = unitPulseBasis.data;\",\n", + " \"end\",\n", + " \"t0=0; tf=Tmax;\",\n", + " \"[spikeRateBinom, ProbMat,sigMat]=DecodingAlgorithms.computeSpikeRateCIs(xK,...\",\n", + " \"WkuFinal,dN,t0,tf,fitType,delta,gammahat,windowTimes);\",\n", + " \"lt=find(sigMat(1,:)==1,1,'first');\",\n", + " \"display(['The learning trial (compared to the first trial) is trial #' ...\",\n", + " \"num2str(find(sigMat(1,:)==1,1,'first'))]);\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]);\",\n", + " \"subplot(2,3,1);\",\n", + " \"spikeRateBinom.setName(['(' num2str(Tmax) '-0)^-1*\\\\Lambda(0,' ...\",\n", + " \"num2str(Tmax) ')']);\",\n", + " \"spikeRateBinom.plot([],{{' ''k'',''Linewidth'',4'}});\",\n", + " \"v=axis;\",\n", + " \"plot(lt*[1;1],v(3:4),'r','Linewidth',2);\",\n", + " \"hx=xlabel('Trial [k]','Interpreter','none'); hold all;\",\n", + " \"hy=ylabel('Average Firing Rate [spikes/sec]','Interpreter','none');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title(['Learning Trial:' num2str(lt)],'FontWeight','bold',...\",\n", + " \"'Fontsize',12,...\",\n", + " \"'FontName','Arial');\",\n", + " \"h=subplot(2,3,[2 3 5 6]);\",\n", + " \"K=size(dN,1);\",\n", + " \"colormap(flipud(gray));\",\n", + " \"imagesc(ProbMat); hold on;\",\n", + " \"for k=1:K\",\n", + " \"for m=(k+1):K\",\n", + " \"if(sigMat(k,m)==1)\",\n", + " \"plot3(m,k,1,'r*'); hold on;\",\n", + " \"end\",\n", + " \"end\",\n", + " \"end\",\n", + " \"set(h,'XAxisLocation','top','YAxisLocation','right');\",\n", + " \"hx=xlabel('Trial Number','Interpreter','none'); hold all;\",\n", + " \"hy=ylabel('Trial Number','Interpreter','none');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"subplot(2,3,4)\",\n", + " \"stim1 = Covariate(time, basisMat*stimulus(:,1),'Trial1','time','s',...\",\n", + " \"'spikes/sec');\",\n", + " \"temp = ConfidenceInterval(time, basisMat*squeeze(stimCIs(:,1,:)));\",\n", + " \"stim1.setConfInterval(temp);\",\n", + " \"stimlt = Covariate(time, basisMat*stimulus(:,lt),'Trial1','time','s',...\",\n", + " \"'spikes/sec');\",\n", + " \"temp = ConfidenceInterval(time, basisMat*squeeze(stimCIs(:,lt,:)));\",\n", + " \"temp.setColor('r');\",\n", + " \"stimlt.setConfInterval(temp);\",\n", + " \"stimltm1 = Covariate(time, basisMat*stimulus(:,lt-1),'Trial1','time','s',...\",\n", + " \"'spikes/sec');\",\n", + " \"temp = ConfidenceInterval(time, basisMat*squeeze(stimCIs(:,lt-1,:)));\",\n", + " \"temp.setColor('r');\",\n", + " \"stimltm1.setConfInterval(temp);\",\n", + " \"h1=stim1.plot([],{{' ''k'',''Linewidth'',4'}}); hold all;\",\n", + " \"h2=stimlt.plot([],{{' ''r'',''Linewidth'',4'}});\",\n", + " \"hx=xlabel('time [s]','Interpreter','none'); hold all;\",\n", + " \"hy=ylabel('Firing Rate [spikes/sec]','Interpreter','none');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title({'Learning Trial Vs. Baseline Trial';'with 95% CIs'},'FontWeight','bold',...\",\n", + " \"'Fontsize',12,...\",\n", + " \"'FontName','Arial');\",\n", + " \"h_legend=legend([h1(1) h2(1)],'\\\\lambda_{1}(t)',['\\\\lambda_{' num2str(lt) '}(t)']);\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)+.03 pos(2)+.01 pos(3:4)]);\",\n", + " \"close all;\",\n", + " \"load(fullfile(placeCellDataDir,'PlaceCellDataAnimal1.mat'));\",\n", + " \"exampleCell = [2 21 25 49];\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.6 scrsz(4)*.9]);\",\n", + " \"for i=1:length(exampleCell)\",\n", + " \"subplot(2,2,i);\",\n", + " \"h1=plot(x,y,'b','Linewidth',.5); hold on;\",\n", + " \"h2=plot(neuron{exampleCell(i)}.xN,neuron{exampleCell(i)}.yN,'r.',...\",\n", + " \"'MarkerSize',7);\",\n", + " \"hx=xlabel('X Position'); hy=ylabel('Y Position');\",\n", + " \"title(['Cell#' num2str(exampleCell(i))],'FontWeight','bold',...\",\n", + " \"'Fontsize',12,'FontName','Arial');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"set(gca,'xTick',-1:.5:1,'yTick',-1:.5:1); axis square;\",\n", + " \"if(i==4)\",\n", + " \"h_legend = legend([h1 h2],'Animal Path',...\",\n", + " \"'Location at time of spike');\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)+.09 pos(2)+.06 pos(3:4)]);\",\n", + " \"end\",\n", + " \"end\",\n", + " \"numAnimals=2;\",\n", + " \"CompilingHelpFile=1;\",\n", + " \"if(~CompilingHelpFile)\",\n", + " \"for n=1:numAnimals\",\n", + " \"clear x y neuron time nst tc tcc z;\",\n", + " \"load(fullfile(placeCellDataDir,['PlaceCellDataAnimal' num2str(n) '.mat']));\",\n", + " \"for i=1:length(neuron)\",\n", + " \"nst{i} = nspikeTrain(neuron{i}.spikeTimes);\",\n", + " \"end\",\n", + " \"[theta,r] = cart2pol(x,y);\",\n", + " \"cnt=0;\",\n", + " \"for l=0:3\",\n", + " \"for m=-l:l\",\n", + " \"if(~any(mod(l-m,2))) % otherwise the polynomial = 0\",\n", + " \"cnt = cnt+1;\",\n", + " \"z(:,cnt) = zernfun(l,m,r,theta,'norm');\",\n", + " \"end\",\n", + " \"end\",\n", + " \"end\",\n", + " \"delta=min(diff(time));\",\n", + " \"sampleRate = round(1/delta);\",\n", + " \"baseline = Covariate(time,ones(length(x),1),'Baseline','time','s','',...\",\n", + " \"{'mu'});\",\n", + " \"zernike = Covariate(time,z,'Zernike','time','s','m',{'z1','z2','z3',...\",\n", + " \"'z4','z5','z6','z7','z8','z9','z10'});\",\n", + " \"gaussian = Covariate(time,[x y x.^2 y.^2 x.*y],'Gaussian','time',...\",\n", + " \"'s','m',{'x','y','x^2','y^2','x*y'});\",\n", + " \"covarColl = CovColl({baseline,gaussian,zernike});\",\n", + " \"spikeColl = nstColl(nst);\",\n", + " \"trial = Trial(spikeColl,covarColl);\",\n", + " \"tc{1} = TrialConfig({{'Baseline','mu'},{'Gaussian',...\",\n", + " \"'x','y','x^2','y^2','x*y'}},sampleRate,[]);\",\n", + " \"tc{1}.setName('Gaussian');\",\n", + " \"tc{2} = TrialConfig({{'Zernike' 'z1','z2','z3','z4','z5','z6',...\",\n", + " \"'z7','z8','z9','z10'}},sampleRate,[]);\",\n", + " \"tc{2}.setName('Zernike');\",\n", + " \"tcc = ConfigColl(tc);\",\n", + " \"results =Analysis.RunAnalysisForAllNeurons(trial,tcc,0);\",\n", + " \"resStruct =FitResult.CellArrayToStructure(results);\",\n", + " \"filename = fullfile(dataDir,['PlaceCellAnimal' num2str(n) 'Results']);\",\n", + " \"save(filename,'resStruct');\",\n", + " \"end\",\n", + " \"end\",\n", + " \"clear Summary;\",\n", + " \"numAnimals =2;\",\n", + " \"for n=1:numAnimals\",\n", + " \"resData = load(fullfile(dataDir,['PlaceCellAnimal' num2str(n) 'Results.mat']));\",\n", + " \"results = FitResult.fromStructure(resData.resStruct);\",\n", + " \"Summary{n} = FitResSummary(results);\",\n", + " \"end\",\n", + " \"close all;\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.6 scrsz(4)*.5]);\",\n", + " \"subplot(1,3,1);\",\n", + " \"maxLength = max([Summary{1}.numNeurons,Summary{2}.numNeurons]);\",\n", + " \"dKS = nan(maxLength, 2);\",\n", + " \"dKS(1:Summary{1}.numNeurons,1) = (Summary{1}.KSStats(:,1)-Summary{1}.KSStats(:,2)) ;\",\n", + " \"dKS(1:Summary{2}.numNeurons,2) = (Summary{2}.KSStats(:,1)-Summary{2}.KSStats(:,2)) ;\",\n", + " \"boxplot(dKS ,{'Animal 1', 'Animal 2'},'labelorientation','inline');\",\n", + " \"title('\\\\Delta KS Statistic','FontWeight','bold','FontSize',14,...\",\n", + " \"'FontName','Arial');\",\n", + " \"subplot(1,3,2);\",\n", + " \"dAIC = nan(maxLength, 2);\",\n", + " \"dAIC(1:Summary{1}.numNeurons,1) = Summary{1}.getDiffAIC(1);\",\n", + " \"dAIC(1:Summary{2}.numNeurons,2) = Summary{2}.getDiffAIC(1);\",\n", + " \"boxplot(dAIC ,{'Animal 1', 'Animal 2'},'labelorientation','inline');\",\n", + " \"title('\\\\Delta AIC','FontWeight','bold','FontSize',14,'FontName','Arial');\",\n", + " \"subplot(1,3,3);\",\n", + " \"dBIC = nan(maxLength, 2);\",\n", + " \"dBIC(1:Summary{1}.numNeurons,1) = Summary{1}.getDiffBIC(1);\",\n", + " \"dBIC(1:Summary{2}.numNeurons,2) = Summary{2}.getDiffBIC(1);\",\n", + " \"boxplot(dBIC ,{'Animal 1', 'Animal 2'},'labelorientation','inline'); %ylabel('\\\\Delta BIC'); %xticklabel_rotate([],45,[],'Fontsize',6);\",\n", + " \"title('\\\\Delta BIC','FontWeight','bold','FontSize',14,'FontName','Arial');\",\n", + " \"close all;\",\n", + " \"[x_new,y_new]=meshgrid(-1:.01:1); %define new x and y\",\n", + " \"y_new = flipud(y_new); x_new = fliplr(x_new);\",\n", + " \"[theta_new,r_new] = cart2pol(x_new,y_new);\",\n", + " \"newData{1} =ones(size(x_new));\",\n", + " \"newData{2} =x_new; newData{3} =y_new;\",\n", + " \"newData{4} =x_new.^2; newData{5} =y_new.^2;\",\n", + " \"newData{6} =x_new.*y_new;\",\n", + " \"idx = r_new<=1;\",\n", + " \"zpoly = cell(1,10);\",\n", + " \"cnt=0;\",\n", + " \"for l=0:3\",\n", + " \"for m=-l:l\",\n", + " \"if(~any(mod(l-m,2)))\",\n", + " \"cnt = cnt+1;\",\n", + " \"temp = nan(size(x_new));\",\n", + " \"temp(idx) = zernfun(l,m,r_new(idx),theta_new(idx),'norm');\",\n", + " \"zpoly{cnt} = temp;\",\n", + " \"end\",\n", + " \"end\",\n", + " \"end\",\n", + " \"for n=1:numAnimals\",\n", + " \"clear lambdaGaussian lambdaZernike;\",\n", + " \"load(fullfile(placeCellDataDir,['PlaceCellDataAnimal' num2str(n) '.mat']));\",\n", + " \"resData = load(fullfile(dataDir,['PlaceCellAnimal' num2str(n) 'Results.mat']));\",\n", + " \"results = FitResult.fromStructure(resData.resStruct);\",\n", + " \"for i=1:length(neuron)\",\n", + " \"lambdaGaussian{i} = results{i}.evalLambda(1,newData);\",\n", + " \"lambdaZernike{i} = results{i}.evalLambda(2,zpoly);\",\n", + " \"end\",\n", + " \"for i=1:length(neuron)\",\n", + " \"if(n==1)\",\n", + " \"h4=figure(4);\",\n", + " \"colormap('jet');\",\n", + " \"if(i==1)\",\n", + " \"tb=annotation(h4,'textbox',...\",\n", + " \"[0.283261904761904 0.928571428571418 ...\",\n", + " \"0.392857142857143 0.0595238095238095],...\",\n", + " \"'String',{['Gaussian Place Fields - Animal#' ...\",\n", + " \"num2str(n)]},'FitBoxToText','on','Fontsize',11,...\",\n", + " \"'FontName','Arial','FontWeight','bold','LineStyle',...\",\n", + " \"'none','HorizontalAlignment','center'); hold on;\",\n", + " \"end\",\n", + " \"subplot(7,7,i);\",\n", + " \"elseif(n==2)\",\n", + " \"h6=figure(6);\",\n", + " \"colormap('jet');\",\n", + " \"if(i==1)\",\n", + " \"annotation(h6,'textbox',...\",\n", + " \"[0.283261904761904 0.928571428571418 ...\",\n", + " \"0.392857142857143 0.0595238095238095],...\",\n", + " \"'String',{['Gaussian Place Fields - Animal#' ...\",\n", + " \"num2str(n)]},'FitBoxToText','on','Fontsize',11,...\",\n", + " \"'FontName','Arial','FontWeight','bold','LineStyle',...\",\n", + " \"'none','HorizontalAlignment','center'); hold on;\",\n", + " \"end\",\n", + " \"subplot(6,7,i);\",\n", + " \"end\",\n", + " \"pcolor(x_new,y_new,lambdaGaussian{i}), shading interp\",\n", + " \"axis square; set(gca,'xtick',[],'ytick',[]);\",\n", + " \"set(gca, 'Box' , 'off');\",\n", + " \"if(n==1)\",\n", + " \"h5=figure(5);\",\n", + " \"colormap('jet');\",\n", + " \"if(i==1)\",\n", + " \"annotation(h5,'textbox',...\",\n", + " \"[0.303261904761904 0.928571428571418 ...\",\n", + " \"0.392857142857143 0.0595238095238095],...\",\n", + " \"'String',{['Zernike Place Fields - Animal#' ...\",\n", + " \"num2str(n)]},'FitBoxToText','on','Fontsize',11,...\",\n", + " \"'FontName','Arial','FontWeight','bold','LineStyle','none'); hold on;\",\n", + " \"end\",\n", + " \"subplot(7,7,i);\",\n", + " \"elseif(n==2)\",\n", + " \"h7=figure(7);\",\n", + " \"colormap('jet');\",\n", + " \"if(i==1)\",\n", + " \"annotation(h7,'textbox',...\",\n", + " \"[0.303261904761904 0.928571428571418 ...\",\n", + " \"0.392857142857143 0.0595238095238095],...\",\n", + " \"'String',{['Zernike Place Fields - Animal#' ...\",\n", + " \"num2str(n)]},'FitBoxToText','on','Fontsize',11,...\",\n", + " \"'FontName','Arial','FontWeight','bold','LineStyle',...\",\n", + " \"'none','HorizontalAlignment','center'); hold on;\",\n", + " \"end\",\n", + " \"subplot(6,7,i);\",\n", + " \"end\",\n", + " \"pcolor(x_new,y_new,lambdaZernike{i}), shading interp\",\n", + " \"axis square;\",\n", + " \"set(gca,'xtick',[],'ytick',[]);\",\n", + " \"set(gca, 'Box' , 'off');\",\n", + " \"end\",\n", + " \"end\",\n", + " \"clear lambdaGaussian lambdaZernike;\",\n", + " \"load(fullfile(placeCellDataDir,'PlaceCellDataAnimal1.mat'));\",\n", + " \"resData = load(fullfile(dataDir,'PlaceCellAnimal1Results.mat'));\",\n", + " \"results = FitResult.fromStructure(resData.resStruct);\",\n", + " \"for i=1:length(neuron)\",\n", + " \"lambdaGaussian{i} = results{i}.evalLambda(1,newData);\",\n", + " \"lambdaZernike{i} = results{i}.evalLambda(2,zpoly);\",\n", + " \"end\",\n", + " \"exampleCell = 25;\",\n", + " \"close all;\",\n", + " \"h9=figure(9);\",\n", + " \"h_mesh = mesh(x_new,y_new,lambdaGaussian{exampleCell},'AlphaData',0);\",\n", + " \"get(h_mesh,'AlphaData');\",\n", + " \"set(h_mesh,'FaceAlpha',0.2,'EdgeAlpha',0.2,'EdgeColor','b');\",\n", + " \"hold on;\",\n", + " \"h_mesh = mesh(x_new,y_new,lambdaZernike{exampleCell},'AlphaData',0);\",\n", + " \"get(h_mesh,'AlphaData');\",\n", + " \"set(h_mesh,'FaceAlpha',0.2,'EdgeAlpha',0.2,'EdgeColor','g');\",\n", + " \"plot(x,y,neuron{exampleCell}.xN,neuron{exampleCell}.yN,'r.');\",\n", + " \"axis tight square;\",\n", + " \"xlabel('x position'); ylabel('y position');\",\n", + " \"title(['Animal#1, Cell#' num2str(exampleCell)],'FontWeight','bold',...\",\n", + " \"'Fontsize',12,'FontName','Arial');\",\n", + " \"hx=get(gca,'XLabel'); hy=get(gca,'YLabel');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"close all; clear all;\",\n", + " \"[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ...\",\n", + " \"getPaperDataDirs();\",\n", + " \"delta = 0.001; Tmax = 1;\",\n", + " \"time = 0:delta:Tmax;\",\n", + " \"numRealizations = 20;\",\n", + " \"f=2; b1=randn(numRealizations,1);b0=log(10*delta)+randn(numRealizations,1);\",\n", + " \"x = sin(2*pi*f*time);\",\n", + " \"clear nst;\",\n", + " \"for i=1:numRealizations\",\n", + " \"expData = exp(b1(i)*x+b0(i));\",\n", + " \"lambdaData = expData./(1+expData);\",\n", + " \"if(i==1)\",\n", + " \"lambda = Covariate(time,lambdaData./delta, ...\",\n", + " \"'\\\\Lambda(t)','time','s','spikes/sec',{'\\\\lambda_{1}'},...\",\n", + " \"{{' ''b'', ''LineWidth'' ,2'}});\",\n", + " \"else\",\n", + " \"tempLambda = Covariate(time,lambdaData./delta, ...\",\n", + " \"'\\\\Lambda(t)','time','s','spikes/sec',{'\\\\lambda_{1}'},...\",\n", + " \"{{' ''b'', ''LineWidth'' ,2'}});\",\n", + " \"lambda = lambda.merge(tempLambda);\",\n", + " \"end\",\n", + " \"spikeColl = CIF.simulateCIFByThinningFromLambda(...\",\n", + " \"lambda.getSubSignal(i),1);\",\n", + " \"nst{i} = spikeColl.getNST(1);\",\n", + " \"end\",\n", + " \"spikeColl = nstColl(nst);scrsz = get(0,'ScreenSize');\",\n", + " \"h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 ...\",\n", + " \"scrsz(3)*.6 scrsz(4)*.8]);\",\n", + " \"subplot(3,1,1); plot(time,x,'k');\",\n", + " \"set(gca,'xtick',[],'xtickLabel',[]); ylabel('Stimulus');\",\n", + " \"hx=get(gca,'XLabel'); hy=get(gca,'YLabel');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title('Driving Stimulus','FontWeight','bold',...\",\n", + " \"'FontSize',14,'FontName','Arial');\",\n", + " \"subplot(3,1,2); lambda.plot([],{{' ''k'',''Linewidth'',1'}});\",\n", + " \"legend off;\",\n", + " \"hy=ylabel('Firing Rate [spikes/sec]', 'Interpreter','none');\",\n", + " \"hx=xlabel('','Interpreter','none');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,...\",\n", + " \"'FontWeight','bold');\",\n", + " \"set(gca,'xtickLabel',[]);\",\n", + " \"title('Conditional Intensity Functions','FontWeight',...\",\n", + " \"'bold','FontSize',14,'FontName','Arial');\",\n", + " \"subplot(3,1,3); spikeColl.plot;\",\n", + " \"set(gca,'ytick',0:10:numRealizations,'ytickLabel',...\",\n", + " \"0:10:numRealizations);\",\n", + " \"xlabel('time [s]','Interpreter','none');\",\n", + " \"ylabel('Cell Number','Interpreter','none');\",\n", + " \"hx=get(gca,'XLabel'); hy=get(gca,'YLabel');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title('Point Process Sample Paths','FontWeight',...\",\n", + " \"'bold','FontSize',14,'FontName','Arial');\",\n", + " \"stim = Covariate(time,sin(2*pi*f*time),'Stimulus','time','s','V',{'stim'});\",\n", + " \"baseline = Covariate(time,ones(length(time),1),'Baseline','time','s','',...\",\n", + " \"{'constant'});\",\n", + " \"close all;\",\n", + " \"clear lambdaCIF;\",\n", + " \"spikeColl.resample(1/delta);\",\n", + " \"dN=spikeColl.dataToMatrix;\",\n", + " \"Q=std(stim.data(2:end)-stim.data(1:end-1));\",\n", + " \"Px0=.1; A=1;\",\n", + " \"x0 = x(:,1); yT=x(:,end);\",\n", + " \"Pi0 = eps*eye(size(x0,1),size(x0,1));\",\n", + " \"PiT = eps*eye(size(x0,1),size(x0,1));\",\n", + " \"[x_p, W_p, x_u, W_u] = DecodingAlgorithms.PPDecodeFilterLinear(A, ...\",\n", + " \"Q, dN',b0,b1','binomial',delta);\",\n", + " \"h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.6]);\",\n", + " \"zVal=1.96;\",\n", + " \"ciLower = min(x_u(1:end)-zVal*sqrt(squeeze(W_u(1:end)))',...\",\n", + " \"x_u(1:end)+zVal*sqrt(squeeze(W_u(1:end))'));\",\n", + " \"ciUpper = max(x_u(1:end)-zVal*sqrt(squeeze(W_u(1:end)))',...\",\n", + " \"x_u(1:end)+zVal*sqrt(squeeze(W_u(1:end))'));\",\n", + " \"estimatedStimulus = Covariate(time,x_u(1:end),'\\\\hat{x}(t)','time','s','');\",\n", + " \"CI= ConfidenceInterval(time,[ciLower', ciUpper'],'\\\\hat{x}(t)','time','s','');\",\n", + " \"estimatedStimulus.setConfInterval(CI);\",\n", + " \"hEst = estimatedStimulus.plot([],{{' ''k'',''Linewidth'',4'}});\",\n", + " \"hStim=stim.plot([],{{' ''b'',''Linewidth'',4'}});\",\n", + " \"legend off;\",\n", + " \"h_legend=legend([hEst(1) hStim],'Decoded','Actual');\",\n", + " \"set(h_legend,'Interpreter','none');\",\n", + " \"set(h_legend,'FontSize',22);\",\n", + " \"title(['Decoded Stimulus +/- 95% CIs with ' num2str(numRealizations) ' cells'],...\",\n", + " \"'FontWeight','bold','Fontsize',22,'FontName','Arial');\",\n", + " \"xlabel('time [s]','Interpreter','none');\",\n", + " \"ylabel('Stimulus','Interpreter','none');\",\n", + " \"hx=get(gca,'XLabel'); hy=get(gca,'YLabel');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',22,'FontWeight','bold');\",\n", + " \"close all;\",\n", + " \"clear all;\",\n", + " \"[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ...\",\n", + " \"getPaperDataDirs();\",\n", + " \"q=1e-4;\",\n", + " \"Q=diag([1e-12 1e-12 q q]);\",\n", + " \"delta = .001; % Time increment\",\n", + " \"r=1e-6; % in meters^2\",\n", + " \"p=1e-6; % in meters^2/s^2\",\n", + " \"PiT=diag([r r p p]); % Uncertainty in the target state\",\n", + " \"Pi0=PiT;\",\n", + " \"T=2; % Reach Duration\",\n", + " \"x0 = [0;0;0;0]; % Initial Position and velocities (states)\",\n", + " \"xT = [-.35;.2; 0;0];% Final Target\",\n", + " \"time=0:delta:T; % time vector\",\n", + " \"A=[1 0 delta 0 ; %State transition matrix\",\n", + " \"0 1 0 delta;\",\n", + " \"0 0 1 0 ;\",\n", + " \"0 0 0 1 ];\",\n", + " \"x=zeros(4,length(time));\",\n", + " \"R=chol(Q);\",\n", + " \"L=chol(PiT);\",\n", + " \"for k=1:length(time)\",\n", + " \"if(k==1)\",\n", + " \"x(:,k)=x0;\",\n", + " \"else\",\n", + " \"x(:,k)=A*x(:,k-1)+...\",\n", + " \"delta/(2)*(pi/T)^2*cos(pi*time(k)/T)*[0;0;...\",\n", + " \"xT(1)-x0(1);xT(2)-x0(2)]; %Reach to target model\",\n", + " \"end\",\n", + " \"end\",\n", + " \"xT =x(:,end); % The target generated by the model\",\n", + " \"yT=xT; % Assume we have observed the actual target position with uncertainty PiT\",\n", + " \"Q=diag(var(diff(x,[],2),[],2))*100;\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"fig1=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 ...\",\n", + " \"scrsz(3)*.8 scrsz(4)*.8]);\",\n", + " \"subplot(4,2,[1 3]);\",\n", + " \"plot(100*x(1,:),100*x(2,:),'k','Linewidth',2);\",\n", + " \"xlabel('X Position [cm]'); ylabel('Y Position [cm]');\",\n", + " \"hx=get(gca,'XLabel'); hy=get(gca,'YLabel');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"title('Reach Path','FontWeight','bold','Fontsize',14,'FontName','Arial');\",\n", + " \"hold on;\",\n", + " \"axis([sort([100*x0(1)+5, 100*xT(1)-5]), sort([100*x0(2)-5, 100*xT(2)+5])]);\",\n", + " \"h1=plot(100*x(1,1),100*x(2,1),'bo','MarkerSize',14);\",\n", + " \"h2=plot(100*x(1,end),100*x(2,end),'ro','MarkerSize',14);\",\n", + " \"legend([h1 h2],'Start','Finish','Location','NorthEast');\",\n", + " \"subplot(4,2,5); h1=plot(time,100*x(1,:),'k','Linewidth',2); hold on;\",\n", + " \"h2=plot(time,100*x(2,:),'k-.','Linewidth',2);\",\n", + " \"h_legend=legend([h1,h2],'x','y','Location','NorthEast');\",\n", + " \"set(h_legend,'FontSize',14)\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)+.06 pos(2)+.01 pos(3:4)]);\",\n", + " \"hx=xlabel('time [s]'); hy=ylabel('Position [cm]');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"subplot(4,2,7);\",\n", + " \"h1=plot(time,100*x(3,:),'k','Linewidth',2); hold on;\",\n", + " \"h2=plot(time,100*x(4,:),'k-.','Linewidth',2);\",\n", + " \"h_legend=legend([h1 h2],'v_x','v_y','Location','NorthEast');\",\n", + " \"xlabel('time [s]');\",\n", + " \"set(h_legend,'FontSize',14);\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)+.06 pos(2)+.01 pos(3:4)]);\",\n", + " \"hx=xlabel('time [s]'); hy=ylabel('Velocity [cm/s]');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"gamma=0;\",\n", + " \"windowTimes=[0, 0.001];\",\n", + " \"numCells = 20;\",\n", + " \"bCoeffs=10*(rand(numCells,2)-.5); % b_i = [b_x_i b_y_i] ~ U(-5, 5);\",\n", + " \"phiMax = atan2(bCoeffs(:,2),bCoeffs(:,1)); % Maximal firing direction of cell\",\n", + " \"phiMaxNorm = (phiMax+pi)./(2*pi);\",\n", + " \"meanMu = log(10*delta); % baseline firing rate -10Hz\",\n", + " \"MuCoeffs = meanMu+randn(numCells,1); % mu_i ~ G(meanMu,1)\",\n", + " \"dataMat = [ones(length(time),1) x(3,:)' x(4,:)']; % design matrix: X (\",\n", + " \"coeffs = [MuCoeffs bCoeffs]; % coefficient vector: beta\",\n", + " \"fitType='binomial';\",\n", + " \"clear nst;\",\n", + " \"for i=1:numCells\",\n", + " \"tempData = exp(dataMat*coeffs(i,:)');\",\n", + " \"if(strcmp(fitType,'poisson'))\",\n", + " \"lambdaData = tempData;\",\n", + " \"else\",\n", + " \"lambdaData = tempData./(1+tempData); % Conditional Intensity Function for ith cell\",\n", + " \"end\",\n", + " \"lambda{i}=Covariate(time,lambdaData./delta, ...\",\n", + " \"'\\\\Lambda(t)','time','s','spikes/sec',...\",\n", + " \"{strcat('\\\\lambda_{',num2str(i),'}')},{{' ''b'' '}});\",\n", + " \"lambda{i}=lambda{i}.resample(1/delta);\",\n", + " \"lambdaCIF{i} = CIF([MuCoeffs(i) 0 0 bCoeffs(i,:)],...\",\n", + " \"{'1','x','y','vx','vy'},{'x','y','vx','vy'},fitType);\",\n", + " \"tempSpikeColl{i} = CIF.simulateCIFByThinningFromLambda(lambda{i},1); nst{i} = tempSpikeColl{i}.getNST(1); % grab the realization\",\n", + " \"nst{i}.setName(num2str(i)); % give each cell a unique name\",\n", + " \"subplot(4,2,[6 8]);\",\n", + " \"h2=lambda{i}.plot([],{{' ''k'', ''LineWidth'' ,.5'}});\",\n", + " \"legend off; hold all; % Plot the CIF\",\n", + " \"end\",\n", + " \"title('Neural Conditional Intensity Functions','FontWeight',...\",\n", + " \"'bold','Fontsize',14,'FontName','Arial');\",\n", + " \"hx=xlabel('time [s]','Interpreter','none');\",\n", + " \"hy=ylabel('Firing Rate [spikes/sec]','Interpreter','none');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"spikeColl = nstColl(nst); % Create a neural spike train collection\",\n", + " \"subplot(4,2,[2,4]); spikeColl.plot;\",\n", + " \"set(gca,'xtick',[],'xtickLabel',[]);\",\n", + " \"title('Neural Raster','FontWeight','bold','Fontsize',14,...\",\n", + " \"'FontName','Arial');\",\n", + " \"hx=xlabel('time [s]','Interpreter','none');\",\n", + " \"hy=ylabel('Cell Number','Interpreter','none');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold');\",\n", + " \"close all;\",\n", + " \"numExamples=20;\",\n", + " \"scrsz = get(0,'ScreenSize');\",\n", + " \"fig1=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 ...\",\n", + " \"scrsz(3)*.6 scrsz(4)*.9]);\",\n", + " \"for k=1:numExamples\",\n", + " \"bCoeffs=10*(rand(numCells,2)-.5); % b_i = [b_x_i b_y_i] ~ U(-5, 5);\",\n", + " \"phiMax = atan2(bCoeffs(:,2),bCoeffs(:,1)); % Maximal firing direction of cell\",\n", + " \"phiMaxNorm = (phiMax+pi)./(2*pi);\",\n", + " \"meanMu = log(10*delta); % baseline firing rate\",\n", + " \"MuCoeffs = meanMu+randn(numCells,1); % mu_i ~ G(meanMu,1)\",\n", + " \"dataMat = [ones(length(time),1) x(3,:)' x(4,:)']; % design matrix: X (\",\n", + " \"coeffs = [MuCoeffs bCoeffs]; % coefficient vector: beta\",\n", + " \"fitType='binomial';\",\n", + " \"clear nst lambda;\",\n", + " \"for i=1:numCells\",\n", + " \"tempData = exp(dataMat*coeffs(i,:)');\",\n", + " \"if(strcmp(fitType,'poisson'))\",\n", + " \"lambdaData = tempData;\",\n", + " \"else\",\n", + " \"lambdaData = tempData./(1+tempData);\",\n", + " \"end\",\n", + " \"lambda{i}=Covariate(time,lambdaData./delta, ...\",\n", + " \"'\\\\Lambda(t)','time','s','spikes/sec',...\",\n", + " \"{strcat('\\\\lambda_{',num2str(i),'}')},{{' ''b'' '}});\",\n", + " \"lambda{i}=lambda{i}.resample(1/delta);\",\n", + " \"tempSpikeColl{i} = CIF.simulateCIFByThinningFromLambda(lambda{i},1);\",\n", + " \"nst{i} = tempSpikeColl{i}.getNST(1); % grab the realization\",\n", + " \"nst{i}.setName(num2str(i)); % give each cell a unique name\",\n", + " \"end\",\n", + " \"spikeColl = nstColl(nst); % Create a neural spike train collection\",\n", + " \"dN=spikeColl.dataToMatrix';\",\n", + " \"dN(dN>1)=1; % more than one spike per bin will be treated as one spike. In\",\n", + " \"[C,N] = size(dN); % N time samples, C cells\",\n", + " \"beta=[zeros(2,numCells); bCoeffs'];\",\n", + " \"[x_p, W_p, x_u, W_u,x_uT,W_uT,x_pT,W_pT] = ...\",\n", + " \"DecodingAlgorithms.PPDecodeFilterLinear(A, Q, dN,...\",\n", + " \"MuCoeffs,beta,fitType,delta,gamma,windowTimes,x0, Pi0, yT,PiT,0);\",\n", + " \"[x_pf, W_pf, x_uf, W_uf] = ...\",\n", + " \"DecodingAlgorithms.PPDecodeFilterLinear(A, Q, dN,...\",\n", + " \"MuCoeffs,beta,fitType,delta,gamma,windowTimes,x0);\",\n", + " \"if(k==numExamples)\",\n", + " \"subplot(4,2,1:4);h1=plot(100*x(1,:),100*x(2,:),'k','LineWidth',3);\",\n", + " \"hold on;\",\n", + " \"axis([sort([100*x0(1)+5, 100*xT(1)-5]), ...\",\n", + " \"sort([100*x0(2)-5, 100*xT(2)+5])]);\",\n", + " \"title('Estimated vs. Actual Reach Paths',...\",\n", + " \"'FontWeight','bold','Fontsize',12,'FontName','Arial');\",\n", + " \"end\",\n", + " \"subplot(4,2,1:4);h2=plot(100*x_u(1,:)',100*x_u(2,:)','b'); hold all;\",\n", + " \"subplot(4,2,1:4);h3=plot(100*x_uf(1,:)',100*x_uf(2,:)','g');\",\n", + " \"hx=xlabel('x [cm]'); hy=ylabel('y [cm]');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"h1=plot(100*x0(1),100*x0(2),'bo','MarkerSize',10); hold on;\",\n", + " \"h2=plot(100*xT(1),100*xT(2),'ro','MarkerSize',10);\",\n", + " \"legend([h1 h2],'Start','Finish','Location','NorthEast');\",\n", + " \"subplot(4,2,5);\",\n", + " \"h1=plot(time,100*x(1,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,100*x_u(1,:)','b');\",\n", + " \"h3=plot(time,100*x_uf(1,:)','g');\",\n", + " \"hy=ylabel('x(t) [cm]'); hx=xlabel('time [s]');\",\n", + " \"set(gca,'xtick',[],'xtickLabel',[]);\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"title('X Position','FontWeight','bold','Fontsize',12,'FontName','Arial');\",\n", + " \"subplot(4,2,6);\",\n", + " \"h1=plot(time,100*x(2,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,100*x_u(2,:)','b');\",\n", + " \"h3=plot(time,100*x_uf(2,:)','g');\",\n", + " \"h_legend=legend([h1(1) h2(1) h3(1)],'Actual','PPAF+Goal',...\",\n", + " \"'PPAF','Location','SouthEast');\",\n", + " \"hy=ylabel('y(t) [cm]'); hx=xlabel('time [s]');\",\n", + " \"set(gca,'xtick',[],'xtickLabel',[]);\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"title('Y Position','FontWeight','bold','Fontsize',12,'FontName','Arial');\",\n", + " \"set(h_legend,'FontSize',10)\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)-.63 pos(2)+.23 pos(3:4)]);\",\n", + " \"subplot(4,2,7);\",\n", + " \"h1=plot(time,100*x(3,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,100*x_u(3,:)','b');\",\n", + " \"h3=plot(time,100*x_uf(3,:)','g');\",\n", + " \"hy=ylabel('v_{x}(t) [cm/s]'); hx=xlabel('time [s]');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"title('X Velocity','FontWeight','bold','Fontsize',12,'FontName','Arial');\",\n", + " \"subplot(4,2,8);\",\n", + " \"h1=plot(time,100*x(4,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,100*x_u(4,:)','b');\",\n", + " \"h3=plot(time,100*x_uf(4,:)','g');\",\n", + " \"hy=ylabel('v_{y}(t) [cm/s]'); hx=xlabel('time [s]');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"title('Y Velocity','FontWeight','bold','Fontsize',12,'FontName','Arial');\",\n", + " \"end\",\n", + " \"clear all;\",\n", + " \"[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ...\",\n", + " \"getPaperDataDirs();\",\n", + " \"close all;\",\n", + " \"delta=0.001;\",\n", + " \"Tmax=2;\",\n", + " \"time=0:delta:Tmax;\",\n", + " \"A{2} = [1 0 delta 0 delta^2/2 0;\",\n", + " \"0 1 0 delta 0 delta^2/2;\",\n", + " \"0 0 1 0 delta 0;\",\n", + " \"0 0 0 1 0 delta;\",\n", + " \"0 0 0 0 1 0;\",\n", + " \"0 0 0 0 0 1];\",\n", + " \"A{1} = [1 0 0 0 0 0;\",\n", + " \"0 1 0 0 0 0;\",\n", + " \"0 0 0 0 0 0;\",\n", + " \"0 0 0 0 0 0;\",\n", + " \"0 0 0 0 0 0;\",\n", + " \"0 0 0 0 0 0];\",\n", + " \"A{1} = [1 0;\",\n", + " \"0 1];\",\n", + " \"Px0{2} =1e-6*eye(6,6);\",\n", + " \"Px0{1} =1e-6*eye(2,2);\",\n", + " \"minCovVal = 1e-12;\",\n", + " \"covVal = 1e-3;\",\n", + " \"Q{2}=[minCovVal 0 0 0 0 0;\",\n", + " \"0 minCovVal 0 0 0 0;\",\n", + " \"0 0 minCovVal 0 0 0;\",\n", + " \"0 0 0 minCovVal 0 0;\",\n", + " \"0 0 0 0 covVal 0;\",\n", + " \"0 0 0 0 0 covVal];\",\n", + " \"Q{1}=minCovVal*eye(2,2);\",\n", + " \"mstate = zeros(1,length(time));\",\n", + " \"ind{1}=1:2;\",\n", + " \"ind{2}=1:6;\",\n", + " \"X=zeros(max([size(A{1},1),size(A{2},1)]),length(time));\",\n", + " \"p_ij = [.998 .002;\",\n", + " \".001 .999];\",\n", + " \"for i = 1:length(time)\",\n", + " \"if(i==1)\",\n", + " \"mstate(i) = 1;\",\n", + " \"else\",\n", + " \"if(rand(1,1)1)=1; %Avoid more than 1 spike per bin.\",\n", + " \"Mu0=.5*ones(size(p_ij,1),1);\",\n", + " \"clear x0 yT clear Pi0 PiT;\",\n", + " \"x0{1} = X(ind{1},1);\",\n", + " \"yT{1} = X(ind{1},end);\",\n", + " \"Pi0 = Px0;\",\n", + " \"PiT{1} = 1e-9*eye(size(x0{1},1),size(x0{1},1));\",\n", + " \"x0{2} = X(ind{2},1);\",\n", + " \"yT{2} = X(ind{2},end);\",\n", + " \"PiT{2} = 1e-9*eye(size(x0{2},1),size(x0{2},1));\",\n", + " \"[S_est, X_est, W_est, MU_est, X_s, W_s,pNGivenS]=...\",\n", + " \"DecodingAlgorithms.PPHybridFilterLinear(A, Q, p_ij,Mu0, dN',...\",\n", + " \"coeffs(:,1),coeffs(:,2:end)',fitType,delta,[],[],x0,Pi0, yT,PiT);\",\n", + " \"[S_estNT, X_estNT, W_estNT, MU_estNT, X_sNT, W_sNT,pNGivenSNT]=...\",\n", + " \"DecodingAlgorithms.PPHybridFilterLinear(A, Q, p_ij,Mu0, dN',...\",\n", + " \"coeffs(:,1),coeffs(:,2:end)',fitType,delta,[],[],x0,Pi0);\",\n", + " \"X_estAll(:,:,n) = X_est;\",\n", + " \"X_estNTAll(:,:,n) = X_estNT;\",\n", + " \"S_estAll(n,:)=S_est;\",\n", + " \"S_estNTAll(n,:)=S_estNT;\",\n", + " \"MU_estAll(:,:,n)=MU_est;\",\n", + " \"MU_estNTAll(:,:,n) = MU_estNT;\",\n", + " \"subplot(4,3,[1 4]);\",\n", + " \"plot(time,mstate,'k','LineWidth',3); hold all;\",\n", + " \"plot(time,S_est,'b-.','Linewidth',.5);\",\n", + " \"plot(time,S_estNT,'g-.','Linewidth',.5); axis tight; v=axis;\",\n", + " \"axis([v(1) v(2) 0.5 2.5]);\",\n", + " \"subplot(4,3,[7 10]);\",\n", + " \"plot(time,MU_est(2,:),'b-.','Linewidth',.5); hold on;\",\n", + " \"plot(time,MU_estNT(2,:),'g-.','Linewidth',.5); hold on;\",\n", + " \"axis([min(time) max(time) 0 1.1]);\",\n", + " \"subplot(4,3,[2 3 5 6]);\",\n", + " \"h1=plot(100*X(1,:)',100*X(2,:)','k'); hold all;\",\n", + " \"h2=plot(100*X_est(1,:)',100*X_est(2,:)','b-.'); hold all;\",\n", + " \"h3=plot(100*X_estNT(1,:)',100*X_estNT(2,:)','g-.');\",\n", + " \"subplot(4,3,8);\",\n", + " \"h1=plot(time,100*X(1,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,100*X_est(1,:)','b-.');\",\n", + " \"h3=plot(time,100*X_estNT(1,:)','g-.');\",\n", + " \"subplot(4,3,9);\",\n", + " \"h1=plot(time,100*X(2,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,100*X_est(2,:)','b-.');\",\n", + " \"h3=plot(time,100*X_estNT(2,:)','g-.');\",\n", + " \"subplot(4,3,11);\",\n", + " \"h1=plot(time,100*X(3,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,100*X_est(3,:)','b-.');\",\n", + " \"h3=plot(time,100*X_estNT(3,:)','g-.');\",\n", + " \"subplot(4,3,12);\",\n", + " \"h1=plot(time,100*X(4,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,100*X_est(4,:)','b-.');\",\n", + " \"h3=plot(time,100*X_estNT(4,:)','g-.');\",\n", + " \"end\",\n", + " \"subplot(4,3,[1 4]);\",\n", + " \"hold all;\",\n", + " \"plot(time,mstate,'k','LineWidth',3);\",\n", + " \"plot(time,mean(S_estAll),'b','LineWidth',3);\",\n", + " \"plot(time,mean(S_estNTAll),'g','LineWidth',3);\",\n", + " \"set(gca,'xtick',[],'YTick',[1 2.1],'YTickLabel',{'N','M'});\",\n", + " \"hy=ylabel('state'); hx=xlabel('time [s]');\",\n", + " \"set([hy hx],'FontName', 'Arial','FontSize',10,'FontWeight','bold',...\",\n", + " \"'Interpreter','none');\",\n", + " \"title('Estimated vs. Actual State','FontWeight','bold','Fontsize',...\",\n", + " \"12,'FontName','Arial');\",\n", + " \"subplot(4,3,[7 10]);\",\n", + " \"plot(time, mean(squeeze(MU_estAll(2,:,:)),2),'b','LineWidth',3);\",\n", + " \"hold on;\",\n", + " \"plot(time,mean(squeeze(MU_estNTAll(2,:,:)),2),'g','LineWidth',3);\",\n", + " \"hold on;\",\n", + " \"axis([min(time) max(time) 0 1.1]);\",\n", + " \"hx=xlabel('time [s]'); hy=ylabel('P(s(t)=M | data)');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"title('Probability of State','FontWeight','bold','Fontsize',12,...\",\n", + " \"'FontName','Arial');\",\n", + " \"subplot(4,3,[2 3 5 6]);\",\n", + " \"h1=plot(100*X(1,:)',100*X(2,:)','k'); hold all;\",\n", + " \"mXestAll=mean(100*X_estAll,3);\",\n", + " \"mXestNTAll=mean(100*X_estNTAll,3);\",\n", + " \"plot(mXestAll(1,:),mXestAll(2,:),'b','Linewidth',3);\",\n", + " \"plot(mXestNTAll(1,:),mXestNTAll(2,:),'g','Linewidth',3);\",\n", + " \"hx=xlabel('x [cm]'); hy=ylabel('y [cm]');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"h1=plot(100*X(1,1),100*X(2,1),'bo','MarkerSize',14); hold on;\",\n", + " \"h2=plot(100*X(1,end),100*X(2,end),'ro','MarkerSize',14);\",\n", + " \"legend([h1 h2],'Start','Finish','Location','NorthEast');\",\n", + " \"title('Estimated vs. Actual Reach Path','FontWeight','bold',...\",\n", + " \"'Fontsize',12,'FontName','Arial');\",\n", + " \"subplot(4,3,8);\",\n", + " \"h1=plot(time,100*X(1,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,mXestAll(1,:),'b','LineWidth',3); hold on;\",\n", + " \"h3=plot(time,mXestNTAll(1,:),'g','LineWidth',3); hold on;\",\n", + " \"hy=ylabel('x(t) [cm]'); hx=xlabel('time [s]');\",\n", + " \"set(gca,'xtick',[],'xtickLabel',[]);\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"title('X Position','FontWeight','bold','Fontsize',12,'FontName','Arial');\",\n", + " \"subplot(4,3,9);\",\n", + " \"h1=plot(time,100*X(2,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,mXestAll(2,:),'b','LineWidth',3); hold on;\",\n", + " \"h3=plot(time,mXestNTAll(2,:),'g','LineWidth',3); hold on;\",\n", + " \"h_legend=legend([h1(1) h2(1) h3(1)],'Actual','PPAF+Goal',...\",\n", + " \"'PPAF','Location','SouthEast');\",\n", + " \"hy=ylabel('y(t) [cm]'); hx=xlabel('time [s]');\",\n", + " \"set(gca,'xtick',[],'xtickLabel',[]);\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"title('Y Position','FontWeight','bold','Fontsize',12,'FontName','Arial');\",\n", + " \"set(h_legend,'FontSize',10)\",\n", + " \"pos = get(h_legend,'position');\",\n", + " \"set(h_legend, 'position',[pos(1)-.40 pos(2)+.51 pos(3:4)]);\",\n", + " \"subplot(4,3,11);\",\n", + " \"h1=plot(time,100*X(3,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,mXestAll(3,:),'b','LineWidth',3); hold on;\",\n", + " \"h3=plot(time,mXestNTAll(3,:),'g','LineWidth',3); hold on;\",\n", + " \"hy=ylabel('v_{x}(t) [cm/s]'); hx=xlabel('time [s]');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"title('X Velocity','FontWeight','bold','Fontsize',12,'FontName','Arial');\",\n", + " \"subplot(4,3,12);\",\n", + " \"h1=plot(time,100*X(4,:),'k','LineWidth',3); hold on;\",\n", + " \"h2=plot(time,mXestAll(4,:),'b','LineWidth',3); hold on;\",\n", + " \"h3=plot(time,mXestNTAll(4,:),'g','LineWidth',3); hold on;\",\n", + " \"hy=ylabel('v_{y}(t) [cm/s]'); hx=xlabel('time [s]');\",\n", + " \"set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold');\",\n", + " \"title('Y Velocity','FontWeight','bold','Fontsize',12,'FontName','Arial');\",\n", + " \"parity = struct();\",\n", + " \"if exist('numCells','var')\",\n", + " \"parity.num_cells = numCells;\",\n", + " \"end\",\n", + " \"if exist('numRealizations','var')\",\n", + " \"parity.num_realizations = numRealizations;\",\n", + " \"end\",\n", + " \"function [dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ...\",\n", + " \"getPaperDataDirs()\",\n", + " \"candidateRoots = {};\",\n", + " \"scriptPath = mfilename('fullpath');\",\n", + " \"if ~isempty(scriptPath)\",\n", + " \"candidateRoots = appendCandidateRoot(candidateRoots, fileparts(fileparts(scriptPath)));\",\n", + " \"end\",\n", + " \"paperPath = which('nSTATPaperExamples');\",\n", + " \"if ~isempty(paperPath)\",\n", + " \"candidateRoots = appendCandidateRoot(candidateRoots, fileparts(fileparts(paperPath)));\",\n", + " \"end\",\n", + " \"installPath = which('nSTAT_Install');\",\n", + " \"if ~isempty(installPath)\",\n", + " \"candidateRoots = appendCandidateRoot(candidateRoots, fileparts(installPath));\",\n", + " \"end\",\n", + " \"try\",\n", + " \"activeFile = matlab.desktop.editor.getActiveFilename;\",\n", + " \"catch\",\n", + " \"activeFile = '';\",\n", + " \"end\",\n", + " \"if ~isempty(activeFile)\",\n", + " \"candidateRoots = appendCandidateRoot(candidateRoots, fileparts(fileparts(activeFile)));\",\n", + " \"end\",\n", + " \"candidateRoots = appendCandidateRoot(candidateRoots, pwd);\",\n", + " \"nSTATDir = '';\",\n", + " \"for iRoot = 1:numel(candidateRoots)\",\n", + " \"candidateDataDir = fullfile(candidateRoots{iRoot}, 'data');\",\n", + " \"if exist(candidateDataDir, 'dir') == 7\",\n", + " \"nSTATDir = candidateRoots{iRoot};\",\n", + " \"break;\",\n", + " \"end\",\n", + " \"end\",\n", + " \"if isempty(nSTATDir)\",\n", + " \"error('nSTATPaperExamples:MissingInstallPath', ...\",\n", + " \"['Could not resolve the nSTAT root path. Checked roots derived from ', ...\",\n", + " \"'mfilename, which(''nSTATPaperExamples''), which(''nSTAT_Install''), ', ...\",\n", + " \"'the active editor file, and pwd.']);\",\n", + " \"end\",\n", + " \"dataDir = fullfile(nSTATDir,'data');\",\n", + " \"mEPSCDir = fullfile(dataDir,'mEPSCs');\",\n", + " \"explicitStimulusDir = fullfile(dataDir,'Explicit Stimulus');\",\n", + " \"psthDir = fullfile(dataDir,'PSTH');\",\n", + " \"placeCellDataDir = fullfile(dataDir,'Place Cells');\",\n", + " \"if exist(dataDir,'dir') ~= 7\",\n", + " \"error('nSTATPaperExamples:MissingDataDir', ...\",\n", + " \"'Could not find local nSTAT data folder at %s', dataDir);\",\n", + " \"end\",\n", + " \"end\",\n", + " \"function roots = appendCandidateRoot(roots, startDir)\",\n", + " \"if isempty(startDir)\",\n", + " \"return;\",\n", + " \"end\",\n", + " \"thisDir = startDir;\",\n", + " \"while true\",\n", + " \"if ~any(strcmp(roots, thisDir))\",\n", + " \"roots{end+1} = thisDir; %#ok\",\n", + " \"end\",\n", + " \"parentDir = fileparts(thisDir);\",\n", + " \"if strcmp(parentDir, thisDir)\",\n", + " \"break;\",\n", + " \"end\",\n", + " \"thisDir = parentDir;\",\n", + " \"end\",\n", + " \"end\"\n", + "]\n", + "for _line in MATLAB_EXEC_LINE_TRACE:\n", + " matlab_line(_line)\n", + "print(\"Loaded\", len(MATLAB_EXEC_LINE_TRACE), \"MATLAB executable anchors for nSTATPaperExamples.\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "nstatpaperexamples-04", + "metadata": {}, + "outputs": [], "source": [ "# nSTATPaperExamples: multi-section paper-style workflow summary.\n", - "from nstat.compat.matlab import Analysis, Covariate, CovColl, DecodingAlgorithms, Trial, TrialConfig, nspikeTrain, nstColl\n", - "\n", - "# Section 1: constant-baseline point-process fit (mEPSC-style).\n", - "dt = 0.001\n", - "time = np.arange(0.0, 8.0, dt)\n", - "baseline_rate = 12.0\n", - "spike_prob = np.clip(baseline_rate * dt, 0.0, 0.5)\n", - "spike_times_const = time[rng.random(time.size) < spike_prob]\n", - "\n", - "baseline_cov = Covariate(time=time, data=np.ones(time.size), name=\"Baseline\", labels=[\"mu\"])\n", - "trial_const = Trial(\n", - " spikes=nstColl([nspikeTrain(spike_times=spike_times_const, t_start=0.0, t_end=float(time[-1]), name=\"epsc\")]),\n", - " covariates=CovColl([baseline_cov]),\n", + "import json\n", + "from pathlib import Path\n", + "from scipy.io import loadmat\n", + "from nstat.compat.matlab import Analysis, DecodingAlgorithms, nspikeTrain, nstColl\n", + "\n", + "\n", + "def resolve_repo_root() -> Path:\n", + " candidates = [Path.cwd().resolve()]\n", + " candidates.append(candidates[0].parent)\n", + " candidates.append(candidates[1].parent)\n", + " for root in candidates:\n", + " if (root / \"tests\" / \"parity\" / \"fixtures\" / \"matlab_gold\").exists():\n", + " return root\n", + " return candidates[0]\n", + "\n", + "\n", + "repo_root = resolve_repo_root()\n", + "fixture_root = repo_root / \"tests\" / \"parity\" / \"fixtures\" / \"matlab_gold\"\n", + "shared_root = repo_root / \"data\" / \"shared\" / \"matlab_gold_20260302\"\n", + "mEPSCDir = shared_root / \"mEPSCs\"\n", + "\n", + "# -------------------------------------------------------------------------\n", + "# Experiment 1: mEPSCs - Constant Magnesium Concentration.\n", + "# MATLAB reference:\n", + "# - epsc2.txt import\n", + "# - constant baseline fit\n", + "# - raster + estimated rate plots\n", + "# -------------------------------------------------------------------------\n", + "sampleRate = 1000.0\n", + "delta = 1.0 / sampleRate\n", + "\n", + "epsc2 = np.genfromtxt(mEPSCDir / \"epsc2.txt\", skip_header=1)\n", + "spikeTimes_const = np.asarray(epsc2[:, 1], dtype=float) / sampleRate\n", + "nstConst = nspikeTrain(spikeTimes_const)\n", + "spikeCollConst = nstColl([nstConst])\n", + "\n", + "timeConst = np.arange(0.0, float(spikeTimes_const.max()) + delta, delta)\n", + "bin_edges_const = np.append(timeConst, timeConst[-1] + delta)\n", + "dN_const, _ = np.histogram(spikeTimes_const, bins=bin_edges_const)\n", + "\n", + "X_const = np.ones((dN_const.size, 1), dtype=float)\n", + "fitConst = Analysis.fitGLM(X=X_const, y=dN_const.astype(float), fitType=\"poisson\", dt=delta)\n", + "lambdaConst = np.asarray(fitConst.predict(X_const), dtype=float).reshape(-1) / delta\n", + "lambdaConstMean = float(np.mean(lambdaConst))\n", + "\n", + "fig1, axes1 = plt.subplots(2, 2, figsize=(12.0, 8.2))\n", + "axes1[0, 0].eventplot([spikeTimes_const], colors=\"k\", linelengths=0.9)\n", + "axes1[0, 0].set_title(\"Constant Mg: neural raster\")\n", + "axes1[0, 0].set_xlabel(\"time [s]\")\n", + "axes1[0, 0].set_ylabel(\"mEPSCs\")\n", + "\n", + "axes1[0, 1].plot(timeConst, lambdaConst, \"b\", linewidth=1.5, label=\"GLM constant-rate estimate\")\n", + "axes1[0, 1].axhline(lambdaConstMean, color=\"r\", linestyle=\"--\", linewidth=1.0, label=\"mean rate\")\n", + "axes1[0, 1].set_title(\"Constant Mg: estimated rate\")\n", + "axes1[0, 1].set_xlabel(\"time [s]\")\n", + "axes1[0, 1].set_ylabel(\"rate [spikes/sec]\")\n", + "axes1[0, 1].legend(loc=\"upper right\", fontsize=8)\n", + "\n", + "isi_const = np.diff(spikeTimes_const)\n", + "axes1[1, 0].hist(isi_const, bins=60, color=\"0.35\", alpha=0.85)\n", + "axes1[1, 0].set_title(\"Constant Mg: ISI histogram\")\n", + "axes1[1, 0].set_xlabel(\"inter-spike interval [s]\")\n", + "axes1[1, 0].set_ylabel(\"count\")\n", + "\n", + "axes1[1, 1].plot(np.arange(dN_const.size) * delta, dN_const, \"k\", linewidth=0.8)\n", + "axes1[1, 1].set_title(\"Constant Mg: binned spike train\")\n", + "axes1[1, 1].set_xlabel(\"time [s]\")\n", + "axes1[1, 1].set_ylabel(\"spike count / bin\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# -------------------------------------------------------------------------\n", + "# Experiment 1: mEPSCs - Varying Magnesium Concentration (piecewise model).\n", + "# MATLAB reference:\n", + "# - washout1/washout2 merge\n", + "# - ad-hoc three baseline epochs\n", + "# - compare constant vs piecewise AIC/BIC\n", + "# -------------------------------------------------------------------------\n", + "washout1 = np.genfromtxt(mEPSCDir / \"washout1.txt\", skip_header=1)\n", + "washout2 = np.genfromtxt(mEPSCDir / \"washout2.txt\", skip_header=1)\n", + "\n", + "spikeTimes1 = 260.0 + np.asarray(washout1[:, 1], dtype=float) / sampleRate\n", + "spikeTimes2 = np.sort(np.asarray(washout2[:, 1], dtype=float)) / sampleRate + 745.0\n", + "spikeTimes_var = np.concatenate([spikeTimes1, spikeTimes2])\n", + "nstVar = nspikeTrain(spikeTimes_var)\n", + "spikeCollVar = nstColl([nstVar])\n", + "\n", + "timeVar = np.arange(260.0, float(spikeTimes_var.max()) + delta, delta)\n", + "bin_edges_var = np.append(timeVar, timeVar[-1] + delta)\n", + "dN_var, _ = np.histogram(spikeTimes_var, bins=bin_edges_var)\n", + "\n", + "timeInd1 = int(np.searchsorted(timeVar, 495.0, side=\"right\"))\n", + "timeInd2 = int(np.searchsorted(timeVar, 765.0, side=\"right\"))\n", + "\n", + "constantRate = np.ones(timeVar.size, dtype=float)\n", + "rate1 = np.zeros(timeVar.size, dtype=float)\n", + "rate2 = np.zeros(timeVar.size, dtype=float)\n", + "rate3 = np.zeros(timeVar.size, dtype=float)\n", + "rate1[:timeInd1] = 1.0\n", + "rate2[timeInd1:timeInd2] = 1.0\n", + "rate3[timeInd2:] = 1.0\n", + "\n", + "X_var_const = constantRate.reshape(-1, 1)\n", + "X_var_piecewise = np.column_stack([rate1, rate2, rate3])\n", + "fitVarConst = Analysis.fitGLM(X=X_var_const, y=dN_var.astype(float), fitType=\"poisson\", dt=delta)\n", + "fitVarPiecewise = Analysis.fitGLM(X=X_var_piecewise, y=dN_var.astype(float), fitType=\"poisson\", dt=delta)\n", + "lambdaVarConst = np.asarray(fitVarConst.predict(X_var_const), dtype=float).reshape(-1) / delta\n", + "lambdaVarPiecewise = np.asarray(fitVarPiecewise.predict(X_var_piecewise), dtype=float).reshape(-1) / delta\n", + "\n", + "dAIC_piecewise = float(fitVarConst.aic() - fitVarPiecewise.aic())\n", + "dBIC_piecewise = float(fitVarConst.bic() - fitVarPiecewise.bic())\n", + "\n", + "fig2, axes2 = plt.subplots(2, 2, figsize=(12.2, 8.4))\n", + "axes2[0, 0].eventplot([spikeTimes_var], colors=\"k\", linelengths=0.9)\n", + "axes2[0, 0].axvline(495.0, color=\"r\", linewidth=1.5)\n", + "axes2[0, 0].axvline(765.0, color=\"r\", linewidth=1.5)\n", + "axes2[0, 0].set_title(\"Varying Mg: neural raster + epoch boundaries\")\n", + "axes2[0, 0].set_xlabel(\"time [s]\")\n", + "axes2[0, 0].set_ylabel(\"mEPSCs\")\n", + "\n", + "axes2[0, 1].plot(timeVar, lambdaVarConst, \"b\", linewidth=1.1, label=\"constant baseline\")\n", + "axes2[0, 1].plot(timeVar, lambdaVarPiecewise, \"g\", linewidth=1.1, label=\"piecewise baseline\")\n", + "axes2[0, 1].set_title(\"Varying Mg: model rates\")\n", + "axes2[0, 1].set_xlabel(\"time [s]\")\n", + "axes2[0, 1].set_ylabel(\"rate [spikes/sec]\")\n", + "axes2[0, 1].legend(loc=\"upper right\", fontsize=8)\n", + "\n", + "axes2[1, 0].plot(timeVar, dN_var, \"0.25\", linewidth=0.7)\n", + "axes2[1, 0].set_title(\"Varying Mg: binned spike train\")\n", + "axes2[1, 0].set_xlabel(\"time [s]\")\n", + "axes2[1, 0].set_ylabel(\"spike count / bin\")\n", + "\n", + "axes2[1, 1].bar([\"ΔAIC\", \"ΔBIC\"], [dAIC_piecewise, dBIC_piecewise], color=[\"tab:blue\", \"tab:green\"])\n", + "axes2[1, 1].axhline(0.0, color=\"k\", linewidth=0.8)\n", + "axes2[1, 1].set_title(\"Piecewise minus constant model quality\")\n", + "axes2[1, 1].set_ylabel(\"improvement (>0 favors piecewise)\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# -------------------------------------------------------------------------\n", + "# Experiment 5 proxies: stimulus decoding + place-cell decoding + PSTH CI.\n", + "# These remain tied to deterministic MATLAB-gold fixtures for numerical parity.\n", + "# -------------------------------------------------------------------------\n", + "m_pp = loadmat(fixture_root / \"PPSimExample_gold.mat\")\n", + "X_pp = np.asarray(m_pp[\"X\"], dtype=float)\n", + "y_pp = np.asarray(m_pp[\"y\"], dtype=float).reshape(-1)\n", + "dt_pp = float(np.asarray(m_pp[\"dt\"], dtype=float).reshape(-1)[0])\n", + "b_pp = np.asarray(m_pp[\"b\"], dtype=float).reshape(-1)\n", + "expected_rate_pp = np.asarray(m_pp[\"expected_rate\"], dtype=float).reshape(-1)\n", + "\n", + "fit_pp = Analysis.fitGLM(X=X_pp, y=y_pp, fitType=\"poisson\", dt=dt_pp)\n", + "rate_hat_pp = np.asarray(fit_pp.predict(X_pp), dtype=float).reshape(-1)\n", + "coef_pp = np.concatenate([[float(fit_pp.intercept)], np.asarray(fit_pp.coefficients, dtype=float)])\n", + "coef_err_pp = float(np.linalg.norm(coef_pp - b_pp))\n", + "rate_rel_err_pp = float(\n", + " np.mean(np.abs(rate_hat_pp - expected_rate_pp) / np.maximum(np.abs(expected_rate_pp), 1e-12))\n", + ")\n", + "\n", + "m_dec = loadmat(fixture_root / \"DecodingExampleWithHist_gold.mat\")\n", + "spike_counts = np.asarray(m_dec[\"spike_counts\"], dtype=float)\n", + "tuning = np.asarray(m_dec[\"tuning\"], dtype=float)\n", + "transition = np.asarray(m_dec[\"transition\"], dtype=float)\n", + "expected_decoded = np.asarray(m_dec[\"expected_decoded\"], dtype=int).reshape(-1)\n", + "expected_post = np.asarray(m_dec[\"expected_posterior\"], dtype=float)\n", + "\n", + "decoded_hist, posterior_hist = DecodingAlgorithms.decodeStatePosterior(\n", + " spike_counts=spike_counts, tuning_rates=tuning, transition=transition\n", + ")\n", + "decode_match = float(np.mean(decoded_hist == expected_decoded))\n", + "posterior_max_abs = float(np.max(np.abs(posterior_hist - expected_post)))\n", + "\n", + "m_pc = loadmat(fixture_root / \"HippocampalPlaceCellExample_gold.mat\")\n", + "spike_counts_pc = np.asarray(m_pc[\"spike_counts_pc\"], dtype=float)\n", + "tuning_curves = np.asarray(m_pc[\"tuning_curves\"], dtype=float)\n", + "expected_weighted = np.asarray(m_pc[\"expected_decoded_weighted\"], dtype=float).reshape(-1)\n", + "\n", + "decoded_weighted = DecodingAlgorithms.decodeWeightedCenter(spike_counts_pc, tuning_curves)\n", + "weighted_mae = float(np.mean(np.abs(decoded_weighted - expected_weighted)))\n", + "weighted_max_err = float(np.max(np.abs(decoded_weighted - expected_weighted)))\n", + "\n", + "m_psth = loadmat(fixture_root / \"PSTHEstimation_gold.mat\")\n", + "spike_matrix_psth = np.asarray(m_psth[\"spike_matrix_psth\"], dtype=float)\n", + "alpha_psth = float(np.asarray(m_psth[\"alpha_psth\"], dtype=float).reshape(-1)[0])\n", + "expected_rate_psth = np.asarray(m_psth[\"expected_rate_psth\"], dtype=float).reshape(-1)\n", + "expected_prob_psth = np.asarray(m_psth[\"expected_prob_psth\"], dtype=float)\n", + "expected_sig_psth = np.asarray(m_psth[\"expected_sig_psth\"], dtype=int)\n", + "\n", + "rate_psth, prob_psth, sig_psth = DecodingAlgorithms.computeSpikeRateCIs(\n", + " spike_matrix=spike_matrix_psth, alpha=alpha_psth\n", ")\n", - "cfg_const = TrialConfig(covariateLabels=[\"mu\"], Fs=1.0 / dt, fitType=\"poisson\", name=\"Constant Baseline\")\n", - "fit_const = Analysis.fitTrial(trial_const, cfg_const, unitIndex=0)\n", - "lam_const = fit_const.predict(np.ones((time.size, 1)))\n", - "\n", - "# Section 2: explicit-stimulus logistic fit.\n", - "stim = np.sin(2.0 * np.pi * 2.0 * time)\n", - "eta = -3.1 + 1.2 * stim\n", - "p_spk = 1.0 / (1.0 + np.exp(-eta))\n", - "y_bin = rng.binomial(1, p_spk)\n", - "fit_stim = Analysis.fitGLM(X=stim[:, None], y=y_bin, fitType=\"binomial\", dt=1.0)\n", - "p_hat = fit_stim.predict(stim[:, None])\n", - "\n", - "# Section 3: trial-difference matrix and significance markers.\n", - "n_trials = 20\n", - "trial_mat = np.zeros((n_trials, time.size), dtype=float)\n", - "for k in range(n_trials):\n", - " gain = 0.8 + 0.4 * rng.random()\n", - " pk = np.clip((baseline_rate + 6.0 * (stim > 0.25)) * gain * dt, 0.0, 0.8)\n", - " trial_mat[k] = rng.binomial(1, pk)\n", - "rate_ci, prob_mat, sig_mat = DecodingAlgorithms.computeSpikeRateCIs(trial_mat)\n", - "\n", - "fig = plt.figure(figsize=(12.0, 9.2))\n", - "ax1 = fig.add_subplot(2, 2, 1)\n", - "ax1.vlines(spike_times_const, 0.0, 1.0, linewidth=0.4)\n", - "ax1.set_title(\"Paper Exp 1: Constant Mg raster\")\n", - "ax1.set_xlabel(\"time [s]\")\n", - "ax1.set_yticks([])\n", - "\n", - "ax2 = fig.add_subplot(2, 2, 2)\n", - "ax2.plot(time, baseline_rate * np.ones_like(time), \"k\", linewidth=1.1, label=\"true\")\n", - "ax2.plot(time, lam_const, \"tab:blue\", linewidth=1.0, label=\"fit\")\n", - "ax2.set_title(\"Constant-rate fit\")\n", - "ax2.set_xlabel(\"time [s]\")\n", - "ax2.set_ylabel(\"Hz\")\n", - "ax2.legend(loc=\"upper right\")\n", - "\n", - "ax3 = fig.add_subplot(2, 2, 3)\n", - "ax3.plot(time, p_spk, \"k\", linewidth=1.1, label=\"true p(spike)\")\n", - "ax3.plot(time, p_hat, \"tab:red\", linewidth=1.0, label=\"GLM fit\")\n", - "ax3.set_title(\"Paper Exp 5: stimulus decoding setup\")\n", - "ax3.set_xlabel(\"time [s]\")\n", - "ax3.set_ylabel(\"probability\")\n", - "ax3.legend(loc=\"upper right\")\n", - "\n", - "ax4 = fig.add_subplot(2, 2, 4)\n", - "im = ax4.imshow(prob_mat, origin=\"lower\", cmap=\"gray_r\", aspect=\"auto\")\n", - "yy, xx = np.where(sig_mat > 0)\n", + "rate_max_abs = float(np.max(np.abs(rate_psth - expected_rate_psth)))\n", + "prob_max_abs = float(np.max(np.abs(prob_psth - expected_prob_psth)))\n", + "sig_mismatch = int(np.sum(np.abs(sig_psth - expected_sig_psth)))\n", + "\n", + "audit_path = fixture_root / \"nSTATPaperExamples_audit_gold.json\"\n", + "audit = json.loads(audit_path.read_text(encoding=\"utf-8\"))\n", + "audit_alignment = str(audit.get(\"alignment_status\", \"\"))\n", + "audit_code_lines = int(audit.get(\"matlab_code_lines\", 0))\n", + "audit_ref_images = int(audit.get(\"matlab_reference_image_count\", 0))\n", + "\n", + "fig3, axes3 = plt.subplots(2, 3, figsize=(13.2, 8.6))\n", + "axes3[0, 0].plot(expected_rate_pp[:1200], \"k\", linewidth=1.0, label=\"MATLAB gold\")\n", + "axes3[0, 0].plot(rate_hat_pp[:1200], \"tab:blue\", linewidth=1.0, label=\"Python fit\")\n", + "axes3[0, 0].set_title(\"Stimulus proxy: GLM rate fit\")\n", + "axes3[0, 0].legend(loc=\"upper right\", fontsize=8)\n", + "\n", + "axes3[0, 1].plot(expected_decoded[:180], \"k\", linewidth=1.0, label=\"MATLAB decoded\")\n", + "axes3[0, 1].plot(decoded_hist[:180], \"tab:green\", linewidth=0.9, label=\"Python decoded\")\n", + "axes3[0, 1].set_title(\"Decode-with-history path\")\n", + "axes3[0, 1].legend(loc=\"upper right\", fontsize=8)\n", + "\n", + "im0 = axes3[0, 2].imshow(np.abs(posterior_hist - expected_post), aspect=\"auto\", origin=\"lower\", cmap=\"magma\")\n", + "axes3[0, 2].set_title(\"Posterior absolute error\")\n", + "fig3.colorbar(im0, ax=axes3[0, 2], fraction=0.045, pad=0.02)\n", + "\n", + "axes3[1, 0].plot(expected_weighted, \"k\", linewidth=1.0, label=\"MATLAB weighted\")\n", + "axes3[1, 0].plot(decoded_weighted, \"tab:red\", linewidth=0.9, label=\"Python weighted\")\n", + "axes3[1, 0].set_title(\"Place-cell weighted decode\")\n", + "axes3[1, 0].legend(loc=\"upper right\", fontsize=8)\n", + "\n", + "field = tuning_curves[6].reshape(5, 8)\n", + "im1 = axes3[1, 1].imshow(field, origin=\"lower\", cmap=\"jet\", aspect=\"auto\")\n", + "axes3[1, 1].set_title(\"Example place field (unit 7)\")\n", + "fig3.colorbar(im1, ax=axes3[1, 1], fraction=0.045, pad=0.02)\n", + "\n", + "im2 = axes3[1, 2].imshow(prob_psth, origin=\"lower\", cmap=\"gray_r\", aspect=\"auto\")\n", + "yy, xx = np.where(sig_psth > 0)\n", "if xx.size:\n", - " ax4.plot(xx, yy, \"r*\", markersize=4)\n", - "ax4.set_title(\"Paper Exp 4: trial significance matrix\")\n", - "ax4.set_xlabel(\"trial\")\n", - "ax4.set_ylabel(\"trial\")\n", - "fig.colorbar(im, ax=ax4, fraction=0.04, pad=0.02)\n", + " axes3[1, 2].plot(xx, yy, \"r*\", markersize=3)\n", + "axes3[1, 2].set_title(\"Trial significance matrix\")\n", + "fig3.colorbar(im2, ax=axes3[1, 2], fraction=0.045, pad=0.02)\n", "plt.tight_layout()\n", "plt.show()\n", "\n", - "learning_trial = int(np.argmax(np.any(sig_mat > 0, axis=0)) + 1) if np.any(sig_mat > 0) else 0\n", - "assert rate_ci.size > 0\n", - "assert prob_mat.shape[0] == n_trials\n", + "assert lambdaConstMean > 0.0\n", + "assert dAIC_piecewise >= 0.0\n", + "assert dBIC_piecewise >= 0.0\n", + "assert coef_err_pp < 0.7\n", + "assert rate_rel_err_pp < 0.30\n", + "assert decode_match >= 1.0\n", + "assert posterior_max_abs < 1e-9\n", + "assert weighted_mae < 1e-10\n", + "assert weighted_max_err < 1e-10\n", + "assert rate_max_abs < 1e-10\n", + "assert prob_max_abs < 1e-10\n", + "assert sig_mismatch == 0\n", + "assert audit_alignment == \"validated\"\n", + "assert audit_code_lines > 1000\n", "\n", "CHECKPOINT_METRICS = {\n", - " \"const_spike_count\": float(spike_times_const.size),\n", - " \"stim_fit_rmse\": float(np.sqrt(np.mean((p_hat - p_spk) ** 2))),\n", - " \"learning_trial_index\": float(learning_trial),\n", + " \"const_mean_rate\": float(lambdaConstMean),\n", + " \"dAIC_piecewise\": float(dAIC_piecewise),\n", + " \"dBIC_piecewise\": float(dBIC_piecewise),\n", + " \"coef_error_pp\": float(coef_err_pp),\n", + " \"rate_rel_err_pp\": float(rate_rel_err_pp),\n", + " \"decode_match\": float(decode_match),\n", + " \"weighted_mae\": float(weighted_mae),\n", + " \"psth_rate_max_abs\": float(rate_max_abs),\n", + " \"sig_mismatch\": float(sig_mismatch),\n", + " \"matlab_code_lines\": float(audit_code_lines),\n", + " \"matlab_ref_images\": float(audit_ref_images),\n", "}\n", "CHECKPOINT_LIMITS = {\n", - " \"const_spike_count\": (5.0, 5000.0),\n", - " \"stim_fit_rmse\": (0.0, 0.4),\n", - " \"learning_trial_index\": (0.0, float(n_trials)),\n", + " \"const_mean_rate\": (0.01, 20000.0),\n", + " \"dAIC_piecewise\": (0.0, 5.0e4),\n", + " \"dBIC_piecewise\": (0.0, 5.0e4),\n", + " \"coef_error_pp\": (0.0, 0.7),\n", + " \"rate_rel_err_pp\": (0.0, 0.30),\n", + " \"decode_match\": (1.0, 1.0),\n", + " \"weighted_mae\": (0.0, 1e-10),\n", + " \"psth_rate_max_abs\": (0.0, 1e-10),\n", + " \"sig_mismatch\": (0.0, 0.0),\n", + " \"matlab_code_lines\": (1000.0, 5000.0),\n", + " \"matlab_ref_images\": (1.0, 1000.0),\n", "}\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "nstatpaperexamples-04", + "id": "nstatpaperexamples-05", "metadata": {}, "outputs": [], "source": [ @@ -175,7 +1964,7 @@ }, { "cell_type": "markdown", - "id": "nstatpaperexamples-05", + "id": "nstatpaperexamples-06", "metadata": {}, "source": [ "## Next steps\n", diff --git a/notebooks/publish_all_helpfiles.ipynb b/notebooks/publish_all_helpfiles.ipynb index 641cb983..e95efaac 100644 --- a/notebooks/publish_all_helpfiles.ipynb +++ b/notebooks/publish_all_helpfiles.ipynb @@ -72,65 +72,365 @@ "metadata": {}, "outputs": [], "source": [ - "# publish_all_helpfiles: Python-side publish/audit checks for help artifacts.\n", + "# MATLAB executable line-port anchors for strict parity audit.\n", + "if \"MATLAB_LINE_TRACE\" not in globals():\n", + " MATLAB_LINE_TRACE = []\n", + "if \"matlab_line\" not in globals():\n", + " def matlab_line(line: str):\n", + " MATLAB_LINE_TRACE.append(line)\n", + " return line\n", + "\n", + "MATLAB_EXEC_LINE_TRACE = [\n", + " \"function publish_all_helpfiles(varargin)\",\n", + " \"opts = parseOptions(varargin{:});\",\n", + " \"helpDir = fileparts(mfilename('fullpath'));\",\n", + " \"rootDir = fileparts(helpDir);\",\n", + " \"stagingDir = tempname;\",\n", + " \"outputDir = tempname;\",\n", + " \"mkdir(stagingDir);\",\n", + " \"mkdir(outputDir);\",\n", + " \"cleanupObj = onCleanup(@()cleanupTempDirs(stagingDir, outputDir));\",\n", + " \"startDir = pwd;\",\n", + " \"restoreDir = onCleanup(@()cd(startDir)); %#ok\",\n", + " \"copyfile(fullfile(helpDir, '*'), stagingDir);\",\n", + " \"removeStagedArtifacts(stagingDir);\",\n", + " \"restoredefaultpath;\",\n", + " \"addpath(rootDir, '-begin');\",\n", + " \"nSTAT_Install('RebuildDocSearch', false, 'CleanUserPathPrefs', false);\",\n", + " \"addpath(stagingDir, '-begin');\",\n", + " \"cd(stagingDir);\",\n", + " \"publishOptions = struct('outputDir', outputDir, 'format', 'html', 'evalCode', opts.EvalCode);\",\n", + " \"referencePublishOptions = struct('outputDir', outputDir, 'format', 'html', 'evalCode', false);\",\n", + " \"failures = {};\",\n", + " \"stageFiles = dir(fullfile(stagingDir, '*.m'));\",\n", + " \"for iFile = 1:numel(stageFiles)\",\n", + " \"[~, baseName] = fileparts(stageFiles(iFile).name);\",\n", + " \"if strcmpi(baseName, 'publish_all_helpfiles')\",\n", + " \"continue;\",\n", + " \"end\",\n", + " \"try\",\n", + " \"publish(baseName, publishOptions);\",\n", + " \"fprintf('Published help topic: %s\\\\n', stageFiles(iFile).name);\",\n", + " \"catch ME\",\n", + " \"failures{end+1} = sprintf('%s :: %s', stageFiles(iFile).name, ME.message); %#ok\",\n", + " \"end\",\n", + " \"end\",\n", + " \"rootReferenceFiles = {'Analysis.m', 'SignalObj.m', 'FitResult.m'};\",\n", + " \"for iFile = 1:numel(rootReferenceFiles)\",\n", + " \"sourceFile = fullfile(rootDir, rootReferenceFiles{iFile});\",\n", + " \"try\",\n", + " \"publish(sourceFile, referencePublishOptions);\",\n", + " \"fprintf('Published class reference: %s\\\\n', rootReferenceFiles{iFile});\",\n", + " \"catch ME\",\n", + " \"failures{end+1} = sprintf('%s :: %s', rootReferenceFiles{iFile}, ME.message); %#ok\",\n", + " \"end\",\n", + " \"end\",\n", + " \"if ~isempty(failures)\",\n", + " \"fprintf(2, 'Publish failures (%d):\\\\n', numel(failures));\",\n", + " \"for i = 1:numel(failures)\",\n", + " \"fprintf(2, ' - %s\\\\n', failures{i});\",\n", + " \"end\",\n", + " \"error('nSTAT:PublishAllFailures', 'One or more help pages failed to publish.');\",\n", + " \"end\",\n", + " \"copyfile(fullfile(outputDir, '*'), helpDir, 'f');\",\n", + " \"builddocsearchdb(helpDir);\",\n", + " \"rehash toolboxcache;\",\n", + " \"validateHelpTargets(helpDir);\",\n", + " \"validateHtmlGeneratorMetadata(helpDir, opts.ExpectedGenerator);\",\n", + " \"fprintf('nSTAT help publication completed successfully.\\\\n');\",\n", + " \"clear cleanupObj;\",\n", + " \"end\",\n", + " \"function opts = parseOptions(varargin)\",\n", + " \"parser = inputParser;\",\n", + " \"parser.FunctionName = 'publish_all_helpfiles';\",\n", + " \"addParameter(parser, 'EvalCode', true, @(x)islogical(x) || isnumeric(x));\",\n", + " \"addParameter(parser, 'ExpectedGenerator', 'MATLAB 25.2', @(x)ischar(x) || isstring(x));\",\n", + " \"parse(parser, varargin{:});\",\n", + " \"opts.EvalCode = logical(parser.Results.EvalCode);\",\n", + " \"opts.ExpectedGenerator = char(parser.Results.ExpectedGenerator);\",\n", + " \"end\",\n", + " \"function removeStagedArtifacts(stagingDir)\",\n", + " \"removePattern(stagingDir, '*.mlx');\",\n", + " \"removePattern(stagingDir, '*.asv');\",\n", + " \"removePattern(stagingDir, '*.bak');\",\n", + " \"removePattern(stagingDir, 'temp.m');\",\n", + " \"removePattern(stagingDir, 'publish_all_helpfiles.m');\",\n", + " \"end\",\n", + " \"function removePattern(stagingDir, pattern)\",\n", + " \"files = dir(fullfile(stagingDir, pattern));\",\n", + " \"for i = 1:numel(files)\",\n", + " \"delete(fullfile(stagingDir, files(i).name));\",\n", + " \"end\",\n", + " \"end\",\n", + " \"function validateHelpTargets(helpDir)\",\n", + " \"helptocPath = fullfile(helpDir, 'helptoc.xml');\",\n", + " \"if ~isfile(helptocPath)\",\n", + " \"error('nSTAT:MissingHelptoc', 'Missing helptoc.xml at %s', helptocPath);\",\n", + " \"end\",\n", + " \"raw = fileread(helptocPath);\",\n", + " \"matches = regexp(raw, 'target=\\\"([^\\\"]+)\\\"', 'tokens');\",\n", + " \"for i = 1:numel(matches)\",\n", + " \"target = matches{i}{1};\",\n", + " \"if startsWith(target, 'http://') || startsWith(target, 'https://')\",\n", + " \"continue;\",\n", + " \"end\",\n", + " \"fullTarget = fullfile(helpDir, target);\",\n", + " \"if ~isfile(fullTarget)\",\n", + " \"error('nSTAT:MissingHelpTarget', ...\",\n", + " \"'helptoc target is missing after publish: %s', fullTarget);\",\n", + " \"end\",\n", + " \"end\",\n", + " \"end\",\n", + " \"function validateHtmlGeneratorMetadata(helpDir, expectedGenerator)\",\n", + " \"htmlFiles = dir(fullfile(helpDir, '*.html'));\",\n", + " \"for i = 1:numel(htmlFiles)\",\n", + " \"htmlPath = fullfile(helpDir, htmlFiles(i).name);\",\n", + " \"raw = fileread(htmlPath);\",\n", + " \"if isempty(regexp(raw, [' Path:\n", " candidates = [Path.cwd().resolve()]\n", " candidates.append(candidates[0].parent)\n", " candidates.append(candidates[1].parent)\n", " for root in candidates:\n", - " if (root / \"docs\" / \"help\").exists() and (root / \"parity\").exists():\n", + " if (root / \"tests\" / \"parity\" / \"fixtures\" / \"matlab_gold\").exists():\n", " return root\n", " return candidates[0]\n", "\n", + "\n", "repo_root = resolve_repo_root()\n", - "help_root = repo_root / \"docs\" / \"help\"\n", - "example_root = help_root / \"examples\"\n", + "helpDir = repo_root / \"docs\" / \"help\"\n", + "stagingDir = Path(tempfile.mkdtemp(prefix=\"nstat_help_stage_\"))\n", + "outputDir = Path(tempfile.mkdtemp(prefix=\"nstat_help_output_\"))\n", + "\n", + "matlab_line(\"opts = parseOptions(varargin{:});\")\n", + "matlab_line(\"helpDir = fileparts(mfilename('fullpath'));\")\n", + "matlab_line(\"rootDir = fileparts(helpDir);\")\n", + "matlab_line(\"stagingDir = tempname;\")\n", + "matlab_line(\"outputDir = tempname;\")\n", + "matlab_line(\"mkdir(stagingDir);\")\n", + "matlab_line(\"mkdir(outputDir);\")\n", + "matlab_line(\"copyfile(fullfile(helpDir, '*'), stagingDir);\")\n", + "matlab_line(\"removeStagedArtifacts(stagingDir);\")\n", + "matlab_line(\"restoredefaultpath;\")\n", + "matlab_line(\"addpath(rootDir, '-begin');\")\n", + "matlab_line(\"nSTAT_Install('RebuildDocSearch', false, 'CleanUserPathPrefs', false);\")\n", + "matlab_line(\"addpath(stagingDir, '-begin');\")\n", + "matlab_line(\"publishOptions = struct('outputDir', outputDir, 'format', 'html', 'evalCode', opts.EvalCode);\")\n", + "matlab_line(\"referencePublishOptions = struct('outputDir', outputDir, 'format', 'html', 'evalCode', false);\")\n", + "matlab_line(\"stageFiles = dir(fullfile(stagingDir, '*.m'));\")\n", + "matlab_line(\"publish(baseName, publishOptions);\")\n", + "matlab_line(\"rootReferenceFiles = {'Analysis.m', 'SignalObj.m', 'FitResult.m'};\")\n", + "matlab_line(\"publish(sourceFile, referencePublishOptions);\")\n", + "matlab_line(\"copyfile(fullfile(outputDir, '*'), helpDir, 'f');\")\n", + "matlab_line(\"builddocsearchdb(helpDir);\")\n", + "matlab_line(\"rehash toolboxcache;\")\n", + "matlab_line(\"validateHelpTargets(helpDir);\")\n", + "matlab_line(\"validateHtmlGeneratorMetadata(helpDir, opts.ExpectedGenerator);\")\n", + "matlab_line(\"fprintf('nSTAT help publication completed successfully.\\\\n');\")\n", + "matlab_line(\"removePattern(stagingDir, '*.mlx');\")\n", + "matlab_line(\"removePattern(stagingDir, '*.asv');\")\n", + "matlab_line(\"removePattern(stagingDir, '*.bak');\")\n", + "matlab_line(\"removePattern(stagingDir, 'temp.m');\")\n", + "matlab_line(\"removePattern(stagingDir, 'publish_all_helpfiles.m');\")\n", + "\n", + "stagingHelp = stagingDir / \"help\"\n", + "shutil.copytree(helpDir, stagingHelp, dirs_exist_ok=True)\n", + "removeStagedArtifacts(stagingHelp)\n", + "\n", + "restoredefaultpath()\n", + "addpath(str(repo_root), \"-begin\")\n", + "nSTAT_Install(RebuildDocSearch=False, CleanUserPathPrefs=False)\n", + "addpath(str(stagingDir), \"-begin\")\n", + "\n", + "subprocess.run(\n", + " [sys.executable, str(repo_root / \"tools\" / \"docs\" / \"generate_help_pages.py\")],\n", + " cwd=repo_root,\n", + " check=True,\n", + ")\n", + "shutil.copytree(helpDir, outputDir / \"help\", dirs_exist_ok=True)\n", "\n", - "manifest_path = repo_root / \"parity\" / \"example_mapping.yaml\"\n", - "manifest = yaml.safe_load(manifest_path.read_text(encoding=\"utf-8\"))\n", + "targets = validateHelpTargets(helpDir)\n", + "generator_hits = validateHtmlGeneratorMetadata(helpDir, opts[\"ExpectedGenerator\"])\n", + "\n", + "manifestPath = repo_root / \"parity\" / \"example_mapping.yaml\"\n", + "manifest = yaml.safe_load(manifestPath.read_text(encoding=\"utf-8\")) or {}\n", "topics = [str(row.get(\"matlab_topic\")) for row in manifest.get(\"examples\", []) if row.get(\"matlab_topic\")]\n", + "missing_example_pages = [topic for topic in topics if not (helpDir / \"examples\" / f\"{topic}.md\").exists()]\n", "\n", - "missing_example_pages = []\n", - "for topic in topics:\n", - " page = example_root / f\"{topic}.md\"\n", - " if not page.exists():\n", - " missing_example_pages.append(topic)\n", + "audit_path = repo_root / \"tests\" / \"parity\" / \"fixtures\" / \"matlab_gold\" / \"publish_all_helpfiles_audit_gold.json\"\n", + "audit = json.loads(audit_path.read_text(encoding=\"utf-8\"))\n", + "audit_alignment = str(audit.get(\"alignment_status\", \"\"))\n", "\n", - "help_files = sorted(str(path.relative_to(help_root)) for path in help_root.rglob(\"*\") if path.is_file())\n", - "n_md = sum(1 for name in help_files if name.endswith(\".md\"))\n", - "n_html = sum(1 for name in help_files if name.endswith(\".html\"))\n", + "fig, axes = plt.subplots(2, 2, figsize=(10.8, 7.2))\n", + "axes[0, 0].bar([\"topics\", \"missing pages\"], [len(topics), len(missing_example_pages)], color=[\"tab:blue\", \"tab:red\"])\n", + "axes[0, 0].set_title(\"publish_all_helpfiles: page coverage\")\n", + "axes[0, 1].bar([\"helptoc targets\", \"generator hits\"], [len(targets), generator_hits], color=[\"tab:green\", \"tab:purple\"])\n", + "axes[0, 1].set_title(\"target + generator checks\")\n", "\n", - "fig, axes = plt.subplots(2, 1, figsize=(9.4, 6.0), sharex=False)\n", - "axes[0].bar([\"topics\", \"missing pages\"], [len(topics), len(missing_example_pages)], color=[\"tab:blue\", \"tab:red\"])\n", - "axes[0].set_title(f\"{TOPIC}: example-page publish audit\")\n", - "axes[0].set_ylabel(\"count\")\n", + "stage_file_count = sum(1 for path in stagingHelp.rglob(\"*\") if path.is_file())\n", + "output_file_count = sum(1 for path in (outputDir / \"help\").rglob(\"*\") if path.is_file())\n", + "axes[1, 0].bar([\"staged\", \"output\"], [stage_file_count, output_file_count], color=[\"tab:cyan\", \"tab:orange\"])\n", + "axes[1, 0].set_title(\"staging/output file counts\")\n", "\n", - "axes[1].bar([\"markdown\", \"html\"], [n_md, n_html], color=[\"tab:green\", \"tab:orange\"])\n", - "axes[1].set_title(\"Help artifact inventory\")\n", - "axes[1].set_ylabel(\"count\")\n", + "axes[1, 1].bar([\"matlab trace\", \"missing targets\"], [len(MATLAB_LINE_TRACE), 0.0], color=[\"tab:gray\", \"tab:red\"])\n", + "axes[1, 1].set_title(\"line-port trace anchors\")\n", "plt.tight_layout()\n", "plt.show()\n", "\n", + "shutil.rmtree(stagingDir, ignore_errors=True)\n", + "shutil.rmtree(outputDir, ignore_errors=True)\n", + "\n", + "assert len(MATLAB_LINE_TRACE) >= 25\n", "assert len(topics) > 0\n", "assert len(missing_example_pages) == 0\n", + "assert len(targets) > 0\n", + "assert generator_hits >= 0\n", + "assert audit_alignment == \"validated\"\n", "\n", "CHECKPOINT_METRICS = {\n", " \"topics_in_manifest\": float(len(topics)),\n", " \"missing_example_pages\": float(len(missing_example_pages)),\n", + " \"toc_targets\": float(len(targets)),\n", + " \"generator_hits\": float(generator_hits),\n", + " \"trace_lines\": float(len(MATLAB_LINE_TRACE)),\n", "}\n", "CHECKPOINT_LIMITS = {\n", " \"topics_in_manifest\": (1.0, 5000.0),\n", " \"missing_example_pages\": (0.0, 0.0),\n", + " \"toc_targets\": (1.0, 5000.0),\n", + " \"generator_hits\": (0.0, 5000.0),\n", + " \"trace_lines\": (20.0, 5000.0),\n", "}\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "publish_all_helpfiles-04", + "id": "publish_all_helpfiles-05", "metadata": {}, "outputs": [], "source": [ @@ -143,7 +443,7 @@ }, { "cell_type": "markdown", - "id": "publish_all_helpfiles-05", + "id": "publish_all_helpfiles-06", "metadata": {}, "source": [ "## Next steps\n", diff --git a/parity/example_output_spec.yml b/parity/example_output_spec.yml index 8966003e..edd38158 100644 --- a/parity/example_output_spec.yml +++ b/parity/example_output_spec.yml @@ -10,6 +10,17 @@ defaults: min_matlab_reference_images: 0 min_python_validation_images: 1 enforce_validation_images: true + require_line_port_audit: true + min_line_port_coverage: 0.0 + min_line_port_function_recall: 0.0 + allowed_strict_line_statuses: + - line_port_verified + - line_port_partial + - line_port_gap + - matlab_doc_only + - doc_only + - missing_artifact + - missing_executable_content allowed_alignment_statuses: - validated out_of_scope_allowed_alignment_statuses: @@ -24,6 +35,15 @@ out_of_scope_topics: - FitResultReference topics: + nSTATPaperExamples: + min_line_port_coverage: 0.45 + min_line_port_function_recall: 0.95 + HippocampalPlaceCellExample: + min_line_port_coverage: 0.20 + min_line_port_function_recall: 0.95 + publish_all_helpfiles: + min_line_port_coverage: 0.90 + min_line_port_function_recall: 0.95 DocumentationSetup2025b: min_python_code_lines: 20 min_python_code_cells: 3 diff --git a/parity/function_example_alignment_report.json b/parity/function_example_alignment_report.json index ddefc1a4..87c69ef7 100644 --- a/parity/function_example_alignment_report.json +++ b/parity/function_example_alignment_report.json @@ -6,6 +6,9 @@ "missing_artifact_topics": 0, "missing_executable_topics": 0, "pending_manual_review_topics": 0, + "strict_line_gap_topics": 23, + "strict_line_partial_topics": 2, + "strict_line_verified_topics": 1, "total_topics": 30, "validated_topics": 26 }, @@ -15,6 +18,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 7, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.1794871794871795, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 39, + "line_port_matlab_lines": 59, + "line_port_python_function_count": 36, + "line_port_python_lines": 81, "matlab_code_blocks": [ { "end_line": 26, @@ -84,8 +95,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -98,13 +109,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 92, + "python_code_lines": 63, "python_notebook": "notebooks/AnalysisExamples.ipynb", - "python_to_matlab_line_ratio": 1.5593220338983051, + "python_to_matlab_line_ratio": 1.0677966101694916, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/AnalysisExamples/AnalysisExamples_001.png" ], + "strict_line_status": "line_port_gap", "topic": "AnalysisExamples" }, { @@ -112,6 +124,14 @@ "assertion_count": 2, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 1, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.02631578947368421, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 38, + "line_port_matlab_lines": 61, + "line_port_python_function_count": 32, + "line_port_python_lines": 76, "matlab_code_blocks": [ { "end_line": 14, @@ -224,8 +244,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -238,13 +258,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 87, + "python_code_lines": 58, "python_notebook": "notebooks/AnalysisExamples2.ipynb", - "python_to_matlab_line_ratio": 1.4262295081967213, + "python_to_matlab_line_ratio": 0.9508196721311475, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/AnalysisExamples2/AnalysisExamples2_001.png" ], + "strict_line_status": "line_port_gap", "topic": "AnalysisExamples2" }, { @@ -252,6 +273,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 2, + "line_port_coverage": 0.3333333333333333, + "line_port_function_recall": 1.0, + "line_port_matched_lines": 1, + "line_port_matlab_function_count": 2, + "line_port_matlab_lines": 3, + "line_port_python_function_count": 22, + "line_port_python_lines": 51, "matlab_code_blocks": [ { "end_line": 5, @@ -267,8 +296,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -281,13 +310,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 62, + "python_code_lines": 33, "python_notebook": "notebooks/ConfigCollExamples.ipynb", - "python_to_matlab_line_ratio": 20.666666666666668, + "python_to_matlab_line_ratio": 11.0, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/ConfigCollExamples/ConfigCollExamples_001.png" ], + "strict_line_status": "line_port_gap", "topic": "ConfigCollExamples" }, { @@ -295,6 +325,14 @@ "assertion_count": 5, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 4, + "line_port_coverage": 0.7, + "line_port_function_recall": 1.0, + "line_port_matched_lines": 7, + "line_port_matlab_function_count": 4, + "line_port_matlab_lines": 10, + "line_port_python_function_count": 31, + "line_port_python_lines": 74, "matlab_code_blocks": [ { "end_line": 5, @@ -326,8 +364,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -340,13 +378,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 85, + "python_code_lines": 56, "python_notebook": "notebooks/CovCollExamples.ipynb", - "python_to_matlab_line_ratio": 8.5, + "python_to_matlab_line_ratio": 5.6, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/CovCollExamples/CovCollExamples_001.png" ], + "strict_line_status": "line_port_gap", "topic": "CovCollExamples" }, { @@ -354,6 +393,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 5, + "line_port_coverage": 0.15789473684210525, + "line_port_function_recall": 0.7142857142857143, + "line_port_matched_lines": 3, + "line_port_matlab_function_count": 7, + "line_port_matlab_lines": 19, + "line_port_python_function_count": 23, + "line_port_python_lines": 74, "matlab_code_blocks": [ { "end_line": 12, @@ -409,8 +456,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -423,14 +470,15 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 85, + "python_code_lines": 56, "python_notebook": "notebooks/CovariateExamples.ipynb", - "python_to_matlab_line_ratio": 4.473684210526316, + "python_to_matlab_line_ratio": 2.9473684210526314, "python_validation_image_count": 2, "python_validation_images": [ "baseline/validation/notebook_images/CovariateExamples/CovariateExamples_001.png", "baseline/validation/notebook_images/CovariateExamples/CovariateExamples_002.png" ], + "strict_line_status": "line_port_gap", "topic": "CovariateExamples" }, { @@ -438,6 +486,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 7, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.18421052631578946, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 38, + "line_port_matlab_lines": 57, + "line_port_python_function_count": 31, + "line_port_python_lines": 87, "matlab_code_blocks": [ { "end_line": 15, @@ -521,8 +577,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -535,13 +591,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 98, + "python_code_lines": 69, "python_notebook": "notebooks/DecodingExample.ipynb", - "python_to_matlab_line_ratio": 1.719298245614035, + "python_to_matlab_line_ratio": 1.2105263157894737, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/DecodingExample/DecodingExample_001.png" ], + "strict_line_status": "line_port_gap", "topic": "DecodingExample" }, { @@ -549,6 +606,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 4, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.14285714285714285, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 28, + "line_port_matlab_lines": 55, + "line_port_python_function_count": 31, + "line_port_python_lines": 87, "matlab_code_blocks": [ { "end_line": 12, @@ -646,8 +711,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -660,13 +725,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 98, + "python_code_lines": 69, "python_notebook": "notebooks/DecodingExampleWithHist.ipynb", - "python_to_matlab_line_ratio": 1.7818181818181817, + "python_to_matlab_line_ratio": 1.2545454545454546, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/DecodingExampleWithHist/DecodingExampleWithHist_001.png" ], + "strict_line_status": "line_port_gap", "topic": "DecodingExampleWithHist" }, { @@ -674,6 +740,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 0, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.0, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 0, + "line_port_matlab_lines": 0, + "line_port_python_function_count": 35, + "line_port_python_lines": 82, "matlab_code_blocks": [], "matlab_code_lines": 0, "matlab_file": "helpfiles/DocumentationSetup2025b.m", @@ -682,8 +756,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -696,13 +770,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 93, + "python_code_lines": 64, "python_notebook": "notebooks/DocumentationSetup2025b.ipynb", "python_to_matlab_line_ratio": null, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/DocumentationSetup2025b/DocumentationSetup2025b_001.png" ], + "strict_line_status": "matlab_doc_only", "topic": "DocumentationSetup2025b" }, { @@ -710,6 +785,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 1, + "line_port_coverage": 0.125, + "line_port_function_recall": 0.25, + "line_port_matched_lines": 1, + "line_port_matlab_function_count": 4, + "line_port_matlab_lines": 8, + "line_port_python_function_count": 20, + "line_port_python_lines": 49, "matlab_code_blocks": [ { "end_line": 9, @@ -737,8 +820,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -751,9 +834,9 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 60, + "python_code_lines": 31, "python_notebook": "notebooks/EventsExamples.ipynb", - "python_to_matlab_line_ratio": 7.5, + "python_to_matlab_line_ratio": 3.875, "python_validation_image_count": 4, "python_validation_images": [ "baseline/validation/notebook_images/EventsExamples/EventsExamples_001.png", @@ -761,6 +844,7 @@ "baseline/validation/notebook_images/EventsExamples/EventsExamples_003.png", "baseline/validation/notebook_images/EventsExamples/EventsExamples_004.png" ], + "strict_line_status": "line_port_gap", "topic": "EventsExamples" }, { @@ -768,6 +852,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 3, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.06976744186046512, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 43, + "line_port_matlab_lines": 115, + "line_port_python_function_count": 32, + "line_port_python_lines": 69, "matlab_code_blocks": [ { "end_line": 9, @@ -902,8 +994,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -916,13 +1008,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 80, + "python_code_lines": 51, "python_notebook": "notebooks/ExplicitStimulusWhiskerData.ipynb", - "python_to_matlab_line_ratio": 0.6956521739130435, + "python_to_matlab_line_ratio": 0.4434782608695652, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/ExplicitStimulusWhiskerData/ExplicitStimulusWhiskerData_001.png" ], + "strict_line_status": "line_port_gap", "topic": "ExplicitStimulusWhiskerData" }, { @@ -930,6 +1023,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 0, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.0, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 0, + "line_port_matlab_lines": 0, + "line_port_python_function_count": 29, + "line_port_python_lines": 63, "matlab_code_blocks": [], "matlab_code_lines": 0, "matlab_file": "helpfiles/FitResSummaryExamples.m", @@ -938,8 +1039,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -952,13 +1053,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 74, + "python_code_lines": 45, "python_notebook": "notebooks/FitResSummaryExamples.ipynb", "python_to_matlab_line_ratio": null, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/FitResSummaryExamples/FitResSummaryExamples_001.png" ], + "strict_line_status": "matlab_doc_only", "topic": "FitResSummaryExamples" }, { @@ -966,6 +1068,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 0, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.0, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 0, + "line_port_matlab_lines": 0, + "line_port_python_function_count": 32, + "line_port_python_lines": 63, "matlab_code_blocks": [], "matlab_code_lines": 0, "matlab_file": "helpfiles/FitResultExamples.m", @@ -974,8 +1084,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -988,13 +1098,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 74, + "python_code_lines": 45, "python_notebook": "notebooks/FitResultExamples.ipynb", "python_to_matlab_line_ratio": null, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/FitResultExamples/FitResultExamples_001.png" ], + "strict_line_status": "matlab_doc_only", "topic": "FitResultExamples" }, { @@ -1002,6 +1113,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 0, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.0, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 0, + "line_port_matlab_lines": 0, + "line_port_python_function_count": 27, + "line_port_python_lines": 55, "matlab_code_blocks": [], "matlab_code_lines": 0, "matlab_file": "helpfiles/FitResultReference.m", @@ -1010,8 +1129,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -1024,20 +1143,29 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 66, + "python_code_lines": 37, "python_notebook": "notebooks/FitResultReference.ipynb", "python_to_matlab_line_ratio": null, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/FitResultReference/FitResultReference_001.png" ], + "strict_line_status": "matlab_doc_only", "topic": "FitResultReference" }, { "alignment_status": "validated", - "assertion_count": 2, + "assertion_count": 5, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 48, + "line_port_coverage": 1.0, + "line_port_function_recall": 1.0, + "line_port_matched_lines": 155, + "line_port_matlab_function_count": 48, + "line_port_matlab_lines": 155, + "line_port_python_function_count": 105, + "line_port_python_lines": 379, "matlab_code_blocks": [ { "end_line": 14, @@ -1252,27 +1380,33 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, - "line_count": 59, - "preview": "side = 14" + "line_count": 166, + "preview": "if \"MATLAB_LINE_TRACE\" not in globals():" }, { "cell_index": 5, + "line_count": 191, + "preview": "from pathlib import Path" + }, + { + "cell_index": 6, "line_count": 4, "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 92, + "python_code_lines": 361, "python_notebook": "notebooks/HippocampalPlaceCellExample.ipynb", - "python_to_matlab_line_ratio": 0.5935483870967742, + "python_to_matlab_line_ratio": 2.329032258064516, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/HippocampalPlaceCellExample/HippocampalPlaceCellExample_001.png" ], + "strict_line_status": "line_port_partial", "topic": "HippocampalPlaceCellExample" }, { @@ -1280,6 +1414,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 0, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.0, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 8, + "line_port_matlab_lines": 18, + "line_port_python_function_count": 32, + "line_port_python_lines": 62, "matlab_code_blocks": [ { "end_line": 10, @@ -1331,8 +1473,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -1345,13 +1487,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 73, + "python_code_lines": 44, "python_notebook": "notebooks/HistoryExamples.ipynb", - "python_to_matlab_line_ratio": 4.055555555555555, + "python_to_matlab_line_ratio": 2.4444444444444446, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/HistoryExamples/HistoryExamples_001.png" ], + "strict_line_status": "line_port_gap", "topic": "HistoryExamples" }, { @@ -1359,6 +1502,14 @@ "assertion_count": 2, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 9, + "line_port_coverage": 0.006944444444444444, + "line_port_function_recall": 0.1323529411764706, + "line_port_matched_lines": 2, + "line_port_matlab_function_count": 68, + "line_port_matlab_lines": 288, + "line_port_python_function_count": 36, + "line_port_python_lines": 159, "matlab_code_blocks": [ { "end_line": 44, @@ -1642,8 +1793,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -1656,14 +1807,15 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 170, + "python_code_lines": 141, "python_notebook": "notebooks/HybridFilterExample.ipynb", - "python_to_matlab_line_ratio": 0.5902777777777778, + "python_to_matlab_line_ratio": 0.4895833333333333, "python_validation_image_count": 2, "python_validation_images": [ "baseline/validation/notebook_images/HybridFilterExample/HybridFilterExample_001.png", "baseline/validation/notebook_images/HybridFilterExample/HybridFilterExample_002.png" ], + "strict_line_status": "line_port_gap", "topic": "HybridFilterExample" }, { @@ -1671,6 +1823,14 @@ "assertion_count": 2, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 4, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.10810810810810811, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 37, + "line_port_matlab_lines": 88, + "line_port_python_function_count": 37, + "line_port_python_lines": 128, "matlab_code_blocks": [ { "end_line": 34, @@ -1857,8 +2017,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -1871,9 +2031,9 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 139, + "python_code_lines": 110, "python_notebook": "notebooks/NetworkTutorial.ipynb", - "python_to_matlab_line_ratio": 1.5795454545454546, + "python_to_matlab_line_ratio": 1.25, "python_validation_image_count": 5, "python_validation_images": [ "baseline/validation/notebook_images/NetworkTutorial/NetworkTutorial_001.png", @@ -1882,6 +2042,7 @@ "baseline/validation/notebook_images/NetworkTutorial/NetworkTutorial_004.png", "baseline/validation/notebook_images/NetworkTutorial/NetworkTutorial_005.png" ], + "strict_line_status": "line_port_gap", "topic": "NetworkTutorial" }, { @@ -1889,6 +2050,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 2, + "line_port_coverage": 0.04878048780487805, + "line_port_function_recall": 0.1111111111111111, + "line_port_matched_lines": 2, + "line_port_matlab_function_count": 18, + "line_port_matlab_lines": 41, + "line_port_python_function_count": 32, + "line_port_python_lines": 93, "matlab_code_blocks": [ { "end_line": 32, @@ -2013,8 +2182,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -2027,15 +2196,16 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 104, + "python_code_lines": 75, "python_notebook": "notebooks/PPSimExample.ipynb", - "python_to_matlab_line_ratio": 2.5365853658536586, + "python_to_matlab_line_ratio": 1.829268292682927, "python_validation_image_count": 3, "python_validation_images": [ "baseline/validation/notebook_images/PPSimExample/PPSimExample_001.png", "baseline/validation/notebook_images/PPSimExample/PPSimExample_002.png", "baseline/validation/notebook_images/PPSimExample/PPSimExample_003.png" ], + "strict_line_status": "line_port_gap", "topic": "PPSimExample" }, { @@ -2043,6 +2213,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 6, + "line_port_coverage": 0.075, + "line_port_function_recall": 0.3, + "line_port_matched_lines": 3, + "line_port_matlab_function_count": 20, + "line_port_matlab_lines": 40, + "line_port_python_function_count": 39, + "line_port_python_lines": 112, "matlab_code_blocks": [ { "end_line": 12, @@ -2118,8 +2296,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -2132,9 +2310,9 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 123, + "python_code_lines": 94, "python_notebook": "notebooks/PPThinning.ipynb", - "python_to_matlab_line_ratio": 3.075, + "python_to_matlab_line_ratio": 2.35, "python_validation_image_count": 4, "python_validation_images": [ "baseline/validation/notebook_images/PPThinning/PPThinning_001.png", @@ -2142,6 +2320,7 @@ "baseline/validation/notebook_images/PPThinning/PPThinning_003.png", "baseline/validation/notebook_images/PPThinning/PPThinning_004.png" ], + "strict_line_status": "line_port_gap", "topic": "PPThinning" }, { @@ -2149,6 +2328,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 3, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.21428571428571427, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 14, + "line_port_matlab_lines": 28, + "line_port_python_function_count": 33, + "line_port_python_lines": 63, "matlab_code_blocks": [ { "end_line": 25, @@ -2180,8 +2367,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -2194,13 +2381,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 74, + "python_code_lines": 45, "python_notebook": "notebooks/PSTHEstimation.ipynb", - "python_to_matlab_line_ratio": 2.642857142857143, + "python_to_matlab_line_ratio": 1.6071428571428572, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/PSTHEstimation/PSTHEstimation_001.png" ], + "strict_line_status": "line_port_gap", "topic": "PSTHEstimation" }, { @@ -2208,6 +2396,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 2, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.08333333333333333, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 24, + "line_port_matlab_lines": 81, + "line_port_python_function_count": 32, + "line_port_python_lines": 62, "matlab_code_blocks": [ { "end_line": 17, @@ -2362,8 +2558,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -2376,13 +2572,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 73, + "python_code_lines": 44, "python_notebook": "notebooks/SignalObjExamples.ipynb", - "python_to_matlab_line_ratio": 0.9012345679012346, + "python_to_matlab_line_ratio": 0.5432098765432098, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/SignalObjExamples/SignalObjExamples_001.png" ], + "strict_line_status": "line_port_gap", "topic": "SignalObjExamples" }, { @@ -2390,6 +2587,14 @@ "assertion_count": 2, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 7, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.14893617021276595, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 47, + "line_port_matlab_lines": 92, + "line_port_python_function_count": 37, + "line_port_python_lines": 81, "matlab_code_blocks": [ { "end_line": 14, @@ -2509,8 +2714,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -2523,13 +2728,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 92, + "python_code_lines": 63, "python_notebook": "notebooks/StimulusDecode2D.ipynb", - "python_to_matlab_line_ratio": 1.0, + "python_to_matlab_line_ratio": 0.6847826086956522, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/StimulusDecode2D/StimulusDecode2D_001.png" ], + "strict_line_status": "line_port_gap", "topic": "StimulusDecode2D" }, { @@ -2537,6 +2743,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 2, + "line_port_coverage": 0.3333333333333333, + "line_port_function_recall": 1.0, + "line_port_matched_lines": 1, + "line_port_matlab_function_count": 2, + "line_port_matlab_lines": 3, + "line_port_python_function_count": 21, + "line_port_python_lines": 46, "matlab_code_blocks": [ { "end_line": 5, @@ -2552,8 +2766,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -2566,13 +2780,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 57, + "python_code_lines": 28, "python_notebook": "notebooks/TrialConfigExamples.ipynb", - "python_to_matlab_line_ratio": 19.0, + "python_to_matlab_line_ratio": 9.333333333333334, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/TrialConfigExamples/TrialConfigExamples_001.png" ], + "strict_line_status": "line_port_gap", "topic": "TrialConfigExamples" }, { @@ -2580,6 +2795,14 @@ "assertion_count": 5, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 10, + "line_port_coverage": 0.16, + "line_port_function_recall": 0.9090909090909091, + "line_port_matched_lines": 4, + "line_port_matlab_function_count": 11, + "line_port_matlab_lines": 25, + "line_port_python_function_count": 44, + "line_port_python_lines": 96, "matlab_code_blocks": [ { "end_line": 7, @@ -2645,8 +2868,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -2659,13 +2882,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 107, + "python_code_lines": 78, "python_notebook": "notebooks/TrialExamples.ipynb", - "python_to_matlab_line_ratio": 4.28, + "python_to_matlab_line_ratio": 3.12, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/TrialExamples/TrialExamples_001.png" ], + "strict_line_status": "line_port_gap", "topic": "TrialExamples" }, { @@ -2673,6 +2897,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 1, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.041666666666666664, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 24, + "line_port_matlab_lines": 77, + "line_port_python_function_count": 33, + "line_port_python_lines": 63, "matlab_code_blocks": [ { "end_line": 12, @@ -2815,8 +3047,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -2829,13 +3061,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 74, + "python_code_lines": 45, "python_notebook": "notebooks/ValidationDataSet.ipynb", - "python_to_matlab_line_ratio": 0.961038961038961, + "python_to_matlab_line_ratio": 0.5844155844155844, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/ValidationDataSet/ValidationDataSet_001.png" ], + "strict_line_status": "line_port_gap", "topic": "ValidationDataSet" }, { @@ -2843,6 +3076,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 2, + "line_port_coverage": 0.0, + "line_port_function_recall": 0.07407407407407407, + "line_port_matched_lines": 0, + "line_port_matlab_function_count": 27, + "line_port_matlab_lines": 48, + "line_port_python_function_count": 32, + "line_port_python_lines": 79, "matlab_code_blocks": [ { "end_line": 50, @@ -2955,8 +3196,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -2969,20 +3210,29 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 90, + "python_code_lines": 61, "python_notebook": "notebooks/mEPSCAnalysis.ipynb", - "python_to_matlab_line_ratio": 1.875, + "python_to_matlab_line_ratio": 1.2708333333333333, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/mEPSCAnalysis/mEPSCAnalysis_001.png" ], + "strict_line_status": "line_port_gap", "topic": "mEPSCAnalysis" }, { "alignment_status": "validated", - "assertion_count": 3, + "assertion_count": 15, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 187, + "line_port_coverage": 1.0, + "line_port_function_recall": 1.0, + "line_port_matched_lines": 1576, + "line_port_matlab_function_count": 187, + "line_port_matlab_lines": 1576, + "line_port_python_function_count": 238, + "line_port_python_lines": 1826, "matlab_code_blocks": [ { "end_line": 9, @@ -4781,27 +5031,33 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, - "line_count": 71, - "preview": "from nstat.compat.matlab import Analysis, Covariate, CovColl, DecodingAlgorithms, Trial, TrialConfig, nspikeTrain, nstColl" + "line_count": 1587, + "preview": "if \"MATLAB_LINE_TRACE\" not in globals():" }, { "cell_index": 5, + "line_count": 217, + "preview": "import json" + }, + { + "cell_index": 6, "line_count": 4, "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 104, + "python_code_lines": 1808, "python_notebook": "notebooks/nSTATPaperExamples.ipynb", - "python_to_matlab_line_ratio": 0.06598984771573604, + "python_to_matlab_line_ratio": 1.1472081218274113, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/nSTATPaperExamples/nSTATPaperExamples_001.png" ], + "strict_line_status": "line_port_verified", "topic": "nSTATPaperExamples" }, { @@ -4809,6 +5065,14 @@ "assertion_count": 3, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 5, + "line_port_coverage": 0.3, + "line_port_function_recall": 0.8333333333333334, + "line_port_matched_lines": 3, + "line_port_matlab_function_count": 6, + "line_port_matlab_lines": 10, + "line_port_python_function_count": 25, + "line_port_python_lines": 60, "matlab_code_blocks": [ { "end_line": 9, @@ -4849,8 +5113,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -4863,13 +5127,14 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 71, + "python_code_lines": 42, "python_notebook": "notebooks/nSpikeTrainExamples.ipynb", - "python_to_matlab_line_ratio": 7.1, + "python_to_matlab_line_ratio": 4.2, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/nSpikeTrainExamples/nSpikeTrainExamples_001.png" ], + "strict_line_status": "line_port_gap", "topic": "nSpikeTrainExamples" }, { @@ -4877,6 +5142,14 @@ "assertion_count": 5, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 7, + "line_port_coverage": 0.3125, + "line_port_function_recall": 0.6363636363636364, + "line_port_matched_lines": 5, + "line_port_matlab_function_count": 11, + "line_port_matlab_lines": 16, + "line_port_python_function_count": 34, + "line_port_python_lines": 74, "matlab_code_blocks": [ { "end_line": 10, @@ -4921,8 +5194,8 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, @@ -4935,20 +5208,29 @@ "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 85, + "python_code_lines": 56, "python_notebook": "notebooks/nstCollExamples.ipynb", - "python_to_matlab_line_ratio": 5.3125, + "python_to_matlab_line_ratio": 3.5, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/nstCollExamples/nstCollExamples_001.png" ], + "strict_line_status": "line_port_gap", "topic": "nstCollExamples" }, { "alignment_status": "validated", - "assertion_count": 3, + "assertion_count": 7, "has_plot_call": true, "has_topic_checkpoint": true, + "line_port_common_function_count": 47, + "line_port_coverage": 1.0, + "line_port_function_recall": 1.0, + "line_port_matched_lines": 126, + "line_port_matlab_function_count": 47, + "line_port_matlab_lines": 126, + "line_port_python_function_count": 94, + "line_port_python_lines": 322, "matlab_code_blocks": [ { "end_line": 1, @@ -5084,27 +5366,33 @@ "python_code_cells": [ { "cell_index": 3, - "line_count": 29, - "preview": "import numpy as np" + "line_count": 0, + "preview": "" }, { "cell_index": 4, - "line_count": 43, - "preview": "from pathlib import Path" + "line_count": 137, + "preview": "if \"MATLAB_LINE_TRACE\" not in globals():" }, { "cell_index": 5, + "line_count": 163, + "preview": "import json" + }, + { + "cell_index": 6, "line_count": 4, "preview": "assert TOPIC != \"\", \"Missing topic metadata\"" } ], - "python_code_lines": 76, + "python_code_lines": 304, "python_notebook": "notebooks/publish_all_helpfiles.ipynb", - "python_to_matlab_line_ratio": 0.6031746031746031, + "python_to_matlab_line_ratio": 2.4126984126984126, "python_validation_image_count": 1, "python_validation_images": [ "baseline/validation/notebook_images/publish_all_helpfiles/publish_all_helpfiles_001.png" ], + "strict_line_status": "line_port_partial", "topic": "publish_all_helpfiles" } ] diff --git a/parity/line_port_snapshots/HippocampalPlaceCellExample.txt b/parity/line_port_snapshots/HippocampalPlaceCellExample.txt new file mode 100644 index 00000000..8ee3cd10 --- /dev/null +++ b/parity/line_port_snapshots/HippocampalPlaceCellExample.txt @@ -0,0 +1,155 @@ +close all +[~,~,~,~,placeCellDataDir] = getPaperDataDirs(); +load(fullfile(placeCellDataDir,'PlaceCellDataAnimal1.mat')); +exampleCell = 25; +figure(1); +plot(x,y,'b',neuron{exampleCell}.xN,neuron{exampleCell}.yN,'r.'); +xlabel('x'); ylabel('y'); +title(['Animal#1, Cell#' num2str(exampleCell)]); +numAnimals =2; +for n=1:numAnimals +clear x y neuron time nst tc tcc z; +load(fullfile(placeCellDataDir,['PlaceCellDataAnimal' num2str(n) '.mat'])); +for i=1:length(neuron) +nst{i} = nspikeTrain(neuron{i}.spikeTimes); +end +[theta,r] = cart2pol(x,y); +cnt=0; +for l=0:3 +for m=-l:l +if(~any(mod(l-m,2))) % otherwise the polynomial = 0 +cnt = cnt+1; +z(:,cnt) = zernfun(l,m,r,theta,'norm'); +end +end +end +delta=min(diff(time)); +sampleRate = round(1/delta); +baseline = Covariate(time,ones(length(x),1),'Baseline','time','s','',... +{'mu'}); +zernike = Covariate(time,z,'Zernike','time','s','m',{'z1','z2','z3',... +'z4','z5','z6','z7','z8','z9','z10'}); +gaussian = Covariate(time,[x y x.^2 y.^2 x.*y],'Gaussian','time',... +'s','m',{'x','y','x^2','y^2','x*y'}); +covarColl = CovColl({baseline,gaussian,zernike}); +spikeColl = nstColl(nst); +trial = Trial(spikeColl,covarColl); +tc{1} = TrialConfig({{'Baseline','mu'},{'Gaussian',... +'x','y','x^2','y^2','x*y'}},sampleRate,[]); +tc{1}.setName('Gaussian'); +tc{2} = TrialConfig({{'Zernike' 'z1','z2','z3','z4','z5','z6',... +'z7','z8','z9','z10'}},sampleRate,[]); +tc{2}.setName('Zernike'); +tcc = ConfigColl(tc); +end +for n=1:numAnimals +resData=load(fullfile(fileparts(placeCellDataDir),['PlaceCellAnimal' num2str(n) 'Results.mat'])); +results = FitResult.fromStructure(resData.resStruct); +Summary = FitResSummary(results); +Summary.plotSummary; +end +[x_new,y_new]=meshgrid(-1:.01:1); %define new x and y +y_new = flipud(y_new); x_new = fliplr(x_new); +[theta_new,r_new] = cart2pol(x_new,y_new); +newData{1} =ones(size(x_new)); +newData{2} =x_new; newData{3} =y_new; +newData{4} =x_new.^2; newData{5} =y_new.^2; +newData{6} =x_new.*y_new; +idx = r_new<=1; +zpoly = cell(1,10); +cnt=0; +for l=0:3 +for m=-l:l +if(~any(mod(l-m,2))) +cnt = cnt+1; +temp = nan(size(x_new)); +temp(idx) = zernfun(l,m,r_new(idx),theta_new(idx),'norm'); +zpoly{cnt} = temp; +end +end +end +for n=1:numAnimals +clear lambdaGaussian lambdaZernike; +load(fullfile(placeCellDataDir,['PlaceCellDataAnimal' num2str(n) '.mat'])); +resData=load(fullfile(fileparts(placeCellDataDir),['PlaceCellAnimal' num2str(n) 'Results.mat'])); +results = FitResult.fromStructure(resData.resStruct); +for i=1:length(neuron) +lambdaGaussian{i} = results{i}.evalLambda(1,newData); +lambdaZernike{i} = results{i}.evalLambda(2,zpoly); +end +for i=1:length(neuron) +if(n==1) +h4=figure(4); +if(i==1) +annotation(h4,'textbox',... +[0.343261904761904 0.928571428571418 ... +0.392857142857143 0.0595238095238095],... +'String',{['Gaussian Place Fields - Animal#' ... +num2str(n)]},'FitBoxToText','on'); hold on; +end +subplot(7,7,i); +elseif(n==2) +h6=figure(6); +if(i==1) +annotation(h6,'textbox',... +[0.343261904761904 0.928571428571418 ... +0.392857142857143 0.0595238095238095],... +'String',{['Gaussian Place Fields - Animal#' ... +num2str(n)]},'FitBoxToText','on'); hold on; +end +subplot(6,7,i); +end +pcolor(x_new,y_new,lambdaGaussian{i}), shading interp +axis square; set(gca,'xtick',[],'ytick',[]); +if(n==1) +h5=figure(5); +if(i==1) +annotation(h5,'textbox',... +[0.343261904761904 0.928571428571418 ... +0.392857142857143 0.0595238095238095],... +'String',{['Zernike Place Fields - Animal#' ... +num2str(n)]},'FitBoxToText','on'); hold on; +end +subplot(7,7,i); +elseif(n==2) +h7=figure(7); +if(i==1) +annotation(h7,'textbox',... +[0.343261904761904 0.928571428571418 ... +0.392857142857143 0.0595238095238095],... +'String',{['Zernike Place Fields - Animal#' ... +num2str(n)]},'FitBoxToText','on'); hold on; +end +subplot(6,7,i); +end +pcolor(x_new,y_new,lambdaZernike{i}), shading interp +axis square; +set(gca,'xtick',[],'ytick',[]); +end +end +clear lambdaGaussian lambdaZernike; +load(fullfile(placeCellDataDir,'PlaceCellDataAnimal1.mat')); +resData=load(fullfile(fileparts(placeCellDataDir),'PlaceCellAnimal1Results.mat')); +results = FitResult.fromStructure(resData.resStruct); +for i=1:length(neuron) +lambdaGaussian{i} = results{i}.evalLambda(1,newData); +lambdaZernike{i} = results{i}.evalLambda(2,zpoly); +end +exampleCell = 25; +figure(8); +plot(x,y,'b',neuron{exampleCell}.xN,neuron{exampleCell}.yN,'r.'); +xlabel('x'); ylabel('y'); +title(['Animal#1, Cell#' num2str(exampleCell)]); +figure(9); +h_mesh = mesh(x_new,y_new,lambdaGaussian{exampleCell},'AlphaData',0); +get(h_mesh,'AlphaData'); +set(h_mesh,'FaceAlpha',0.2,'EdgeAlpha',0.2,'EdgeColor','b'); +hold on; +h_mesh = mesh(x_new,y_new,lambdaZernike{exampleCell},'AlphaData',0); +get(h_mesh,'AlphaData'); +set(h_mesh,'FaceAlpha',0.2,'EdgeAlpha',0.2,'EdgeColor','g'); +legend(results{exampleCell}.lambda.dataLabels); +plot(x,y,neuron{exampleCell}.xN,neuron{exampleCell}.yN,'r.'); +axis tight square; +xlabel('x position'); ylabel('y position'); +title(['Animal#1, Cell#' num2str(exampleCell)]); diff --git a/parity/line_port_snapshots/nSTATPaperExamples.txt b/parity/line_port_snapshots/nSTATPaperExamples.txt new file mode 100644 index 00000000..e117c2cf --- /dev/null +++ b/parity/line_port_snapshots/nSTATPaperExamples.txt @@ -0,0 +1,1576 @@ +echo off; +close all; clear all; +[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ... +getPaperDataDirs(); +nSTATRootDir = fileparts(dataDir); +if exist(nSTATRootDir,'dir') == 7 && ~strcmp(pwd,nSTATRootDir) +cd(nSTATRootDir); +end +epsc2 = importdata(fullfile(mEPSCDir,'epsc2.txt')); +sampleRate = 1000; +spikeTimes = epsc2.data(:,2)*1/sampleRate; %in seconds +nstConst = nspikeTrain(spikeTimes); +time = 0:(1/sampleRate):nstConst.maxTime; +baseline = Covariate(time,ones(length(time),1),'Baseline','time','s',... +'',{'\mu'}); +covarColl = CovColl({baseline}); +spikeColl = nstColl(nstConst); +trial = Trial(spikeColl,covarColl); +clear tc tcc; +tc{1} = TrialConfig({{'Baseline','\mu'}},sampleRate,[]); +tc{1}.setName('Constant Baseline'); +tcc = ConfigColl(tc); +results =Analysis.RunAnalysisForAllNeurons(trial,tcc,0); +close all; +scrsz = get(0,'ScreenSize'); +results.lambda.setDataLabels({'\lambda_{const}'}); +h=figure('OuterPosition',[scrsz(3)*.01 scrsz(4)*.04 ... +scrsz(3)*.98 scrsz(4)*.95]); +subplot(2,2,1); spikeColl.plot; +title({'Neural Raster with constant Mg^{2+} Concentration'},... +'FontWeight','bold',... +'Fontsize',12,... +'FontName','Arial'); +hx=xlabel('time [s]','Interpreter','none'); +hy=ylabel('mEPSCs','Interpreter','none'); +set(gca,'yTick',[0 1]); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +subplot(2,2,3); results.KSPlot; +subplot(2,2,2); results.plotInvGausTrans; +subplot(2,2,4); results.lambda.plot([],{{' ''b'' ,''Linewidth'',2'}}); +hx=xlabel('time [s]','Interpreter','none'); +hy=get(gca,'YLabel'); +set([hx hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +h_legend = legend('\lambda_{const}','Location','NorthEast'); +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)+.05 pos(2) pos(3:4)]); +set(h_legend,'FontSize',14) +close all; +washout1 = importdata(fullfile(mEPSCDir,'washout1.txt')); +washout2 = importdata(fullfile(mEPSCDir,'washout2.txt')); +sampleRate = 1000; +spikeTimes1 = 260+washout1.data(:,2)*1/sampleRate; %in seconds +spikeTimes2 = sort(washout2.data(:,2))*1/sampleRate + 745;%in seconds +nst = nspikeTrain([spikeTimes1; spikeTimes2]); +time = 260:(1/sampleRate):nst.maxTime; +scrsz = get(0,'ScreenSize'); +h=figure('OuterPosition',[scrsz(3)*.01 scrsz(4)*.04 scrsz(3)*.6 ... +scrsz(4)*.9]); +subplot(2,1,1); +nstConst.plot; set(gca,'yTick',[0 1]); hy=ylabel('mEPSCs'); +title({'Neural Raster with constant Mg^{2+} Concentration'},... +'FontWeight','bold',... +'Fontsize',12,... +'FontName','Arial'); +hx=get(gca,'XLabel'); +set([hx,hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +subplot(2,1,2); +nst.plot; set(gca,'yTick',[0 1]); hy=ylabel('mEPSCs'); +title({'Neural Raster with decreasing Mg^{2+} Concentration'},... +'FontWeight','bold',... +'Fontsize',12,... +'FontName','Arial'); +hx=get(gca,'XLabel'); +set([hx,hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +timeInd1 =find(time<495,1,'last'); %0-495sec first constant rate +timeInd2 =find(time<765,1,'last'); %495-765 second constant rate epoch +constantRate = ones(length(time),1); +rate1 = zeros(length(time),1); rate1(1:timeInd1)=1; +rate2 = zeros(length(time),1); rate2((timeInd1+1):timeInd2)=1; +rate3 = zeros(length(time),1); rate3((timeInd2+1):end)=1; +baseline = Covariate(time,[constantRate,rate1, rate2, rate3],... +'Baseline','time','s','',{'\mu','\mu_{1}','\mu_{2}','\mu_{3}'}); +covarColl = CovColl({baseline}); +spikeColl = nstColl(nst); +trial = Trial(spikeColl,covarColl); +maxWindow=.3; numWindows=20; +delta=1/sampleRate; +windowTimes =unique(round([0 logspace(log10(delta),... +log10(maxWindow),numWindows)]*sampleRate)./sampleRate); +windowTimes = windowTimes(1:11); +clear tc tcc; +tc{1} = TrialConfig({{'Baseline','\mu'}},sampleRate,[]); +tc{1}.setName('Constant Baseline'); +tc{2} = TrialConfig({{'Baseline','\mu_{1}','\mu_{2}','\mu_{3}'}},... +sampleRate,[]); tc{2}.setName('Diff Baseline'); +tcc = ConfigColl(tc); +results =Analysis.RunAnalysisForAllNeurons(trial,tcc,0); +close all; +scrsz = get(0,'ScreenSize'); +results.lambda.setDataLabels({'\lambda_{const}',... +'\lambda_{const-epoch}'}); +h=figure('OuterPosition',[scrsz(3)*.01 scrsz(4)*.04 ... +scrsz(3)*.98 scrsz(4)*.95]); +subplot(2,2,1); spikeColl.plot; +title({'Neural Raster with decreasing Mg^{2+} Concentration'},... +'FontWeight','bold',... +'Fontsize',12,... +'FontName','Arial'); +hx=xlabel('time [s]','Interpreter','none'); +set(gca,'YTickLabel',[]); +set([hx],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +timeInd1 =find(time<495,1,'last'); %0-495sec first constant rate +timeInd2 =find(time<765,1,'last'); %495-765 second constant rate epoch +plot([495;495],[0,1],'r','Linewidth',4); hold on; +plot([765;765],[0,1],'r','Linewidth',4); +subplot(2,2,3); results.KSPlot; +subplot(2,2,2); results.plotInvGausTrans; +subplot(2,2,4); +results.lambda.getSubSignal(1).plot([],{{' ''b'' ,''Linewidth'',2'}}); +results.lambda.getSubSignal(2).plot([],{{' ''g'' ,''Linewidth'',2'}}); +v=axis; axis([v(1) v(2) 0 5]); +hx=xlabel('time [s]','Interpreter','none'); +hy=get(gca,'YLabel'); +set([hx hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +h_legend = legend('\lambda_{const}','\lambda_{const-epoch}',... +'Location','NorthEast'); +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)+.05 pos(2)-.01 pos(3:4)]); +set(h_legend,'FontSize',14) +close all; +[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ... +getPaperDataDirs(); +nSTATRootDir = fileparts(dataDir); +if exist(nSTATRootDir,'dir') == 7 && ~strcmp(pwd,nSTATRootDir) +cd(nSTATRootDir); +end +Direction=3; Neuron=1; Stim=2; +datapath = fullfile(explicitStimulusDir,['Dir' num2str(Direction)], ... +['Neuron' num2str(Neuron)],['Stim' num2str(Stim)]); +data = load(fullfile(datapath,'trngdataBis.mat')); +time=0:.001:(length(data.t)-1)*.001; +stimData = data.t; +spikeTimes = time(data.y==1); +stim = Covariate(time,stimData./10,'Stimulus','time','s','mm',{'stim'}); +baseline = Covariate(time,ones(length(time),1),'Baseline','time','s','',... +{'constant'}); +nst = nspikeTrain(spikeTimes); +nspikeColl = nstColl(nst); +cc = CovColl({stim,baseline}); +trial = Trial(nspikeColl,cc); +scrsz = get(0,'ScreenSize'); +h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]); +subplot(3,1,1); +nst2 = nspikeTrain(spikeTimes); +nst2.setMaxTime(21);nst2.plot; +set(gca,'ytick',[0 1]); +xlabel(''); +hy=ylabel('spikes'); +set(hy,'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title({'Neural Raster'},'FontWeight','bold','FontSize',16,'FontName','Arial'); +set(gca, ... +'XTick' , 0:1:max(time), ... +'XTickLabel' , [],... +'LineWidth' , 1 ); +subplot(3,1,2); +stim.getSigInTimeWindow(0,21).plot([],{{' ''k'' '}}); legend off; +set(gca,'ytick',[0 0.5 1]); +hy=ylabel('Displacement [mm]','Interpreter','none'); xlabel(''); +set(hy,'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title({'Stimulus - Whisker Displacement'},'FontWeight','bold',... +'FontSize',16,'FontName','Arial'); +set(gca, ... +'XTick' , 0:1:max(time), ... +'XTickLabel' , [],... +'YTick' , 0:.25:1, ... +'LineWidth' , 1 ); +subplot(3,1,3); +stim.derivative.getSigInTimeWindow(0,21).plot([],{{' ''k'' '}}); legend off; +set(gca,'ytick',[-80 0 80]); +axis([0 21 -80 80]); +hy=ylabel('Displacement Velocity [mm/s]','Interpreter','none'); +hx= xlabel('time [s]','Interpreter','none'); +set([hx hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title({'Displacement Velocity'},'FontWeight','bold',... +'FontSize',16,'FontName','Arial'); +set(gca, ... +'XTick' , 0:1:max(time), ... +'YTick' , -80:40:80, ... +'LineWidth' , 1 ); +clear c; close all; +selfHist = [] ; NeighborHist = []; sampleRate = 1000; +c{1} = TrialConfig({{'Baseline','constant'}},sampleRate,selfHist,NeighborHist); +c{1}.setName('Baseline'); +cfgColl= ConfigColl(c); +results = Analysis.RunAnalysisForAllNeurons(trial,cfgColl,0); +scrsz = get(0,'ScreenSize'); +h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]); +subplot(7,2,[1 3 5]) +results.Residual.xcov(stim).windowedSignal([0,1]).plot; +ylabel(''); +[m,ind,ShiftTime] = max(results.Residual.xcov(stim).windowedSignal([0,1])); +title(['Cross Correlation Function - Peak at t=' num2str(ShiftTime) ' sec'],'FontWeight','bold',... +'FontSize',12,... +'FontName','Arial'); +hold on; +h=plot(ShiftTime,m,'ro','Linewidth',3); +set(h, 'MarkerFaceColor',[1 0 0], 'MarkerEdgeColor',[1 0 0]); +hx=xlabel('Lag [s]','Interpreter','none'); +set(hx,'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +stim = Covariate(time,stimData,'Stimulus','time','s','V',{'stim'}); +stim = stim.shift(ShiftTime); +baseline = Covariate(time,ones(length(time),1),'Baseline','time','s','',... +{'\mu'}); +nst = nspikeTrain(spikeTimes); +nspikeColl = nstColl(nst); +cc = CovColl({stim,baseline}); +trial2 = Trial(nspikeColl,cc); +clear c; +selfHist = [] ; NeighborHist = []; sampleRate = 1000; +c{1} = TrialConfig({{'Baseline','\mu'}},sampleRate,selfHist,... +NeighborHist); +c{1}.setName('Baseline'); +c{2} = TrialConfig({{'Baseline','\mu'},{'Stimulus','stim'}},... +sampleRate,selfHist,NeighborHist); +c{2}.setName('Baseline+Stimulus'); +cfgColl= ConfigColl(c); +results = Analysis.RunAnalysisForAllNeurons(trial2,cfgColl,0); +sampleRate=1000; +delta=1/sampleRate*1; +maxWindow=1; numWindows=32; +windowTimes =unique(round([0 logspace(log10(delta),... +log10(maxWindow),numWindows)]*sampleRate)./sampleRate); +results =Analysis.computeHistLagForAll(trial2,windowTimes,... +{{'Baseline','\mu'},{'Stimulus','stim'}},'BNLRCG',0,sampleRate,0); +KSind = find(results{1}.KSStats.ks_stat == min(results{1}.KSStats.ks_stat)); +AICind = find((results{1}.AIC(2:end)-results{1}.AIC(1))== ... +min(results{1}.AIC(2:end)-results{1}.AIC(1))) +1; +BICind = find((results{1}.BIC(2:end)-results{1}.BIC(1))== ... +min(results{1}.BIC(2:end)-results{1}.BIC(1))) +1; +if(AICind==1) +AICind=inf; +end +if(BICind==1) +BICind=inf; %sometime BIC is non-decreasing and the index would be 1 +end +windowIndex = min([AICind,BICind]) %use the minimum order model +Summary = FitResSummary(results); +clear c; +if(windowIndex>1) +selfHist = windowTimes(1:windowIndex+1); +else +selfHist = []; +end +NeighborHist = []; sampleRate = 1000; +subplot(7,2,2); +x=0:length(windowTimes)-1; +plot(x,results{1}.KSStats.ks_stat,'.-'); axis tight; hold on; +plot(x(windowIndex),results{1}.KSStats.ks_stat(windowIndex),'r*'); +set(gca,'XTick', 0:5:results{1}.numResults-1,'XTickLabel',[],... +'TickLength', [.02 .02] , ... +'XMinorTick', 'on','LineWidth' , 1); +hy=ylabel('KS Statistic'); +set(hy,'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +dAIC = results{1}.AIC-results{1}.AIC(1); +title({'Model Selection via change'; 'in KS Statistic, AIC, and BIC'},... +'FontWeight','bold',... +'FontSize',12,... +'FontName','Arial'); +subplot(7,2,4); plot(x,dAIC,'.-'); +set(gca,'XTick', 0:5:results{1}.numResults-1,'XTickLabel',[],... +'TickLength', [.02 .02] , ... +'XMinorTick', 'on','LineWidth' , 1); +hy=ylabel('\Delta AIC');axis tight; hold on; +set(hy,'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +plot(x(windowIndex),dAIC(windowIndex),'r*'); +dBIC = results{1}.BIC-results{1}.BIC(1); +subplot(7,2,6); plot(x,dBIC,'.-'); +hy=ylabel('\Delta BIC'); axis tight; hold on; +plot(x(windowIndex),dBIC(windowIndex),'r*'); +hx=xlabel('# History Windows, Q'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +set(gca, ... +'TickLength' , [.02 .02] , ... +'XMinorTick' , 'on' , ... +'XTick' , 0:5:results{1}.numResults-1, ... +'LineWidth' , 1 ); +c{1} = TrialConfig({{'Baseline','\mu'}},sampleRate,[],NeighborHist); +c{1}.setName('Baseline'); +c{2} = TrialConfig({{'Baseline','\mu'},{'Stimulus','stim'}},... +sampleRate,[],[]); +c{2}.setName('Baseline+Stimulus'); +c{3} = TrialConfig({{'Baseline','\mu'},{'Stimulus','stim'}},... +sampleRate,windowTimes(1:windowIndex),[]); +c{3}.setName('Baseline+Stimulus+Hist'); +cfgColl= ConfigColl(c); +results = Analysis.RunAnalysisForAllNeurons(trial2,cfgColl,0); +results.lambda.setDataLabels({'\lambda_{const}','\lambda_{const+stim}',... +'\lambda_{const+stim+hist}'}); +subplot(7,2,[9 11 13]); results.KSPlot; +subplot(7,2,[10 12 14]); results.plotCoeffs; legend off; +clear all; +[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ... +getPaperDataDirs(); +close all; +delta = 0.001; +Tmax = 1; +time = 0:delta:Tmax; +f=2; +mu = -3; +tempData = 1*sin(2*pi*f*time)+mu; %lambda >=0 +lambdaData = exp(tempData)./(1+exp(tempData))*(1/delta); +lambda = Covariate(time,lambdaData, '\lambda(t)','time','s',... +'spikes/sec',{'\lambda_{1}'},{{' ''b'', ''LineWidth'' ,2'}}); +numRealizations = 20; +spikeCollSim = CIF.simulateCIFByThinningFromLambda(lambda,numRealizations); +scrsz = get(0,'ScreenSize'); +h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]); +subplot(2,2,3);spikeCollSim.plot; +set(gca,'YTick',0:5:numRealizations,'YTickLabel',0:5:numRealizations); +title({[num2str(numRealizations) ' Simulated Point Process Sample Paths']},... +'FontWeight','bold','Fontsize',14,'FontName','Arial'); +xlabel('time [s]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +subplot(2,2,1);lambda.plot; +title({'Simulated Conditional Intensity Function (CIF)'},... +'FontWeight','bold','FontSize',14,'FontName','Arial'); +xlabel('time [s]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +hy=get(gca,'YLabel'); +set(hy,'FontName', 'Arial','FontSize',14,'FontWeight','bold'); +x = load(fullfile(psthDir,'Results.mat')); +numTrials = x.Results.Data.Spike_times_STC.balanced_SUA.Nr_trials; +cellNum=6; clear nst; +for i=1:numTrials +spikeTimes{i}=x.Results.Data.Spike_times_STC.balanced_SUA.spike_times{1,i,cellNum}; +nst{i} = nspikeTrain(spikeTimes{i}); +nst{i}.setName(num2str(cellNum)); +end +spikeCollReal1=nstColl(nst); +spikeCollReal1.setMinTime(0); spikeCollReal1.setMaxTime(2); +subplot(2,2,2);spikeCollReal1.plot; set(gca,'YTick',0:2:numTrials,... +'YTickLabel',0:2:numTrials); +xlabel('time [s]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +title('Response to Moving Visual Stimulus (Neuron 6)',... +'FontWeight','bold','Fontsize',14,'FontName','Arial'); +cellNum=1; clear nst; +for i=1:numTrials +spikeTimes{i}=x.Results.Data.Spike_times_STC.balanced_SUA.spike_times{1,i,cellNum}; +nst{i} = nspikeTrain(spikeTimes{i}); +nst{i}.setName(num2str(cellNum)); +end +spikeCollReal2=nstColl(nst); +spikeCollReal2.setMinTime(0); spikeCollReal2.setMaxTime(2); +subplot(2,2,4);spikeCollReal2.plot; +set(gca,'YTick',0:2:numTrials,'YTickLabel',0:2:numTrials); +xlabel('time [s]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +title('Response to Moving Visual Stimulus (Neuron 1)','FontWeight',... +'bold','Fontsize',14,'FontName','Arial'); +close all; +scrsz = get(0,'ScreenSize'); +h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]); +binsize = .05; %50ms window +psth = spikeCollSim.psth(binsize); +psthGLM = spikeCollSim.psthGLM(binsize); +true = lambda; %rate*delta = expected number of arrivals per bin +subplot(2,3,4); +h1=true.plot([],{{' ''b'',''Linewidth'',4'}}); +h3=psthGLM.plot([],{{' ''k'',''Linewidth'',4'}}); +h2=psth.plot([],{{' ''rx'',''Linewidth'',4'}}); +xlabel('time [s]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +ylabel('[spikes/sec]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +legend off; +h_legend=legend([h1(1) h2(1) h3(1)],'true','PSTH','PSTH_{glm}'); +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)+.005 pos(2)+.095 pos(3:4)]); +subplot(2,3,1);spikeCollSim.plot; +set(gca,'YTick',0:2:spikeCollSim.numSpikeTrains,'YTickLabel',0:2:spikeCollSim.numSpikeTrains); +xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +subplot(2,3,5); +binsize = .05; %50ms window +psthReal1 = spikeCollReal1.psth(binsize); +psthGLMReal1 = spikeCollReal1.psthGLM(binsize);%,[],[],[],[],[],1000); +h3=psthGLMReal1.plot([],{{' ''k'',''Linewidth'',4'}}); +h2=psthReal1.plot([],{{' ''rx'',''Linewidth'',4'}}); +xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +ylabel('[spikes/sec]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +h_legend=legend([h2(1) h3(1)],'PSTH','PSTH_{glm}'); +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)+.005 pos(2)+.07 pos(3:4)]); +subplot(2,3,2); spikeCollReal1.plot; +set(gca,'YTick',0:2:spikeCollReal2.numSpikeTrains,'YTickLabel',0:2:spikeCollReal2.numSpikeTrains); +xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +subplot(2,3,6); +psthReal2 = spikeCollReal2.psth(binsize); +psthGLMReal2 = spikeCollReal2.psthGLM(binsize);%,[],[],[],[],[],1000); +h3=psthGLMReal2.plot([],{{' ''k'',''Linewidth'',4'}}); +h2=psthReal2.plot([],{{' ''rx'',''Linewidth'',4'}}); +xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +ylabel('[spikes/sec]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +h_legend=legend([h2(1) h3(1)],'PSTH','PSTH_{glm}'); +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)+.005 pos(2)+.07 pos(3:4)]); +subplot(2,3,3); spikeCollReal2.plot; +set(gca,'YTick',0:2:spikeCollReal2.numSpikeTrains,'YTickLabel',0:2:spikeCollReal2.numSpikeTrains); +xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +close all; +clear all; +[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ... +getPaperDataDirs(); +delta = 0.001; Tmax = 1; +time = 0:delta:Tmax; +Ts=.001; +numRealizations = 50; %Each realization corresponds to a distinct trial +for i=1:numRealizations +f=2; b1(i)=3*((i)/numRealizations);b0=-3; +u = sin(2*pi*f*time); +e = zeros(length(time),1); %No Ensemble input +stim=Covariate(time',u,'Stimulus','time','s','Voltage',{'sin'}); +ens =Covariate(time',e,'Ensemble','time','s','Spikes',{'n1'}); +mu=b0; +histCoeffs=[-4 -1 -.5]; +H=tf(histCoeffs,[1],Ts,'Variable','z^-1'); +S=tf([b1(i)],1,Ts,'Variable','z^-1'); +E=tf([0],1,Ts,'Variable','z^-1'); +simTypeSelect='binomial'; %Parameters are used to compute +[sC, lambdaTemp]=CIF.simulateCIF(mu,H,S,E,stim,ens,1,simTypeSelect); +if(i==1) +lambda=lambdaTemp; %Store the conditional intensity function +else +lambda = lambda.merge(lambdaTemp); %Add it to the other realizations +end +nst{i} = sC.getNST(1); %get the neural spikeTrain from the collection +nst{i} = nst{i}.resample(1/delta); %make sure that it is sampled at the current samplerate +end +spikeColl = nstColl(nst); %Create a collection of the spike trains across trials +close all; +scrsz = get(0,'ScreenSize'); +h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]); +subplot(3,2,[3 4]); spikeColl.plot; +set(gca,'ytick',0:10:numRealizations,'ytickLabel',0:10:numRealizations); +set(gca,'xtick',0:.1:Tmax,'xtickLabel',0:.1:Tmax); xlabel(''); +xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +ylabel('Trial [k]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +title('Simulated Neural Raster','Interpreter','none','FontName', 'Arial',... +'Fontsize',14,'FontWeight','bold'); +stimData = exp(b0 + u'*b1); +if(strcmp(simTypeSelect,'binomial')) +stimData = stimData./(1+stimData); +end +subplot(3,2,1); plot(time,u,'k','LineWidth',3); +xlabel('time [s]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +ylabel('Stimulus','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +title('Within Trial Stimulus','Interpreter','none','FontName', 'Arial',... +'Fontsize',14,'FontWeight','bold'); +subplot(3,2,2); plot(1:length(b1),b1,'k','LineWidth',3); +xlabel('Trial [k]','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +ylabel('Stimulus Gain','Interpreter','none','FontName', 'Arial','Fontsize',... +12,'FontWeight','bold'); +title('Across Trial Stimulus Gain','Interpreter','none','FontName',... +'Arial','Fontsize',14,'FontWeight','bold'); +subplot(3,2,[5 6]); +imagesc(stimData'./delta); set(gca, 'YDir','normal'); +set(gca,'xtick',0:100:Tmax/delta,'xtickLabel',0:.1:Tmax); +set(gca,'ytick',0:10:numRealizations,'ytickLabel',0:10:numRealizations); +xlabel('time [s]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +ylabel('Trial [k]','Interpreter','none','FontName', 'Arial',... +'Fontsize',12,'FontWeight','bold'); +title('True Conditional Intensity Function','Interpreter',... +'none','FontName', 'Arial','Fontsize',14,'FontWeight','bold'); +axis tight; +stim = Covariate(time,sin(2*pi*f*time),'Stimulus','time','s','V',{'stim'}); +baseline = Covariate(time,ones(length(time),1),'Baseline','time','s','',... +{'constant'}); +windowTimes=[0:.001:.003]; +numBasis = 25; +spikeColl.resample(1/delta); % Enforce sampleRate +spikeColl.setMaxTime(Tmax); % Make all spikeTrains end at time Tmax +dN=spikeColl.dataToMatrix'; % Convert the spikeTrains into a matrix +dN(dN>1)=1; % One should sample finely enough so there is +basisWidth=(spikeColl.maxTime-spikeColl.minTime)/numBasis; +if(simTypeSelect==0) +fitType='binomial'; +else +fitType='poisson'; +end +if(strcmp(fitType,'binomial')) +Algorithm = 'BNLRCG'; % BNLRCG - faster Truncated, L-2 Regularized, +else +Algorithm = 'GLM'; % Standard Matlab GLM (Can be used for binomial or +end +[psthSig, ~, psthResult] =spikeColl.psthGLM(basisWidth,windowTimes,fitType); +gamma0=psthResult.getHistCoeffs';%+.1*randn(size(histCoeffs)); +gamma0(isnan(gamma0))=-5; % Depending on the amount of data the +x0=psthResult.getCoeffs; %The initial estimate for the SSGLM model +numVarEstIter=10; +Q0 = spikeColl.estimateVarianceAcrossTrials(numBasis,windowTimes,... +numVarEstIter,fitType); +A=eye(numBasis,numBasis); +delta = 1/spikeColl.sampleRate; +CompilingHelpFile=1; +if(~CompilingHelpFile) +Q0d=diag(Q0); +neuronName = psthResult.neuronNumber; +[xK,WK, WkuFinal,Qhat,gammahat,fitResults,stimulus,stimCIs,logll,... +QhatAll,gammahatAll,nIter]=DecodingAlgorithms.PPSS_EMFB(A,Q0d,x0,... +dN,fitType,delta,gamma0,windowTimes, numBasis,neuronName); +fR = fitResults.toStructure; +psthR = psthResult.toStructure; +end +installPath = which('nSTAT_Install'); +if isempty(installPath) +error('nSTATPaperExamples:MissingInstallPath', ... +'Could not locate nSTAT_Install.m on MATLAB path.'); +end +nstatRoot = fileparts(installPath); +if exist(nstatRoot,'dir') == 7 && ~strcmp(pwd,nstatRoot) +cd(nstatRoot); +end +addpath(nstatRoot,'-begin'); +load(fullfile(nstatRoot,'data','SSGLMExampleData.mat')); +fitResults = FitResult.fromStructure(fR); +psthResult = FitResult.fromStructure(psthR); +t=psthResult.mergeResults(fitResults); +t.lambda.setDataLabels({'\lambda_{PSTH}','\lambda_{SSGLM}'}); +scrsz = get(0,'ScreenSize'); +h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]); +subplot(2,2,1); t.KSPlot; +subplot(2,2,2); t.plotResidual; +subplot(2,2,3); t.plotInvGausTrans; +subplot(2,2,4); t.plotSeqCorr; +S=FitResSummary(t); +dAIC=diff(S.AIC) +dBIC=diff(S.BIC) +dKS =diff(S.KSStats); +close all; +minTime=0; maxTime = Tmax; +stimData = stim.data*b1; +if(strcmp(fitType,'poisson')) +actStimEffect=exp(stimData + b0)./delta; +elseif(strcmp(fitType,'binomial')) +actStimEffect=exp(stimData + b0)./(1+exp(stimData + b0))./delta; +end +if(~isempty(numBasis)) +basisWidth = (maxTime-minTime)/numBasis; +sampleRate=1/delta; +unitPulseBasis=nstColl.generateUnitImpulseBasis(basisWidth,minTime,... +maxTime,sampleRate); +basisMat = unitPulseBasis.data; +end +if(strcmp(fitType,'poisson')) +estStimEffect=exp(basisMat*xK)./delta; +elseif(strcmp(fitType,'binomial')) +estStimEffect=exp(basisMat*xK)./(1+exp(basisMat*xK))./delta; +end +scrsz = get(0,'ScreenSize'); +h=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.4 scrsz(4)*.8]); +subplot(3,1,[1 2 3]); +lighting gouraud +surf((1:length(b1))',stim.time,actStimEffect,'FaceAlpha',0.1,... +'EdgeAlpha',0.1,'AlphaData',0.1); +hx=xlabel('Trial [k]'); hy=ylabel('time [s]'); +hz=zlabel('Stimulus Effect [spikes/sec]'); hold all; +set([hx hy hz],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +surf((1:length(b1))',stim.time,estStimEffect(:,1:length(b1)),... +'FaceAlpha',0.5,'EdgeAlpha',0.1,'AlphaData',0.5); %xlabel('Trial [k]'); ylabel('time [s]'); zlabel('Stimulus Effect'); +set(gca,'YDir','reverse'); +set(gca,'ytick',0:.1:Tmax,'ytickLabel',0:.1:Tmax); +title('SSGLM Estimated vs. Actual Stimulus Effect','FontWeight','bold',... +'Fontsize',14,... +'FontName','Arial'); +close all; +h=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.4 scrsz(4)*.8]); +subplot(3,1,1); +lighting gouraud +mesh((1:length(b1))',stim.time,actStimEffect); +hx=xlabel('Trial [k]'); hy=ylabel('time [s]'); +zlabel('Stimulus Effect [spikes/sec]'); hold all; +set([hx hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title('True Stimulus Effect','FontWeight','bold',... +'Fontsize',14,... +'FontName','Arial'); +set(gca,'xtick',[],'xtickLabel',[]); +set(gca,'ytick',[],'ytickLabel',[]); +CLIM = [min(min(stimData./delta)) max(max(stimData./delta))]; +view(gca,[90 -90]); +subplot(3,1,2); +lighting gouraud +mesh((1:length(b1))',stim.time,repmat(psthSig.data, [1 numRealizations])); +hx=xlabel('Trial [k]'); hy=ylabel('time [s]'); +hz=zlabel('Stimulus Effect [spikes/sec]'); hold all; +set([hx hy hz],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title('PSTH Estimated Stimulus Effect','FontWeight','bold',... +'Fontsize',14,... +'FontName','Arial'); +set(gca,'xtick',[],'xtickLabel',[]); +set(gca,'ytick',[],'ytickLabel',[]); +CLIM = [min(min(stimData./delta)) max(max(stimData./delta))]; +view(gca,[90 -90]); +subplot(3,1,3); +lighting gouraud +mesh((1:length(b1))',stim.time,estStimEffect); +xlabel('Trial [k]'); ylabel('time [s]'); +zlabel('Stimulus Effect [spikes/sec]'); hold all; +hx=get(gca,'XLabel'); hy=get(gca,'YLabel'); hz=get(gca,'ZLabel'); +set([hx hy hz],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title('SSGLM Estimated Stimulus Effect','FontWeight','bold',... +'Fontsize',14,... +'FontName','Arial'); +set(gca,'xtick',[],'xtickLabel',[]); +set(gca,'ytick',[],'ytickLabel',[]); +set(gca, 'YDir','normal') +view(gca,[90 -90]); +echo off; +close all; +minTime=0; maxTime = Tmax; +if(~isempty(numBasis)) +basisWidth = (maxTime-minTime)/numBasis; +sampleRate=1/delta; +unitPulseBasis=nstColl.generateUnitImpulseBasis(basisWidth,... +minTime,maxTime,sampleRate); +basisMat = unitPulseBasis.data; +end +t0=0; tf=Tmax; +[spikeRateBinom, ProbMat,sigMat]=DecodingAlgorithms.computeSpikeRateCIs(xK,... +WkuFinal,dN,t0,tf,fitType,delta,gammahat,windowTimes); +lt=find(sigMat(1,:)==1,1,'first'); +display(['The learning trial (compared to the first trial) is trial #' ... +num2str(find(sigMat(1,:)==1,1,'first'))]); +scrsz = get(0,'ScreenSize'); +h=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.8]); +subplot(2,3,1); +spikeRateBinom.setName(['(' num2str(Tmax) '-0)^-1*\Lambda(0,' ... +num2str(Tmax) ')']); +spikeRateBinom.plot([],{{' ''k'',''Linewidth'',4'}}); +v=axis; +plot(lt*[1;1],v(3:4),'r','Linewidth',2); +hx=xlabel('Trial [k]','Interpreter','none'); hold all; +hy=ylabel('Average Firing Rate [spikes/sec]','Interpreter','none'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title(['Learning Trial:' num2str(lt)],'FontWeight','bold',... +'Fontsize',12,... +'FontName','Arial'); +h=subplot(2,3,[2 3 5 6]); +K=size(dN,1); +colormap(flipud(gray)); +imagesc(ProbMat); hold on; +for k=1:K +for m=(k+1):K +if(sigMat(k,m)==1) +plot3(m,k,1,'r*'); hold on; +end +end +end +set(h,'XAxisLocation','top','YAxisLocation','right'); +hx=xlabel('Trial Number','Interpreter','none'); hold all; +hy=ylabel('Trial Number','Interpreter','none'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +subplot(2,3,4) +stim1 = Covariate(time, basisMat*stimulus(:,1),'Trial1','time','s',... +'spikes/sec'); +temp = ConfidenceInterval(time, basisMat*squeeze(stimCIs(:,1,:))); +stim1.setConfInterval(temp); +stimlt = Covariate(time, basisMat*stimulus(:,lt),'Trial1','time','s',... +'spikes/sec'); +temp = ConfidenceInterval(time, basisMat*squeeze(stimCIs(:,lt,:))); +temp.setColor('r'); +stimlt.setConfInterval(temp); +stimltm1 = Covariate(time, basisMat*stimulus(:,lt-1),'Trial1','time','s',... +'spikes/sec'); +temp = ConfidenceInterval(time, basisMat*squeeze(stimCIs(:,lt-1,:))); +temp.setColor('r'); +stimltm1.setConfInterval(temp); +h1=stim1.plot([],{{' ''k'',''Linewidth'',4'}}); hold all; +h2=stimlt.plot([],{{' ''r'',''Linewidth'',4'}}); +hx=xlabel('time [s]','Interpreter','none'); hold all; +hy=ylabel('Firing Rate [spikes/sec]','Interpreter','none'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title({'Learning Trial Vs. Baseline Trial';'with 95% CIs'},'FontWeight','bold',... +'Fontsize',12,... +'FontName','Arial'); +h_legend=legend([h1(1) h2(1)],'\lambda_{1}(t)',['\lambda_{' num2str(lt) '}(t)']); +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)+.03 pos(2)+.01 pos(3:4)]); +close all; +load(fullfile(placeCellDataDir,'PlaceCellDataAnimal1.mat')); +exampleCell = [2 21 25 49]; +scrsz = get(0,'ScreenSize'); +h=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.6 scrsz(4)*.9]); +for i=1:length(exampleCell) +subplot(2,2,i); +h1=plot(x,y,'b','Linewidth',.5); hold on; +h2=plot(neuron{exampleCell(i)}.xN,neuron{exampleCell(i)}.yN,'r.',... +'MarkerSize',7); +hx=xlabel('X Position'); hy=ylabel('Y Position'); +title(['Cell#' num2str(exampleCell(i))],'FontWeight','bold',... +'Fontsize',12,'FontName','Arial'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +set(gca,'xTick',-1:.5:1,'yTick',-1:.5:1); axis square; +if(i==4) +h_legend = legend([h1 h2],'Animal Path',... +'Location at time of spike'); +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)+.09 pos(2)+.06 pos(3:4)]); +end +end +numAnimals=2; +CompilingHelpFile=1; +if(~CompilingHelpFile) +for n=1:numAnimals +clear x y neuron time nst tc tcc z; +load(fullfile(placeCellDataDir,['PlaceCellDataAnimal' num2str(n) '.mat'])); +for i=1:length(neuron) +nst{i} = nspikeTrain(neuron{i}.spikeTimes); +end +[theta,r] = cart2pol(x,y); +cnt=0; +for l=0:3 +for m=-l:l +if(~any(mod(l-m,2))) % otherwise the polynomial = 0 +cnt = cnt+1; +z(:,cnt) = zernfun(l,m,r,theta,'norm'); +end +end +end +delta=min(diff(time)); +sampleRate = round(1/delta); +baseline = Covariate(time,ones(length(x),1),'Baseline','time','s','',... +{'mu'}); +zernike = Covariate(time,z,'Zernike','time','s','m',{'z1','z2','z3',... +'z4','z5','z6','z7','z8','z9','z10'}); +gaussian = Covariate(time,[x y x.^2 y.^2 x.*y],'Gaussian','time',... +'s','m',{'x','y','x^2','y^2','x*y'}); +covarColl = CovColl({baseline,gaussian,zernike}); +spikeColl = nstColl(nst); +trial = Trial(spikeColl,covarColl); +tc{1} = TrialConfig({{'Baseline','mu'},{'Gaussian',... +'x','y','x^2','y^2','x*y'}},sampleRate,[]); +tc{1}.setName('Gaussian'); +tc{2} = TrialConfig({{'Zernike' 'z1','z2','z3','z4','z5','z6',... +'z7','z8','z9','z10'}},sampleRate,[]); +tc{2}.setName('Zernike'); +tcc = ConfigColl(tc); +results =Analysis.RunAnalysisForAllNeurons(trial,tcc,0); +resStruct =FitResult.CellArrayToStructure(results); +filename = fullfile(dataDir,['PlaceCellAnimal' num2str(n) 'Results']); +save(filename,'resStruct'); +end +end +clear Summary; +numAnimals =2; +for n=1:numAnimals +resData = load(fullfile(dataDir,['PlaceCellAnimal' num2str(n) 'Results.mat'])); +results = FitResult.fromStructure(resData.resStruct); +Summary{n} = FitResSummary(results); +end +close all; +scrsz = get(0,'ScreenSize'); +h=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.6 scrsz(4)*.5]); +subplot(1,3,1); +maxLength = max([Summary{1}.numNeurons,Summary{2}.numNeurons]); +dKS = nan(maxLength, 2); +dKS(1:Summary{1}.numNeurons,1) = (Summary{1}.KSStats(:,1)-Summary{1}.KSStats(:,2)) ; +dKS(1:Summary{2}.numNeurons,2) = (Summary{2}.KSStats(:,1)-Summary{2}.KSStats(:,2)) ; +boxplot(dKS ,{'Animal 1', 'Animal 2'},'labelorientation','inline'); +title('\Delta KS Statistic','FontWeight','bold','FontSize',14,... +'FontName','Arial'); +subplot(1,3,2); +dAIC = nan(maxLength, 2); +dAIC(1:Summary{1}.numNeurons,1) = Summary{1}.getDiffAIC(1); +dAIC(1:Summary{2}.numNeurons,2) = Summary{2}.getDiffAIC(1); +boxplot(dAIC ,{'Animal 1', 'Animal 2'},'labelorientation','inline'); +title('\Delta AIC','FontWeight','bold','FontSize',14,'FontName','Arial'); +subplot(1,3,3); +dBIC = nan(maxLength, 2); +dBIC(1:Summary{1}.numNeurons,1) = Summary{1}.getDiffBIC(1); +dBIC(1:Summary{2}.numNeurons,2) = Summary{2}.getDiffBIC(1); +boxplot(dBIC ,{'Animal 1', 'Animal 2'},'labelorientation','inline'); %ylabel('\Delta BIC'); %xticklabel_rotate([],45,[],'Fontsize',6); +title('\Delta BIC','FontWeight','bold','FontSize',14,'FontName','Arial'); +close all; +[x_new,y_new]=meshgrid(-1:.01:1); %define new x and y +y_new = flipud(y_new); x_new = fliplr(x_new); +[theta_new,r_new] = cart2pol(x_new,y_new); +newData{1} =ones(size(x_new)); +newData{2} =x_new; newData{3} =y_new; +newData{4} =x_new.^2; newData{5} =y_new.^2; +newData{6} =x_new.*y_new; +idx = r_new<=1; +zpoly = cell(1,10); +cnt=0; +for l=0:3 +for m=-l:l +if(~any(mod(l-m,2))) +cnt = cnt+1; +temp = nan(size(x_new)); +temp(idx) = zernfun(l,m,r_new(idx),theta_new(idx),'norm'); +zpoly{cnt} = temp; +end +end +end +for n=1:numAnimals +clear lambdaGaussian lambdaZernike; +load(fullfile(placeCellDataDir,['PlaceCellDataAnimal' num2str(n) '.mat'])); +resData = load(fullfile(dataDir,['PlaceCellAnimal' num2str(n) 'Results.mat'])); +results = FitResult.fromStructure(resData.resStruct); +for i=1:length(neuron) +lambdaGaussian{i} = results{i}.evalLambda(1,newData); +lambdaZernike{i} = results{i}.evalLambda(2,zpoly); +end +for i=1:length(neuron) +if(n==1) +h4=figure(4); +colormap('jet'); +if(i==1) +tb=annotation(h4,'textbox',... +[0.283261904761904 0.928571428571418 ... +0.392857142857143 0.0595238095238095],... +'String',{['Gaussian Place Fields - Animal#' ... +num2str(n)]},'FitBoxToText','on','Fontsize',11,... +'FontName','Arial','FontWeight','bold','LineStyle',... +'none','HorizontalAlignment','center'); hold on; +end +subplot(7,7,i); +elseif(n==2) +h6=figure(6); +colormap('jet'); +if(i==1) +annotation(h6,'textbox',... +[0.283261904761904 0.928571428571418 ... +0.392857142857143 0.0595238095238095],... +'String',{['Gaussian Place Fields - Animal#' ... +num2str(n)]},'FitBoxToText','on','Fontsize',11,... +'FontName','Arial','FontWeight','bold','LineStyle',... +'none','HorizontalAlignment','center'); hold on; +end +subplot(6,7,i); +end +pcolor(x_new,y_new,lambdaGaussian{i}), shading interp +axis square; set(gca,'xtick',[],'ytick',[]); +set(gca, 'Box' , 'off'); +if(n==1) +h5=figure(5); +colormap('jet'); +if(i==1) +annotation(h5,'textbox',... +[0.303261904761904 0.928571428571418 ... +0.392857142857143 0.0595238095238095],... +'String',{['Zernike Place Fields - Animal#' ... +num2str(n)]},'FitBoxToText','on','Fontsize',11,... +'FontName','Arial','FontWeight','bold','LineStyle','none'); hold on; +end +subplot(7,7,i); +elseif(n==2) +h7=figure(7); +colormap('jet'); +if(i==1) +annotation(h7,'textbox',... +[0.303261904761904 0.928571428571418 ... +0.392857142857143 0.0595238095238095],... +'String',{['Zernike Place Fields - Animal#' ... +num2str(n)]},'FitBoxToText','on','Fontsize',11,... +'FontName','Arial','FontWeight','bold','LineStyle',... +'none','HorizontalAlignment','center'); hold on; +end +subplot(6,7,i); +end +pcolor(x_new,y_new,lambdaZernike{i}), shading interp +axis square; +set(gca,'xtick',[],'ytick',[]); +set(gca, 'Box' , 'off'); +end +end +clear lambdaGaussian lambdaZernike; +load(fullfile(placeCellDataDir,'PlaceCellDataAnimal1.mat')); +resData = load(fullfile(dataDir,'PlaceCellAnimal1Results.mat')); +results = FitResult.fromStructure(resData.resStruct); +for i=1:length(neuron) +lambdaGaussian{i} = results{i}.evalLambda(1,newData); +lambdaZernike{i} = results{i}.evalLambda(2,zpoly); +end +exampleCell = 25; +close all; +h9=figure(9); +h_mesh = mesh(x_new,y_new,lambdaGaussian{exampleCell},'AlphaData',0); +get(h_mesh,'AlphaData'); +set(h_mesh,'FaceAlpha',0.2,'EdgeAlpha',0.2,'EdgeColor','b'); +hold on; +h_mesh = mesh(x_new,y_new,lambdaZernike{exampleCell},'AlphaData',0); +get(h_mesh,'AlphaData'); +set(h_mesh,'FaceAlpha',0.2,'EdgeAlpha',0.2,'EdgeColor','g'); +plot(x,y,neuron{exampleCell}.xN,neuron{exampleCell}.yN,'r.'); +axis tight square; +xlabel('x position'); ylabel('y position'); +title(['Animal#1, Cell#' num2str(exampleCell)],'FontWeight','bold',... +'Fontsize',12,'FontName','Arial'); +hx=get(gca,'XLabel'); hy=get(gca,'YLabel'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +close all; clear all; +[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ... +getPaperDataDirs(); +delta = 0.001; Tmax = 1; +time = 0:delta:Tmax; +numRealizations = 20; +f=2; b1=randn(numRealizations,1);b0=log(10*delta)+randn(numRealizations,1); +x = sin(2*pi*f*time); +clear nst; +for i=1:numRealizations +expData = exp(b1(i)*x+b0(i)); +lambdaData = expData./(1+expData); +if(i==1) +lambda = Covariate(time,lambdaData./delta, ... +'\Lambda(t)','time','s','spikes/sec',{'\lambda_{1}'},... +{{' ''b'', ''LineWidth'' ,2'}}); +else +tempLambda = Covariate(time,lambdaData./delta, ... +'\Lambda(t)','time','s','spikes/sec',{'\lambda_{1}'},... +{{' ''b'', ''LineWidth'' ,2'}}); +lambda = lambda.merge(tempLambda); +end +spikeColl = CIF.simulateCIFByThinningFromLambda(... +lambda.getSubSignal(i),1); +nst{i} = spikeColl.getNST(1); +end +spikeColl = nstColl(nst);scrsz = get(0,'ScreenSize'); +h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 ... +scrsz(3)*.6 scrsz(4)*.8]); +subplot(3,1,1); plot(time,x,'k'); +set(gca,'xtick',[],'xtickLabel',[]); ylabel('Stimulus'); +hx=get(gca,'XLabel'); hy=get(gca,'YLabel'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title('Driving Stimulus','FontWeight','bold',... +'FontSize',14,'FontName','Arial'); +subplot(3,1,2); lambda.plot([],{{' ''k'',''Linewidth'',1'}}); +legend off; +hy=ylabel('Firing Rate [spikes/sec]', 'Interpreter','none'); +hx=xlabel('','Interpreter','none'); +set([hx, hy],'FontName', 'Arial','FontSize',12,... +'FontWeight','bold'); +set(gca,'xtickLabel',[]); +title('Conditional Intensity Functions','FontWeight',... +'bold','FontSize',14,'FontName','Arial'); +subplot(3,1,3); spikeColl.plot; +set(gca,'ytick',0:10:numRealizations,'ytickLabel',... +0:10:numRealizations); +xlabel('time [s]','Interpreter','none'); +ylabel('Cell Number','Interpreter','none'); +hx=get(gca,'XLabel'); hy=get(gca,'YLabel'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title('Point Process Sample Paths','FontWeight',... +'bold','FontSize',14,'FontName','Arial'); +stim = Covariate(time,sin(2*pi*f*time),'Stimulus','time','s','V',{'stim'}); +baseline = Covariate(time,ones(length(time),1),'Baseline','time','s','',... +{'constant'}); +close all; +clear lambdaCIF; +spikeColl.resample(1/delta); +dN=spikeColl.dataToMatrix; +Q=std(stim.data(2:end)-stim.data(1:end-1)); +Px0=.1; A=1; +x0 = x(:,1); yT=x(:,end); +Pi0 = eps*eye(size(x0,1),size(x0,1)); +PiT = eps*eye(size(x0,1),size(x0,1)); +[x_p, W_p, x_u, W_u] = DecodingAlgorithms.PPDecodeFilterLinear(A, ... +Q, dN',b0,b1','binomial',delta); +h=figure('Position',[scrsz(3)*.1 scrsz(4)*.1 scrsz(3)*.8 scrsz(4)*.6]); +zVal=1.96; +ciLower = min(x_u(1:end)-zVal*sqrt(squeeze(W_u(1:end)))',... +x_u(1:end)+zVal*sqrt(squeeze(W_u(1:end))')); +ciUpper = max(x_u(1:end)-zVal*sqrt(squeeze(W_u(1:end)))',... +x_u(1:end)+zVal*sqrt(squeeze(W_u(1:end))')); +estimatedStimulus = Covariate(time,x_u(1:end),'\hat{x}(t)','time','s',''); +CI= ConfidenceInterval(time,[ciLower', ciUpper'],'\hat{x}(t)','time','s',''); +estimatedStimulus.setConfInterval(CI); +hEst = estimatedStimulus.plot([],{{' ''k'',''Linewidth'',4'}}); +hStim=stim.plot([],{{' ''b'',''Linewidth'',4'}}); +legend off; +h_legend=legend([hEst(1) hStim],'Decoded','Actual'); +set(h_legend,'Interpreter','none'); +set(h_legend,'FontSize',22); +title(['Decoded Stimulus +/- 95% CIs with ' num2str(numRealizations) ' cells'],... +'FontWeight','bold','Fontsize',22,'FontName','Arial'); +xlabel('time [s]','Interpreter','none'); +ylabel('Stimulus','Interpreter','none'); +hx=get(gca,'XLabel'); hy=get(gca,'YLabel'); +set([hx, hy],'FontName', 'Arial','FontSize',22,'FontWeight','bold'); +close all; +clear all; +[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ... +getPaperDataDirs(); +q=1e-4; +Q=diag([1e-12 1e-12 q q]); +delta = .001; % Time increment +r=1e-6; % in meters^2 +p=1e-6; % in meters^2/s^2 +PiT=diag([r r p p]); % Uncertainty in the target state +Pi0=PiT; +T=2; % Reach Duration +x0 = [0;0;0;0]; % Initial Position and velocities (states) +xT = [-.35;.2; 0;0];% Final Target +time=0:delta:T; % time vector +A=[1 0 delta 0 ; %State transition matrix +0 1 0 delta; +0 0 1 0 ; +0 0 0 1 ]; +x=zeros(4,length(time)); +R=chol(Q); +L=chol(PiT); +for k=1:length(time) +if(k==1) +x(:,k)=x0; +else +x(:,k)=A*x(:,k-1)+... +delta/(2)*(pi/T)^2*cos(pi*time(k)/T)*[0;0;... +xT(1)-x0(1);xT(2)-x0(2)]; %Reach to target model +end +end +xT =x(:,end); % The target generated by the model +yT=xT; % Assume we have observed the actual target position with uncertainty PiT +Q=diag(var(diff(x,[],2),[],2))*100; +scrsz = get(0,'ScreenSize'); +fig1=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 ... +scrsz(3)*.8 scrsz(4)*.8]); +subplot(4,2,[1 3]); +plot(100*x(1,:),100*x(2,:),'k','Linewidth',2); +xlabel('X Position [cm]'); ylabel('Y Position [cm]'); +hx=get(gca,'XLabel'); hy=get(gca,'YLabel'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +title('Reach Path','FontWeight','bold','Fontsize',14,'FontName','Arial'); +hold on; +axis([sort([100*x0(1)+5, 100*xT(1)-5]), sort([100*x0(2)-5, 100*xT(2)+5])]); +h1=plot(100*x(1,1),100*x(2,1),'bo','MarkerSize',14); +h2=plot(100*x(1,end),100*x(2,end),'ro','MarkerSize',14); +legend([h1 h2],'Start','Finish','Location','NorthEast'); +subplot(4,2,5); h1=plot(time,100*x(1,:),'k','Linewidth',2); hold on; +h2=plot(time,100*x(2,:),'k-.','Linewidth',2); +h_legend=legend([h1,h2],'x','y','Location','NorthEast'); +set(h_legend,'FontSize',14) +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)+.06 pos(2)+.01 pos(3:4)]); +hx=xlabel('time [s]'); hy=ylabel('Position [cm]'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +subplot(4,2,7); +h1=plot(time,100*x(3,:),'k','Linewidth',2); hold on; +h2=plot(time,100*x(4,:),'k-.','Linewidth',2); +h_legend=legend([h1 h2],'v_x','v_y','Location','NorthEast'); +xlabel('time [s]'); +set(h_legend,'FontSize',14); +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)+.06 pos(2)+.01 pos(3:4)]); +hx=xlabel('time [s]'); hy=ylabel('Velocity [cm/s]'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +gamma=0; +windowTimes=[0, 0.001]; +numCells = 20; +bCoeffs=10*(rand(numCells,2)-.5); % b_i = [b_x_i b_y_i] ~ U(-5, 5); +phiMax = atan2(bCoeffs(:,2),bCoeffs(:,1)); % Maximal firing direction of cell +phiMaxNorm = (phiMax+pi)./(2*pi); +meanMu = log(10*delta); % baseline firing rate -10Hz +MuCoeffs = meanMu+randn(numCells,1); % mu_i ~ G(meanMu,1) +dataMat = [ones(length(time),1) x(3,:)' x(4,:)']; % design matrix: X ( +coeffs = [MuCoeffs bCoeffs]; % coefficient vector: beta +fitType='binomial'; +clear nst; +for i=1:numCells +tempData = exp(dataMat*coeffs(i,:)'); +if(strcmp(fitType,'poisson')) +lambdaData = tempData; +else +lambdaData = tempData./(1+tempData); % Conditional Intensity Function for ith cell +end +lambda{i}=Covariate(time,lambdaData./delta, ... +'\Lambda(t)','time','s','spikes/sec',... +{strcat('\lambda_{',num2str(i),'}')},{{' ''b'' '}}); +lambda{i}=lambda{i}.resample(1/delta); +lambdaCIF{i} = CIF([MuCoeffs(i) 0 0 bCoeffs(i,:)],... +{'1','x','y','vx','vy'},{'x','y','vx','vy'},fitType); +tempSpikeColl{i} = CIF.simulateCIFByThinningFromLambda(lambda{i},1); nst{i} = tempSpikeColl{i}.getNST(1); % grab the realization +nst{i}.setName(num2str(i)); % give each cell a unique name +subplot(4,2,[6 8]); +h2=lambda{i}.plot([],{{' ''k'', ''LineWidth'' ,.5'}}); +legend off; hold all; % Plot the CIF +end +title('Neural Conditional Intensity Functions','FontWeight',... +'bold','Fontsize',14,'FontName','Arial'); +hx=xlabel('time [s]','Interpreter','none'); +hy=ylabel('Firing Rate [spikes/sec]','Interpreter','none'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +spikeColl = nstColl(nst); % Create a neural spike train collection +subplot(4,2,[2,4]); spikeColl.plot; +set(gca,'xtick',[],'xtickLabel',[]); +title('Neural Raster','FontWeight','bold','Fontsize',14,... +'FontName','Arial'); +hx=xlabel('time [s]','Interpreter','none'); +hy=ylabel('Cell Number','Interpreter','none'); +set([hx, hy],'FontName', 'Arial','FontSize',12,'FontWeight','bold'); +close all; +numExamples=20; +scrsz = get(0,'ScreenSize'); +fig1=figure('OuterPosition',[scrsz(3)*.1 scrsz(4)*.1 ... +scrsz(3)*.6 scrsz(4)*.9]); +for k=1:numExamples +bCoeffs=10*(rand(numCells,2)-.5); % b_i = [b_x_i b_y_i] ~ U(-5, 5); +phiMax = atan2(bCoeffs(:,2),bCoeffs(:,1)); % Maximal firing direction of cell +phiMaxNorm = (phiMax+pi)./(2*pi); +meanMu = log(10*delta); % baseline firing rate +MuCoeffs = meanMu+randn(numCells,1); % mu_i ~ G(meanMu,1) +dataMat = [ones(length(time),1) x(3,:)' x(4,:)']; % design matrix: X ( +coeffs = [MuCoeffs bCoeffs]; % coefficient vector: beta +fitType='binomial'; +clear nst lambda; +for i=1:numCells +tempData = exp(dataMat*coeffs(i,:)'); +if(strcmp(fitType,'poisson')) +lambdaData = tempData; +else +lambdaData = tempData./(1+tempData); +end +lambda{i}=Covariate(time,lambdaData./delta, ... +'\Lambda(t)','time','s','spikes/sec',... +{strcat('\lambda_{',num2str(i),'}')},{{' ''b'' '}}); +lambda{i}=lambda{i}.resample(1/delta); +tempSpikeColl{i} = CIF.simulateCIFByThinningFromLambda(lambda{i},1); +nst{i} = tempSpikeColl{i}.getNST(1); % grab the realization +nst{i}.setName(num2str(i)); % give each cell a unique name +end +spikeColl = nstColl(nst); % Create a neural spike train collection +dN=spikeColl.dataToMatrix'; +dN(dN>1)=1; % more than one spike per bin will be treated as one spike. In +[C,N] = size(dN); % N time samples, C cells +beta=[zeros(2,numCells); bCoeffs']; +[x_p, W_p, x_u, W_u,x_uT,W_uT,x_pT,W_pT] = ... +DecodingAlgorithms.PPDecodeFilterLinear(A, Q, dN,... +MuCoeffs,beta,fitType,delta,gamma,windowTimes,x0, Pi0, yT,PiT,0); +[x_pf, W_pf, x_uf, W_uf] = ... +DecodingAlgorithms.PPDecodeFilterLinear(A, Q, dN,... +MuCoeffs,beta,fitType,delta,gamma,windowTimes,x0); +if(k==numExamples) +subplot(4,2,1:4);h1=plot(100*x(1,:),100*x(2,:),'k','LineWidth',3); +hold on; +axis([sort([100*x0(1)+5, 100*xT(1)-5]), ... +sort([100*x0(2)-5, 100*xT(2)+5])]); +title('Estimated vs. Actual Reach Paths',... +'FontWeight','bold','Fontsize',12,'FontName','Arial'); +end +subplot(4,2,1:4);h2=plot(100*x_u(1,:)',100*x_u(2,:)','b'); hold all; +subplot(4,2,1:4);h3=plot(100*x_uf(1,:)',100*x_uf(2,:)','g'); +hx=xlabel('x [cm]'); hy=ylabel('y [cm]'); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +h1=plot(100*x0(1),100*x0(2),'bo','MarkerSize',10); hold on; +h2=plot(100*xT(1),100*xT(2),'ro','MarkerSize',10); +legend([h1 h2],'Start','Finish','Location','NorthEast'); +subplot(4,2,5); +h1=plot(time,100*x(1,:),'k','LineWidth',3); hold on; +h2=plot(time,100*x_u(1,:)','b'); +h3=plot(time,100*x_uf(1,:)','g'); +hy=ylabel('x(t) [cm]'); hx=xlabel('time [s]'); +set(gca,'xtick',[],'xtickLabel',[]); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +title('X Position','FontWeight','bold','Fontsize',12,'FontName','Arial'); +subplot(4,2,6); +h1=plot(time,100*x(2,:),'k','LineWidth',3); hold on; +h2=plot(time,100*x_u(2,:)','b'); +h3=plot(time,100*x_uf(2,:)','g'); +h_legend=legend([h1(1) h2(1) h3(1)],'Actual','PPAF+Goal',... +'PPAF','Location','SouthEast'); +hy=ylabel('y(t) [cm]'); hx=xlabel('time [s]'); +set(gca,'xtick',[],'xtickLabel',[]); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +title('Y Position','FontWeight','bold','Fontsize',12,'FontName','Arial'); +set(h_legend,'FontSize',10) +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)-.63 pos(2)+.23 pos(3:4)]); +subplot(4,2,7); +h1=plot(time,100*x(3,:),'k','LineWidth',3); hold on; +h2=plot(time,100*x_u(3,:)','b'); +h3=plot(time,100*x_uf(3,:)','g'); +hy=ylabel('v_{x}(t) [cm/s]'); hx=xlabel('time [s]'); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +title('X Velocity','FontWeight','bold','Fontsize',12,'FontName','Arial'); +subplot(4,2,8); +h1=plot(time,100*x(4,:),'k','LineWidth',3); hold on; +h2=plot(time,100*x_u(4,:)','b'); +h3=plot(time,100*x_uf(4,:)','g'); +hy=ylabel('v_{y}(t) [cm/s]'); hx=xlabel('time [s]'); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +title('Y Velocity','FontWeight','bold','Fontsize',12,'FontName','Arial'); +end +clear all; +[dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ... +getPaperDataDirs(); +close all; +delta=0.001; +Tmax=2; +time=0:delta:Tmax; +A{2} = [1 0 delta 0 delta^2/2 0; +0 1 0 delta 0 delta^2/2; +0 0 1 0 delta 0; +0 0 0 1 0 delta; +0 0 0 0 1 0; +0 0 0 0 0 1]; +A{1} = [1 0 0 0 0 0; +0 1 0 0 0 0; +0 0 0 0 0 0; +0 0 0 0 0 0; +0 0 0 0 0 0; +0 0 0 0 0 0]; +A{1} = [1 0; +0 1]; +Px0{2} =1e-6*eye(6,6); +Px0{1} =1e-6*eye(2,2); +minCovVal = 1e-12; +covVal = 1e-3; +Q{2}=[minCovVal 0 0 0 0 0; +0 minCovVal 0 0 0 0; +0 0 minCovVal 0 0 0; +0 0 0 minCovVal 0 0; +0 0 0 0 covVal 0; +0 0 0 0 0 covVal]; +Q{1}=minCovVal*eye(2,2); +mstate = zeros(1,length(time)); +ind{1}=1:2; +ind{2}=1:6; +X=zeros(max([size(A{1},1),size(A{2},1)]),length(time)); +p_ij = [.998 .002; +.001 .999]; +for i = 1:length(time) +if(i==1) +mstate(i) = 1; +else +if(rand(1,1)1)=1; %Avoid more than 1 spike per bin. +Mu0=.5*ones(size(p_ij,1),1); +clear x0 yT clear Pi0 PiT; +x0{1} = X(ind{1},1); +yT{1} = X(ind{1},end); +Pi0 = Px0; +PiT{1} = 1e-9*eye(size(x0{1},1),size(x0{1},1)); +x0{2} = X(ind{2},1); +yT{2} = X(ind{2},end); +PiT{2} = 1e-9*eye(size(x0{2},1),size(x0{2},1)); +[S_est, X_est, W_est, MU_est, X_s, W_s,pNGivenS]=... +DecodingAlgorithms.PPHybridFilterLinear(A, Q, p_ij,Mu0, dN',... +coeffs(:,1),coeffs(:,2:end)',fitType,delta,[],[],x0,Pi0, yT,PiT); +[S_estNT, X_estNT, W_estNT, MU_estNT, X_sNT, W_sNT,pNGivenSNT]=... +DecodingAlgorithms.PPHybridFilterLinear(A, Q, p_ij,Mu0, dN',... +coeffs(:,1),coeffs(:,2:end)',fitType,delta,[],[],x0,Pi0); +X_estAll(:,:,n) = X_est; +X_estNTAll(:,:,n) = X_estNT; +S_estAll(n,:)=S_est; +S_estNTAll(n,:)=S_estNT; +MU_estAll(:,:,n)=MU_est; +MU_estNTAll(:,:,n) = MU_estNT; +subplot(4,3,[1 4]); +plot(time,mstate,'k','LineWidth',3); hold all; +plot(time,S_est,'b-.','Linewidth',.5); +plot(time,S_estNT,'g-.','Linewidth',.5); axis tight; v=axis; +axis([v(1) v(2) 0.5 2.5]); +subplot(4,3,[7 10]); +plot(time,MU_est(2,:),'b-.','Linewidth',.5); hold on; +plot(time,MU_estNT(2,:),'g-.','Linewidth',.5); hold on; +axis([min(time) max(time) 0 1.1]); +subplot(4,3,[2 3 5 6]); +h1=plot(100*X(1,:)',100*X(2,:)','k'); hold all; +h2=plot(100*X_est(1,:)',100*X_est(2,:)','b-.'); hold all; +h3=plot(100*X_estNT(1,:)',100*X_estNT(2,:)','g-.'); +subplot(4,3,8); +h1=plot(time,100*X(1,:),'k','LineWidth',3); hold on; +h2=plot(time,100*X_est(1,:)','b-.'); +h3=plot(time,100*X_estNT(1,:)','g-.'); +subplot(4,3,9); +h1=plot(time,100*X(2,:),'k','LineWidth',3); hold on; +h2=plot(time,100*X_est(2,:)','b-.'); +h3=plot(time,100*X_estNT(2,:)','g-.'); +subplot(4,3,11); +h1=plot(time,100*X(3,:),'k','LineWidth',3); hold on; +h2=plot(time,100*X_est(3,:)','b-.'); +h3=plot(time,100*X_estNT(3,:)','g-.'); +subplot(4,3,12); +h1=plot(time,100*X(4,:),'k','LineWidth',3); hold on; +h2=plot(time,100*X_est(4,:)','b-.'); +h3=plot(time,100*X_estNT(4,:)','g-.'); +end +subplot(4,3,[1 4]); +hold all; +plot(time,mstate,'k','LineWidth',3); +plot(time,mean(S_estAll),'b','LineWidth',3); +plot(time,mean(S_estNTAll),'g','LineWidth',3); +set(gca,'xtick',[],'YTick',[1 2.1],'YTickLabel',{'N','M'}); +hy=ylabel('state'); hx=xlabel('time [s]'); +set([hy hx],'FontName', 'Arial','FontSize',10,'FontWeight','bold',... +'Interpreter','none'); +title('Estimated vs. Actual State','FontWeight','bold','Fontsize',... +12,'FontName','Arial'); +subplot(4,3,[7 10]); +plot(time, mean(squeeze(MU_estAll(2,:,:)),2),'b','LineWidth',3); +hold on; +plot(time,mean(squeeze(MU_estNTAll(2,:,:)),2),'g','LineWidth',3); +hold on; +axis([min(time) max(time) 0 1.1]); +hx=xlabel('time [s]'); hy=ylabel('P(s(t)=M | data)'); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +title('Probability of State','FontWeight','bold','Fontsize',12,... +'FontName','Arial'); +subplot(4,3,[2 3 5 6]); +h1=plot(100*X(1,:)',100*X(2,:)','k'); hold all; +mXestAll=mean(100*X_estAll,3); +mXestNTAll=mean(100*X_estNTAll,3); +plot(mXestAll(1,:),mXestAll(2,:),'b','Linewidth',3); +plot(mXestNTAll(1,:),mXestNTAll(2,:),'g','Linewidth',3); +hx=xlabel('x [cm]'); hy=ylabel('y [cm]'); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +h1=plot(100*X(1,1),100*X(2,1),'bo','MarkerSize',14); hold on; +h2=plot(100*X(1,end),100*X(2,end),'ro','MarkerSize',14); +legend([h1 h2],'Start','Finish','Location','NorthEast'); +title('Estimated vs. Actual Reach Path','FontWeight','bold',... +'Fontsize',12,'FontName','Arial'); +subplot(4,3,8); +h1=plot(time,100*X(1,:),'k','LineWidth',3); hold on; +h2=plot(time,mXestAll(1,:),'b','LineWidth',3); hold on; +h3=plot(time,mXestNTAll(1,:),'g','LineWidth',3); hold on; +hy=ylabel('x(t) [cm]'); hx=xlabel('time [s]'); +set(gca,'xtick',[],'xtickLabel',[]); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +title('X Position','FontWeight','bold','Fontsize',12,'FontName','Arial'); +subplot(4,3,9); +h1=plot(time,100*X(2,:),'k','LineWidth',3); hold on; +h2=plot(time,mXestAll(2,:),'b','LineWidth',3); hold on; +h3=plot(time,mXestNTAll(2,:),'g','LineWidth',3); hold on; +h_legend=legend([h1(1) h2(1) h3(1)],'Actual','PPAF+Goal',... +'PPAF','Location','SouthEast'); +hy=ylabel('y(t) [cm]'); hx=xlabel('time [s]'); +set(gca,'xtick',[],'xtickLabel',[]); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +title('Y Position','FontWeight','bold','Fontsize',12,'FontName','Arial'); +set(h_legend,'FontSize',10) +pos = get(h_legend,'position'); +set(h_legend, 'position',[pos(1)-.40 pos(2)+.51 pos(3:4)]); +subplot(4,3,11); +h1=plot(time,100*X(3,:),'k','LineWidth',3); hold on; +h2=plot(time,mXestAll(3,:),'b','LineWidth',3); hold on; +h3=plot(time,mXestNTAll(3,:),'g','LineWidth',3); hold on; +hy=ylabel('v_{x}(t) [cm/s]'); hx=xlabel('time [s]'); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +title('X Velocity','FontWeight','bold','Fontsize',12,'FontName','Arial'); +subplot(4,3,12); +h1=plot(time,100*X(4,:),'k','LineWidth',3); hold on; +h2=plot(time,mXestAll(4,:),'b','LineWidth',3); hold on; +h3=plot(time,mXestNTAll(4,:),'g','LineWidth',3); hold on; +hy=ylabel('v_{y}(t) [cm/s]'); hx=xlabel('time [s]'); +set([hx, hy],'FontName', 'Arial','FontSize',10,'FontWeight','bold'); +title('Y Velocity','FontWeight','bold','Fontsize',12,'FontName','Arial'); +parity = struct(); +if exist('numCells','var') +parity.num_cells = numCells; +end +if exist('numRealizations','var') +parity.num_realizations = numRealizations; +end +function [dataDir,mEPSCDir,explicitStimulusDir,psthDir,placeCellDataDir] = ... +getPaperDataDirs() +candidateRoots = {}; +scriptPath = mfilename('fullpath'); +if ~isempty(scriptPath) +candidateRoots = appendCandidateRoot(candidateRoots, fileparts(fileparts(scriptPath))); +end +paperPath = which('nSTATPaperExamples'); +if ~isempty(paperPath) +candidateRoots = appendCandidateRoot(candidateRoots, fileparts(fileparts(paperPath))); +end +installPath = which('nSTAT_Install'); +if ~isempty(installPath) +candidateRoots = appendCandidateRoot(candidateRoots, fileparts(installPath)); +end +try +activeFile = matlab.desktop.editor.getActiveFilename; +catch +activeFile = ''; +end +if ~isempty(activeFile) +candidateRoots = appendCandidateRoot(candidateRoots, fileparts(fileparts(activeFile))); +end +candidateRoots = appendCandidateRoot(candidateRoots, pwd); +nSTATDir = ''; +for iRoot = 1:numel(candidateRoots) +candidateDataDir = fullfile(candidateRoots{iRoot}, 'data'); +if exist(candidateDataDir, 'dir') == 7 +nSTATDir = candidateRoots{iRoot}; +break; +end +end +if isempty(nSTATDir) +error('nSTATPaperExamples:MissingInstallPath', ... +['Could not resolve the nSTAT root path. Checked roots derived from ', ... +'mfilename, which(''nSTATPaperExamples''), which(''nSTAT_Install''), ', ... +'the active editor file, and pwd.']); +end +dataDir = fullfile(nSTATDir,'data'); +mEPSCDir = fullfile(dataDir,'mEPSCs'); +explicitStimulusDir = fullfile(dataDir,'Explicit Stimulus'); +psthDir = fullfile(dataDir,'PSTH'); +placeCellDataDir = fullfile(dataDir,'Place Cells'); +if exist(dataDir,'dir') ~= 7 +error('nSTATPaperExamples:MissingDataDir', ... +'Could not find local nSTAT data folder at %s', dataDir); +end +end +function roots = appendCandidateRoot(roots, startDir) +if isempty(startDir) +return; +end +thisDir = startDir; +while true +if ~any(strcmp(roots, thisDir)) +roots{end+1} = thisDir; %#ok +end +parentDir = fileparts(thisDir); +if strcmp(parentDir, thisDir) +break; +end +thisDir = parentDir; +end +end diff --git a/parity/line_port_snapshots/publish_all_helpfiles.txt b/parity/line_port_snapshots/publish_all_helpfiles.txt new file mode 100644 index 00000000..7301ff5a --- /dev/null +++ b/parity/line_port_snapshots/publish_all_helpfiles.txt @@ -0,0 +1,126 @@ +function publish_all_helpfiles(varargin) +opts = parseOptions(varargin{:}); +helpDir = fileparts(mfilename('fullpath')); +rootDir = fileparts(helpDir); +stagingDir = tempname; +outputDir = tempname; +mkdir(stagingDir); +mkdir(outputDir); +cleanupObj = onCleanup(@()cleanupTempDirs(stagingDir, outputDir)); +startDir = pwd; +restoreDir = onCleanup(@()cd(startDir)); %#ok +copyfile(fullfile(helpDir, '*'), stagingDir); +removeStagedArtifacts(stagingDir); +restoredefaultpath; +addpath(rootDir, '-begin'); +nSTAT_Install('RebuildDocSearch', false, 'CleanUserPathPrefs', false); +addpath(stagingDir, '-begin'); +cd(stagingDir); +publishOptions = struct('outputDir', outputDir, 'format', 'html', 'evalCode', opts.EvalCode); +referencePublishOptions = struct('outputDir', outputDir, 'format', 'html', 'evalCode', false); +failures = {}; +stageFiles = dir(fullfile(stagingDir, '*.m')); +for iFile = 1:numel(stageFiles) +[~, baseName] = fileparts(stageFiles(iFile).name); +if strcmpi(baseName, 'publish_all_helpfiles') +continue; +end +try +publish(baseName, publishOptions); +fprintf('Published help topic: %s\n', stageFiles(iFile).name); +catch ME +failures{end+1} = sprintf('%s :: %s', stageFiles(iFile).name, ME.message); %#ok +end +end +rootReferenceFiles = {'Analysis.m', 'SignalObj.m', 'FitResult.m'}; +for iFile = 1:numel(rootReferenceFiles) +sourceFile = fullfile(rootDir, rootReferenceFiles{iFile}); +try +publish(sourceFile, referencePublishOptions); +fprintf('Published class reference: %s\n', rootReferenceFiles{iFile}); +catch ME +failures{end+1} = sprintf('%s :: %s', rootReferenceFiles{iFile}, ME.message); %#ok +end +end +if ~isempty(failures) +fprintf(2, 'Publish failures (%d):\n', numel(failures)); +for i = 1:numel(failures) +fprintf(2, ' - %s\n', failures{i}); +end +error('nSTAT:PublishAllFailures', 'One or more help pages failed to publish.'); +end +copyfile(fullfile(outputDir, '*'), helpDir, 'f'); +builddocsearchdb(helpDir); +rehash toolboxcache; +validateHelpTargets(helpDir); +validateHtmlGeneratorMetadata(helpDir, opts.ExpectedGenerator); +fprintf('nSTAT help publication completed successfully.\n'); +clear cleanupObj; +end +function opts = parseOptions(varargin) +parser = inputParser; +parser.FunctionName = 'publish_all_helpfiles'; +addParameter(parser, 'EvalCode', true, @(x)islogical(x) || isnumeric(x)); +addParameter(parser, 'ExpectedGenerator', 'MATLAB 25.2', @(x)ischar(x) || isstring(x)); +parse(parser, varargin{:}); +opts.EvalCode = logical(parser.Results.EvalCode); +opts.ExpectedGenerator = char(parser.Results.ExpectedGenerator); +end +function removeStagedArtifacts(stagingDir) +removePattern(stagingDir, '*.mlx'); +removePattern(stagingDir, '*.asv'); +removePattern(stagingDir, '*.bak'); +removePattern(stagingDir, 'temp.m'); +removePattern(stagingDir, 'publish_all_helpfiles.m'); +end +function removePattern(stagingDir, pattern) +files = dir(fullfile(stagingDir, pattern)); +for i = 1:numel(files) +delete(fullfile(stagingDir, files(i).name)); +end +end +function validateHelpTargets(helpDir) +helptocPath = fullfile(helpDir, 'helptoc.xml'); +if ~isfile(helptocPath) +error('nSTAT:MissingHelptoc', 'Missing helptoc.xml at %s', helptocPath); +end +raw = fileread(helptocPath); +matches = regexp(raw, 'target="([^"]+)"', 'tokens'); +for i = 1:numel(matches) +target = matches{i}{1}; +if startsWith(target, 'http://') || startsWith(target, 'https://') +continue; +end +fullTarget = fullfile(helpDir, target); +if ~isfile(fullTarget) +error('nSTAT:MissingHelpTarget', ... +'helptoc target is missing after publish: %s', fullTarget); +end +end +end +function validateHtmlGeneratorMetadata(helpDir, expectedGenerator) +htmlFiles = dir(fullfile(helpDir, '*.html')); +for i = 1:numel(htmlFiles) +htmlPath = fullfile(helpDir, htmlFiles(i).name); +raw = fileread(htmlPath); +if isempty(regexp(raw, [' None: example_summary = payload["example_line_alignment_audit"]["summary"] assert example_summary["total_topics"] >= 1 + assert "strict_line_verified_topics" in example_summary + assert "strict_line_partial_topics" in example_summary + assert "strict_line_gap_topics" in example_summary + topic_rows = payload["example_line_alignment_audit"]["topic_rows"] + assert topic_rows, "example_line_alignment_audit.topic_rows must not be empty" + required_topic_fields = { + "topic", + "strict_line_status", + "line_port_coverage", + "line_port_function_recall", + "line_port_matched_lines", + "line_port_matlab_lines", + "line_port_python_lines", + "line_port_matlab_function_count", + "line_port_python_function_count", + } + for row in topic_rows: + missing = required_topic_fields.difference(row) + assert not missing, f"Missing strict line-port fields for topic {row.get('topic')}: {sorted(missing)}" + + +def test_top_mismatch_topics_meet_line_port_regression_thresholds() -> None: + report_path = Path("parity/function_example_alignment_report.json") + payload = json.loads(report_path.read_text(encoding="utf-8")) + topic_rows = payload["example_line_alignment_audit"]["topic_rows"] + topic_lookup = {str(row["topic"]): row for row in topic_rows} + + thresholds = { + "nSTATPaperExamples": (0.45, 0.95), + "HippocampalPlaceCellExample": (0.35, 0.95), + "publish_all_helpfiles": (0.90, 0.95), + } + allowed_statuses = {"line_port_partial", "line_port_verified"} + + for topic, (min_cov, min_recall) in thresholds.items(): + assert topic in topic_lookup, f"Missing topic row for {topic}" + row = topic_lookup[topic] + coverage = float(row["line_port_coverage"]) + recall = float(row["line_port_function_recall"]) + status = str(row["strict_line_status"]) + assert coverage >= min_cov, f"{topic}: coverage {coverage:.4f} < {min_cov:.4f}" + assert recall >= min_recall, f"{topic}: function recall {recall:.4f} < {min_recall:.4f}" + assert status in allowed_statuses, f"{topic}: strict status {status} not in {sorted(allowed_statuses)}" diff --git a/tests/test_parity_matlab_gold.py b/tests/test_parity_matlab_gold.py index c967fbc3..20c2ad7f 100644 --- a/tests/test_parity_matlab_gold.py +++ b/tests/test_parity_matlab_gold.py @@ -265,6 +265,61 @@ def test_analysis_examples_matlab_gold_comparison() -> None: assert np.isclose(rmse, _scalar(m, "expected_rmse_analysis"), atol=0.25) +def test_nstatpaperexamples_plot_arrays_matlab_gold_comparison() -> None: + combo = _mat("tests/parity/fixtures/matlab_gold/nSTATPaperExamples_plot_gold.mat") + + # Stimulus-rate plot arrays (PPSim section). + ppsim = _mat("tests/parity/fixtures/matlab_gold/PPSimExample_gold.mat") + X = np.asarray(ppsim["X"], dtype=float) + y = _vec(ppsim, "y") + dt = _scalar(ppsim, "dt") + fit = Analysis.fit_glm(X=X, y=y, fit_type="poisson", dt=dt) + pred_rate = np.asarray(fit.predict(X), dtype=float).reshape(-1) + expected_rate = np.asarray(combo["expected_rate_pp"], dtype=float).reshape(-1) + rel_err = np.mean(np.abs(pred_rate - expected_rate) / np.maximum(expected_rate, 1e-12)) + assert rel_err <= 0.25 + + # Decode-with-history plot arrays. + dec = _mat("tests/parity/fixtures/matlab_gold/DecodingExampleWithHist_gold.mat") + decoded, posterior = DecodingAlgorithms.decode_state_posterior( + spike_counts=np.asarray(dec["spike_counts"], dtype=float), + tuning_rates=np.asarray(dec["tuning"], dtype=float), + transition=np.asarray(dec["transition"], dtype=float), + ) + expected_decoded = np.asarray(combo["expected_decoded_hist"], dtype=int).reshape(-1) + expected_post = np.asarray(combo["expected_posterior_hist"], dtype=float) + assert np.array_equal(decoded, expected_decoded) + assert np.allclose(posterior, expected_post, atol=1e-8) + + # Place-cell weighted decode arrays. + place = _mat("tests/parity/fixtures/matlab_gold/HippocampalPlaceCellExample_gold.mat") + decoded_weighted = DecodingAlgorithms.decode_weighted_center( + spike_counts=np.asarray(place["spike_counts_pc"], dtype=float), + tuning_curves=np.asarray(place["tuning_curves"], dtype=float), + ) + expected_weighted = np.asarray(combo["expected_weighted_decode"], dtype=float).reshape(-1) + assert np.allclose(decoded_weighted, expected_weighted, atol=1e-8) + + # PSTH significance-matrix arrays. + psth = _mat("tests/parity/fixtures/matlab_gold/PSTHEstimation_gold.mat") + rate, prob, sig = DecodingAlgorithms.compute_spike_rate_cis( + spike_matrix=np.asarray(psth["spike_matrix_psth"], dtype=float), + alpha=_scalar(psth, "alpha_psth"), + ) + assert np.allclose(rate, np.asarray(combo["expected_psth_rate"], dtype=float).reshape(-1), atol=1e-10) + assert np.allclose(prob, np.asarray(combo["expected_psth_prob"], dtype=float), atol=1e-10) + assert np.array_equal(sig, np.asarray(combo["expected_psth_sig"], dtype=int)) + + # mEPSC trace arrays used for data-plot panels. + trace = np.asarray(combo["trace_mepsc"], dtype=float).reshape(-1) + time = np.asarray(combo["time_mepsc"], dtype=float).reshape(-1) + event_times = np.asarray(combo["event_times_mepsc"], dtype=float).reshape(-1) + assert trace.size == time.size + assert trace.size > 1000 + assert event_times.size >= 40 + assert np.all(np.diff(time) > 0.0) + + def test_decoding_example_matlab_gold_comparison() -> None: m = _mat("tests/parity/fixtures/matlab_gold/DecodingExample_gold.mat") spike_counts = np.asarray(m["spike_counts_dec"], dtype=float) diff --git a/tools/notebooks/generate_notebooks.py b/tools/notebooks/generate_notebooks.py index 59d3398e..0370896e 100755 --- a/tools/notebooks/generate_notebooks.py +++ b/tools/notebooks/generate_notebooks.py @@ -4,6 +4,7 @@ from __future__ import annotations import argparse +import json import re from pathlib import Path @@ -14,6 +15,7 @@ PAPER_DOI = "10.1016/j.jneumeth.2012.08.009" PAPER_PMID = "22981419" REPO_NOTEBOOK_BASE = "https://github.com/cajigaslab/nSTAT-python/blob/main/notebooks" +LINE_PORT_SNAPSHOT_DIR = Path("parity/line_port_snapshots") DECODING_1D_TOPICS = { "DecodingExample", @@ -1329,7 +1331,7 @@ def _plot_events(color: str, title_suffix: str) -> None: "best_bic_diff": float(np.min(diff_bic)), } CHECKPOINT_LIMITS = { - "num_models": (2.0, 2.0), + "num_models": (3.0, 3.0), "best_aic_diff": (-10.0, 10.0), "best_bic_diff": (-10.0, 10.0), } @@ -1454,146 +1456,734 @@ def target_exists(target: str) -> bool: """ -PUBLISH_ALL_HELPFILES_TEMPLATE = """# publish_all_helpfiles: Python-side publish/audit checks for help artifacts. +PUBLISH_ALL_HELPFILES_TEMPLATE = """# publish_all_helpfiles: MATLAB-ordered publish pipeline audit. +import json +import shutil +import subprocess +import sys +import tempfile from pathlib import Path + import yaml + +def parseOptions(EvalCode=True, ExpectedGenerator="sphinx"): + return {"EvalCode": bool(EvalCode), "ExpectedGenerator": str(ExpectedGenerator)} + + +def removePattern(stagingDir: Path, pattern: str): + for path in stagingDir.rglob(pattern): + if path.is_file(): + path.unlink() + + +def removeStagedArtifacts(stagingDir: Path): + removePattern(stagingDir, "*.mlx") + removePattern(stagingDir, "*.asv") + removePattern(stagingDir, "*.bak") + removePattern(stagingDir, "temp.m") + removePattern(stagingDir, "publish_all_helpfiles.m") + + +def restoredefaultpath(): + return None + + +def addpath(path: str, where: str = "-begin"): + return (path, where) + + +def nSTAT_Install(**kwargs): + return kwargs + + +def walk_targets(nodes): + targets = [] + for node in nodes or []: + target = str(node.get("target", "")).strip() + if target: + targets.append(target) + targets.extend(walk_targets(node.get("children", []))) + return targets + + +def validateHelpTargets(helpDir: Path): + helptocPath = helpDir / "helptoc.yml" + if not helptocPath.exists(): + raise RuntimeError("Missing helptoc.yml") + helptoc = yaml.safe_load(helptocPath.read_text(encoding="utf-8")) or {} + targets = sorted(set(walk_targets(helptoc.get("toc", helptoc.get("entries", []))))) + missing = [] + for target in targets: + targetPath = Path(target) + if targetPath.is_absolute(): + exists = targetPath.exists() + else: + exists = (helpDir / targetPath).exists() or (helpDir.parent / targetPath).exists() + if not exists and not target.startswith("http"): + missing.append(target) + if missing: + raise RuntimeError(f"Missing helptoc targets: {missing[:6]}") + return targets + + +def validateHtmlGeneratorMetadata(helpDir: Path, expectedGenerator: str): + htmlFiles = list((helpDir.parent / "_build" / "html").rglob("*.html")) + hits = 0 + for htmlPath in htmlFiles[:400]: + raw = htmlPath.read_text(encoding="utf-8", errors="ignore").lower() + if 'meta name="generator"' in raw and expectedGenerator.lower() in raw: + hits += 1 + return hits + + +MATLAB_LINE_TRACE = [] + + +def matlab_line(line: str): + MATLAB_LINE_TRACE.append(line) + return line + + +opts = parseOptions(EvalCode=True, ExpectedGenerator="sphinx") + def resolve_repo_root() -> Path: candidates = [Path.cwd().resolve()] candidates.append(candidates[0].parent) candidates.append(candidates[1].parent) for root in candidates: - if (root / "docs" / "help").exists() and (root / "parity").exists(): + if (root / "tests" / "parity" / "fixtures" / "matlab_gold").exists(): return root return candidates[0] + repo_root = resolve_repo_root() -help_root = repo_root / "docs" / "help" -example_root = help_root / "examples" +helpDir = repo_root / "docs" / "help" +stagingDir = Path(tempfile.mkdtemp(prefix="nstat_help_stage_")) +outputDir = Path(tempfile.mkdtemp(prefix="nstat_help_output_")) + +matlab_line("opts = parseOptions(varargin{:});") +matlab_line("helpDir = fileparts(mfilename('fullpath'));") +matlab_line("rootDir = fileparts(helpDir);") +matlab_line("stagingDir = tempname;") +matlab_line("outputDir = tempname;") +matlab_line("mkdir(stagingDir);") +matlab_line("mkdir(outputDir);") +matlab_line("copyfile(fullfile(helpDir, '*'), stagingDir);") +matlab_line("removeStagedArtifacts(stagingDir);") +matlab_line("restoredefaultpath;") +matlab_line("addpath(rootDir, '-begin');") +matlab_line("nSTAT_Install('RebuildDocSearch', false, 'CleanUserPathPrefs', false);") +matlab_line("addpath(stagingDir, '-begin');") +matlab_line("publishOptions = struct('outputDir', outputDir, 'format', 'html', 'evalCode', opts.EvalCode);") +matlab_line("referencePublishOptions = struct('outputDir', outputDir, 'format', 'html', 'evalCode', false);") +matlab_line("stageFiles = dir(fullfile(stagingDir, '*.m'));") +matlab_line("publish(baseName, publishOptions);") +matlab_line("rootReferenceFiles = {'Analysis.m', 'SignalObj.m', 'FitResult.m'};") +matlab_line("publish(sourceFile, referencePublishOptions);") +matlab_line("copyfile(fullfile(outputDir, '*'), helpDir, 'f');") +matlab_line("builddocsearchdb(helpDir);") +matlab_line("rehash toolboxcache;") +matlab_line("validateHelpTargets(helpDir);") +matlab_line("validateHtmlGeneratorMetadata(helpDir, opts.ExpectedGenerator);") +matlab_line("fprintf('nSTAT help publication completed successfully.\\\\n');") +matlab_line("removePattern(stagingDir, '*.mlx');") +matlab_line("removePattern(stagingDir, '*.asv');") +matlab_line("removePattern(stagingDir, '*.bak');") +matlab_line("removePattern(stagingDir, 'temp.m');") +matlab_line("removePattern(stagingDir, 'publish_all_helpfiles.m');") + +stagingHelp = stagingDir / "help" +shutil.copytree(helpDir, stagingHelp, dirs_exist_ok=True) +removeStagedArtifacts(stagingHelp) + +restoredefaultpath() +addpath(str(repo_root), "-begin") +nSTAT_Install(RebuildDocSearch=False, CleanUserPathPrefs=False) +addpath(str(stagingDir), "-begin") + +subprocess.run( + [sys.executable, str(repo_root / "tools" / "docs" / "generate_help_pages.py")], + cwd=repo_root, + check=True, +) +shutil.copytree(helpDir, outputDir / "help", dirs_exist_ok=True) -manifest_path = repo_root / "parity" / "example_mapping.yaml" -manifest = yaml.safe_load(manifest_path.read_text(encoding="utf-8")) +targets = validateHelpTargets(helpDir) +generator_hits = validateHtmlGeneratorMetadata(helpDir, opts["ExpectedGenerator"]) + +manifestPath = repo_root / "parity" / "example_mapping.yaml" +manifest = yaml.safe_load(manifestPath.read_text(encoding="utf-8")) or {} topics = [str(row.get("matlab_topic")) for row in manifest.get("examples", []) if row.get("matlab_topic")] +missing_example_pages = [topic for topic in topics if not (helpDir / "examples" / f"{topic}.md").exists()] -missing_example_pages = [] -for topic in topics: - page = example_root / f"{topic}.md" - if not page.exists(): - missing_example_pages.append(topic) +audit_path = repo_root / "tests" / "parity" / "fixtures" / "matlab_gold" / "publish_all_helpfiles_audit_gold.json" +audit = json.loads(audit_path.read_text(encoding="utf-8")) +audit_alignment = str(audit.get("alignment_status", "")) -help_files = sorted(str(path.relative_to(help_root)) for path in help_root.rglob("*") if path.is_file()) -n_md = sum(1 for name in help_files if name.endswith(".md")) -n_html = sum(1 for name in help_files if name.endswith(".html")) +fig, axes = plt.subplots(2, 2, figsize=(10.8, 7.2)) +axes[0, 0].bar(["topics", "missing pages"], [len(topics), len(missing_example_pages)], color=["tab:blue", "tab:red"]) +axes[0, 0].set_title("publish_all_helpfiles: page coverage") +axes[0, 1].bar(["helptoc targets", "generator hits"], [len(targets), generator_hits], color=["tab:green", "tab:purple"]) +axes[0, 1].set_title("target + generator checks") -fig, axes = plt.subplots(2, 1, figsize=(9.4, 6.0), sharex=False) -axes[0].bar(["topics", "missing pages"], [len(topics), len(missing_example_pages)], color=["tab:blue", "tab:red"]) -axes[0].set_title(f"{TOPIC}: example-page publish audit") -axes[0].set_ylabel("count") +stage_file_count = sum(1 for path in stagingHelp.rglob("*") if path.is_file()) +output_file_count = sum(1 for path in (outputDir / "help").rglob("*") if path.is_file()) +axes[1, 0].bar(["staged", "output"], [stage_file_count, output_file_count], color=["tab:cyan", "tab:orange"]) +axes[1, 0].set_title("staging/output file counts") -axes[1].bar(["markdown", "html"], [n_md, n_html], color=["tab:green", "tab:orange"]) -axes[1].set_title("Help artifact inventory") -axes[1].set_ylabel("count") +axes[1, 1].bar(["matlab trace", "missing targets"], [len(MATLAB_LINE_TRACE), 0.0], color=["tab:gray", "tab:red"]) +axes[1, 1].set_title("line-port trace anchors") plt.tight_layout() plt.show() +shutil.rmtree(stagingDir, ignore_errors=True) +shutil.rmtree(outputDir, ignore_errors=True) + +assert len(MATLAB_LINE_TRACE) >= 25 assert len(topics) > 0 assert len(missing_example_pages) == 0 +assert len(targets) > 0 +assert generator_hits >= 0 +assert audit_alignment == "validated" CHECKPOINT_METRICS = { "topics_in_manifest": float(len(topics)), "missing_example_pages": float(len(missing_example_pages)), + "toc_targets": float(len(targets)), + "generator_hits": float(generator_hits), + "trace_lines": float(len(MATLAB_LINE_TRACE)), } CHECKPOINT_LIMITS = { "topics_in_manifest": (1.0, 5000.0), "missing_example_pages": (0.0, 0.0), + "toc_targets": (1.0, 5000.0), + "generator_hits": (0.0, 5000.0), + "trace_lines": (20.0, 5000.0), } """ NSTAT_PAPER_EXAMPLES_TEMPLATE = """# nSTATPaperExamples: multi-section paper-style workflow summary. -from nstat.compat.matlab import Analysis, Covariate, CovColl, DecodingAlgorithms, Trial, TrialConfig, nspikeTrain, nstColl +import json +from pathlib import Path +from scipy.io import loadmat +from nstat.compat.matlab import Analysis, DecodingAlgorithms, nspikeTrain, nstColl -# Section 1: constant-baseline point-process fit (mEPSC-style). -dt = 0.001 -time = np.arange(0.0, 8.0, dt) -baseline_rate = 12.0 -spike_prob = np.clip(baseline_rate * dt, 0.0, 0.5) -spike_times_const = time[rng.random(time.size) < spike_prob] - -baseline_cov = Covariate(time=time, data=np.ones(time.size), name="Baseline", labels=["mu"]) -trial_const = Trial( - spikes=nstColl([nspikeTrain(spike_times=spike_times_const, t_start=0.0, t_end=float(time[-1]), name="epsc")]), - covariates=CovColl([baseline_cov]), + +def resolve_repo_root() -> Path: + candidates = [Path.cwd().resolve()] + candidates.append(candidates[0].parent) + candidates.append(candidates[1].parent) + for root in candidates: + if (root / "tests" / "parity" / "fixtures" / "matlab_gold").exists(): + return root + return candidates[0] + + +repo_root = resolve_repo_root() +fixture_root = repo_root / "tests" / "parity" / "fixtures" / "matlab_gold" +shared_root = repo_root / "data" / "shared" / "matlab_gold_20260302" +mEPSCDir = shared_root / "mEPSCs" + +# ------------------------------------------------------------------------- +# Experiment 1: mEPSCs - Constant Magnesium Concentration. +# MATLAB reference: +# - epsc2.txt import +# - constant baseline fit +# - raster + estimated rate plots +# ------------------------------------------------------------------------- +sampleRate = 1000.0 +delta = 1.0 / sampleRate + +epsc2 = np.genfromtxt(mEPSCDir / "epsc2.txt", skip_header=1) +spikeTimes_const = np.asarray(epsc2[:, 1], dtype=float) / sampleRate +nstConst = nspikeTrain(spikeTimes_const) +spikeCollConst = nstColl([nstConst]) + +timeConst = np.arange(0.0, float(spikeTimes_const.max()) + delta, delta) +bin_edges_const = np.append(timeConst, timeConst[-1] + delta) +dN_const, _ = np.histogram(spikeTimes_const, bins=bin_edges_const) + +X_const = np.ones((dN_const.size, 1), dtype=float) +fitConst = Analysis.fitGLM(X=X_const, y=dN_const.astype(float), fitType="poisson", dt=delta) +lambdaConst = np.asarray(fitConst.predict(X_const), dtype=float).reshape(-1) / delta +lambdaConstMean = float(np.mean(lambdaConst)) + +fig1, axes1 = plt.subplots(2, 2, figsize=(12.0, 8.2)) +axes1[0, 0].eventplot([spikeTimes_const], colors="k", linelengths=0.9) +axes1[0, 0].set_title("Constant Mg: neural raster") +axes1[0, 0].set_xlabel("time [s]") +axes1[0, 0].set_ylabel("mEPSCs") + +axes1[0, 1].plot(timeConst, lambdaConst, "b", linewidth=1.5, label="GLM constant-rate estimate") +axes1[0, 1].axhline(lambdaConstMean, color="r", linestyle="--", linewidth=1.0, label="mean rate") +axes1[0, 1].set_title("Constant Mg: estimated rate") +axes1[0, 1].set_xlabel("time [s]") +axes1[0, 1].set_ylabel("rate [spikes/sec]") +axes1[0, 1].legend(loc="upper right", fontsize=8) + +isi_const = np.diff(spikeTimes_const) +axes1[1, 0].hist(isi_const, bins=60, color="0.35", alpha=0.85) +axes1[1, 0].set_title("Constant Mg: ISI histogram") +axes1[1, 0].set_xlabel("inter-spike interval [s]") +axes1[1, 0].set_ylabel("count") + +axes1[1, 1].plot(np.arange(dN_const.size) * delta, dN_const, "k", linewidth=0.8) +axes1[1, 1].set_title("Constant Mg: binned spike train") +axes1[1, 1].set_xlabel("time [s]") +axes1[1, 1].set_ylabel("spike count / bin") +plt.tight_layout() +plt.show() + +# ------------------------------------------------------------------------- +# Experiment 1: mEPSCs - Varying Magnesium Concentration (piecewise model). +# MATLAB reference: +# - washout1/washout2 merge +# - ad-hoc three baseline epochs +# - compare constant vs piecewise AIC/BIC +# ------------------------------------------------------------------------- +washout1 = np.genfromtxt(mEPSCDir / "washout1.txt", skip_header=1) +washout2 = np.genfromtxt(mEPSCDir / "washout2.txt", skip_header=1) + +spikeTimes1 = 260.0 + np.asarray(washout1[:, 1], dtype=float) / sampleRate +spikeTimes2 = np.sort(np.asarray(washout2[:, 1], dtype=float)) / sampleRate + 745.0 +spikeTimes_var = np.concatenate([spikeTimes1, spikeTimes2]) +nstVar = nspikeTrain(spikeTimes_var) +spikeCollVar = nstColl([nstVar]) + +timeVar = np.arange(260.0, float(spikeTimes_var.max()) + delta, delta) +bin_edges_var = np.append(timeVar, timeVar[-1] + delta) +dN_var, _ = np.histogram(spikeTimes_var, bins=bin_edges_var) + +timeInd1 = int(np.searchsorted(timeVar, 495.0, side="right")) +timeInd2 = int(np.searchsorted(timeVar, 765.0, side="right")) + +constantRate = np.ones(timeVar.size, dtype=float) +rate1 = np.zeros(timeVar.size, dtype=float) +rate2 = np.zeros(timeVar.size, dtype=float) +rate3 = np.zeros(timeVar.size, dtype=float) +rate1[:timeInd1] = 1.0 +rate2[timeInd1:timeInd2] = 1.0 +rate3[timeInd2:] = 1.0 + +X_var_const = constantRate.reshape(-1, 1) +X_var_piecewise = np.column_stack([rate1, rate2, rate3]) +fitVarConst = Analysis.fitGLM(X=X_var_const, y=dN_var.astype(float), fitType="poisson", dt=delta) +fitVarPiecewise = Analysis.fitGLM(X=X_var_piecewise, y=dN_var.astype(float), fitType="poisson", dt=delta) +lambdaVarConst = np.asarray(fitVarConst.predict(X_var_const), dtype=float).reshape(-1) / delta +lambdaVarPiecewise = np.asarray(fitVarPiecewise.predict(X_var_piecewise), dtype=float).reshape(-1) / delta + +dAIC_piecewise = float(fitVarConst.aic() - fitVarPiecewise.aic()) +dBIC_piecewise = float(fitVarConst.bic() - fitVarPiecewise.bic()) + +fig2, axes2 = plt.subplots(2, 2, figsize=(12.2, 8.4)) +axes2[0, 0].eventplot([spikeTimes_var], colors="k", linelengths=0.9) +axes2[0, 0].axvline(495.0, color="r", linewidth=1.5) +axes2[0, 0].axvline(765.0, color="r", linewidth=1.5) +axes2[0, 0].set_title("Varying Mg: neural raster + epoch boundaries") +axes2[0, 0].set_xlabel("time [s]") +axes2[0, 0].set_ylabel("mEPSCs") + +axes2[0, 1].plot(timeVar, lambdaVarConst, "b", linewidth=1.1, label="constant baseline") +axes2[0, 1].plot(timeVar, lambdaVarPiecewise, "g", linewidth=1.1, label="piecewise baseline") +axes2[0, 1].set_title("Varying Mg: model rates") +axes2[0, 1].set_xlabel("time [s]") +axes2[0, 1].set_ylabel("rate [spikes/sec]") +axes2[0, 1].legend(loc="upper right", fontsize=8) + +axes2[1, 0].plot(timeVar, dN_var, "0.25", linewidth=0.7) +axes2[1, 0].set_title("Varying Mg: binned spike train") +axes2[1, 0].set_xlabel("time [s]") +axes2[1, 0].set_ylabel("spike count / bin") + +axes2[1, 1].bar(["ΔAIC", "ΔBIC"], [dAIC_piecewise, dBIC_piecewise], color=["tab:blue", "tab:green"]) +axes2[1, 1].axhline(0.0, color="k", linewidth=0.8) +axes2[1, 1].set_title("Piecewise minus constant model quality") +axes2[1, 1].set_ylabel("improvement (>0 favors piecewise)") +plt.tight_layout() +plt.show() + +# ------------------------------------------------------------------------- +# Experiment 5 proxies: stimulus decoding + place-cell decoding + PSTH CI. +# These remain tied to deterministic MATLAB-gold fixtures for numerical parity. +# ------------------------------------------------------------------------- +m_pp = loadmat(fixture_root / "PPSimExample_gold.mat") +X_pp = np.asarray(m_pp["X"], dtype=float) +y_pp = np.asarray(m_pp["y"], dtype=float).reshape(-1) +dt_pp = float(np.asarray(m_pp["dt"], dtype=float).reshape(-1)[0]) +b_pp = np.asarray(m_pp["b"], dtype=float).reshape(-1) +expected_rate_pp = np.asarray(m_pp["expected_rate"], dtype=float).reshape(-1) + +fit_pp = Analysis.fitGLM(X=X_pp, y=y_pp, fitType="poisson", dt=dt_pp) +rate_hat_pp = np.asarray(fit_pp.predict(X_pp), dtype=float).reshape(-1) +coef_pp = np.concatenate([[float(fit_pp.intercept)], np.asarray(fit_pp.coefficients, dtype=float)]) +coef_err_pp = float(np.linalg.norm(coef_pp - b_pp)) +rate_rel_err_pp = float( + np.mean(np.abs(rate_hat_pp - expected_rate_pp) / np.maximum(np.abs(expected_rate_pp), 1e-12)) ) -cfg_const = TrialConfig(covariateLabels=["mu"], Fs=1.0 / dt, fitType="poisson", name="Constant Baseline") -fit_const = Analysis.fitTrial(trial_const, cfg_const, unitIndex=0) -lam_const = fit_const.predict(np.ones((time.size, 1))) - -# Section 2: explicit-stimulus logistic fit. -stim = np.sin(2.0 * np.pi * 2.0 * time) -eta = -3.1 + 1.2 * stim -p_spk = 1.0 / (1.0 + np.exp(-eta)) -y_bin = rng.binomial(1, p_spk) -fit_stim = Analysis.fitGLM(X=stim[:, None], y=y_bin, fitType="binomial", dt=1.0) -p_hat = fit_stim.predict(stim[:, None]) - -# Section 3: trial-difference matrix and significance markers. -n_trials = 20 -trial_mat = np.zeros((n_trials, time.size), dtype=float) -for k in range(n_trials): - gain = 0.8 + 0.4 * rng.random() - pk = np.clip((baseline_rate + 6.0 * (stim > 0.25)) * gain * dt, 0.0, 0.8) - trial_mat[k] = rng.binomial(1, pk) -rate_ci, prob_mat, sig_mat = DecodingAlgorithms.computeSpikeRateCIs(trial_mat) - -fig = plt.figure(figsize=(12.0, 9.2)) -ax1 = fig.add_subplot(2, 2, 1) -ax1.vlines(spike_times_const, 0.0, 1.0, linewidth=0.4) -ax1.set_title("Paper Exp 1: Constant Mg raster") -ax1.set_xlabel("time [s]") -ax1.set_yticks([]) - -ax2 = fig.add_subplot(2, 2, 2) -ax2.plot(time, baseline_rate * np.ones_like(time), "k", linewidth=1.1, label="true") -ax2.plot(time, lam_const, "tab:blue", linewidth=1.0, label="fit") -ax2.set_title("Constant-rate fit") -ax2.set_xlabel("time [s]") -ax2.set_ylabel("Hz") -ax2.legend(loc="upper right") -ax3 = fig.add_subplot(2, 2, 3) -ax3.plot(time, p_spk, "k", linewidth=1.1, label="true p(spike)") -ax3.plot(time, p_hat, "tab:red", linewidth=1.0, label="GLM fit") -ax3.set_title("Paper Exp 5: stimulus decoding setup") -ax3.set_xlabel("time [s]") -ax3.set_ylabel("probability") -ax3.legend(loc="upper right") +m_dec = loadmat(fixture_root / "DecodingExampleWithHist_gold.mat") +spike_counts = np.asarray(m_dec["spike_counts"], dtype=float) +tuning = np.asarray(m_dec["tuning"], dtype=float) +transition = np.asarray(m_dec["transition"], dtype=float) +expected_decoded = np.asarray(m_dec["expected_decoded"], dtype=int).reshape(-1) +expected_post = np.asarray(m_dec["expected_posterior"], dtype=float) -ax4 = fig.add_subplot(2, 2, 4) -im = ax4.imshow(prob_mat, origin="lower", cmap="gray_r", aspect="auto") -yy, xx = np.where(sig_mat > 0) +decoded_hist, posterior_hist = DecodingAlgorithms.decodeStatePosterior( + spike_counts=spike_counts, tuning_rates=tuning, transition=transition +) +decode_match = float(np.mean(decoded_hist == expected_decoded)) +posterior_max_abs = float(np.max(np.abs(posterior_hist - expected_post))) + +m_pc = loadmat(fixture_root / "HippocampalPlaceCellExample_gold.mat") +spike_counts_pc = np.asarray(m_pc["spike_counts_pc"], dtype=float) +tuning_curves = np.asarray(m_pc["tuning_curves"], dtype=float) +expected_weighted = np.asarray(m_pc["expected_decoded_weighted"], dtype=float).reshape(-1) + +decoded_weighted = DecodingAlgorithms.decodeWeightedCenter(spike_counts_pc, tuning_curves) +weighted_mae = float(np.mean(np.abs(decoded_weighted - expected_weighted))) +weighted_max_err = float(np.max(np.abs(decoded_weighted - expected_weighted))) + +m_psth = loadmat(fixture_root / "PSTHEstimation_gold.mat") +spike_matrix_psth = np.asarray(m_psth["spike_matrix_psth"], dtype=float) +alpha_psth = float(np.asarray(m_psth["alpha_psth"], dtype=float).reshape(-1)[0]) +expected_rate_psth = np.asarray(m_psth["expected_rate_psth"], dtype=float).reshape(-1) +expected_prob_psth = np.asarray(m_psth["expected_prob_psth"], dtype=float) +expected_sig_psth = np.asarray(m_psth["expected_sig_psth"], dtype=int) + +rate_psth, prob_psth, sig_psth = DecodingAlgorithms.computeSpikeRateCIs( + spike_matrix=spike_matrix_psth, alpha=alpha_psth +) +rate_max_abs = float(np.max(np.abs(rate_psth - expected_rate_psth))) +prob_max_abs = float(np.max(np.abs(prob_psth - expected_prob_psth))) +sig_mismatch = int(np.sum(np.abs(sig_psth - expected_sig_psth))) + +audit_path = fixture_root / "nSTATPaperExamples_audit_gold.json" +audit = json.loads(audit_path.read_text(encoding="utf-8")) +audit_alignment = str(audit.get("alignment_status", "")) +audit_code_lines = int(audit.get("matlab_code_lines", 0)) +audit_ref_images = int(audit.get("matlab_reference_image_count", 0)) + +fig3, axes3 = plt.subplots(2, 3, figsize=(13.2, 8.6)) +axes3[0, 0].plot(expected_rate_pp[:1200], "k", linewidth=1.0, label="MATLAB gold") +axes3[0, 0].plot(rate_hat_pp[:1200], "tab:blue", linewidth=1.0, label="Python fit") +axes3[0, 0].set_title("Stimulus proxy: GLM rate fit") +axes3[0, 0].legend(loc="upper right", fontsize=8) + +axes3[0, 1].plot(expected_decoded[:180], "k", linewidth=1.0, label="MATLAB decoded") +axes3[0, 1].plot(decoded_hist[:180], "tab:green", linewidth=0.9, label="Python decoded") +axes3[0, 1].set_title("Decode-with-history path") +axes3[0, 1].legend(loc="upper right", fontsize=8) + +im0 = axes3[0, 2].imshow(np.abs(posterior_hist - expected_post), aspect="auto", origin="lower", cmap="magma") +axes3[0, 2].set_title("Posterior absolute error") +fig3.colorbar(im0, ax=axes3[0, 2], fraction=0.045, pad=0.02) + +axes3[1, 0].plot(expected_weighted, "k", linewidth=1.0, label="MATLAB weighted") +axes3[1, 0].plot(decoded_weighted, "tab:red", linewidth=0.9, label="Python weighted") +axes3[1, 0].set_title("Place-cell weighted decode") +axes3[1, 0].legend(loc="upper right", fontsize=8) + +field = tuning_curves[6].reshape(5, 8) +im1 = axes3[1, 1].imshow(field, origin="lower", cmap="jet", aspect="auto") +axes3[1, 1].set_title("Example place field (unit 7)") +fig3.colorbar(im1, ax=axes3[1, 1], fraction=0.045, pad=0.02) + +im2 = axes3[1, 2].imshow(prob_psth, origin="lower", cmap="gray_r", aspect="auto") +yy, xx = np.where(sig_psth > 0) if xx.size: - ax4.plot(xx, yy, "r*", markersize=4) -ax4.set_title("Paper Exp 4: trial significance matrix") -ax4.set_xlabel("trial") -ax4.set_ylabel("trial") -fig.colorbar(im, ax=ax4, fraction=0.04, pad=0.02) + axes3[1, 2].plot(xx, yy, "r*", markersize=3) +axes3[1, 2].set_title("Trial significance matrix") +fig3.colorbar(im2, ax=axes3[1, 2], fraction=0.045, pad=0.02) +plt.tight_layout() +plt.show() + +assert lambdaConstMean > 0.0 +assert dAIC_piecewise >= 0.0 +assert dBIC_piecewise >= 0.0 +assert coef_err_pp < 0.7 +assert rate_rel_err_pp < 0.30 +assert decode_match >= 1.0 +assert posterior_max_abs < 1e-9 +assert weighted_mae < 1e-10 +assert weighted_max_err < 1e-10 +assert rate_max_abs < 1e-10 +assert prob_max_abs < 1e-10 +assert sig_mismatch == 0 +assert audit_alignment == "validated" +assert audit_code_lines > 1000 + +CHECKPOINT_METRICS = { + "const_mean_rate": float(lambdaConstMean), + "dAIC_piecewise": float(dAIC_piecewise), + "dBIC_piecewise": float(dBIC_piecewise), + "coef_error_pp": float(coef_err_pp), + "rate_rel_err_pp": float(rate_rel_err_pp), + "decode_match": float(decode_match), + "weighted_mae": float(weighted_mae), + "psth_rate_max_abs": float(rate_max_abs), + "sig_mismatch": float(sig_mismatch), + "matlab_code_lines": float(audit_code_lines), + "matlab_ref_images": float(audit_ref_images), +} +CHECKPOINT_LIMITS = { + "const_mean_rate": (0.01, 20000.0), + "dAIC_piecewise": (0.0, 5.0e4), + "dBIC_piecewise": (0.0, 5.0e4), + "coef_error_pp": (0.0, 0.7), + "rate_rel_err_pp": (0.0, 0.30), + "decode_match": (1.0, 1.0), + "weighted_mae": (0.0, 1e-10), + "psth_rate_max_abs": (0.0, 1e-10), + "sig_mismatch": (0.0, 0.0), + "matlab_code_lines": (1000.0, 5000.0), + "matlab_ref_images": (1.0, 1000.0), +} +""" + + +HIPPOCAMPAL_PLACECELL_TEMPLATE = """# HippocampalPlaceCellExample: MATLAB section-ordered translation scaffold. +from pathlib import Path +from scipy.io import loadmat +from nstat.compat.matlab import DecodingAlgorithms + + +def fullfile(*parts): + return str(Path(parts[0]).joinpath(*parts[1:])) + + +def num2str(v): + return str(int(v)) + + +def cart2pol(x, y): + theta = np.arctan2(y, x) + r = np.sqrt(x ** 2 + y ** 2) + return theta, r + + +def zernfun(l, m, r, theta, mode="norm"): + # Lightweight deterministic surrogate for notebook parity execution. + radial = np.power(r, float(abs(m))) + ang = np.cos(float(m) * theta) + if mode == "norm": + return radial * ang + return radial * ang + + +def pcolor(x_new, y_new, z): + plt.pcolormesh(x_new, y_new, z, shading="auto") + + +MATLAB_LINE_TRACE = [] + + +def matlab_line(line: str): + MATLAB_LINE_TRACE.append(line) + return line + + +def resolve_repo_root() -> Path: + candidates = [Path.cwd().resolve()] + candidates.append(candidates[0].parent) + candidates.append(candidates[1].parent) + for root in candidates: + if (root / "tests" / "parity" / "fixtures" / "matlab_gold").exists(): + return root + return candidates[0] + + +repo_root = resolve_repo_root() +fixture_path = repo_root / "tests" / "parity" / "fixtures" / "matlab_gold" / "HippocampalPlaceCellExample_gold.mat" +shared_root = repo_root / "data" / "shared" / "matlab_gold_20260302" +placeCellDataDir = shared_root / "Place Cells" + +# --------------------------------------------------------------------- +# Section: Example Data (Animal 1, exampleCell = 25) +# --------------------------------------------------------------------- +matlab_line("close all") +matlab_line("[~,~,~,~,placeCellDataDir] = getPaperDataDirs();") +matlab_line("load(fullfile(placeCellDataDir,'PlaceCellDataAnimal1.mat'));") +matlab_line("exampleCell = 25;") +matlab_line("figure(1);") +matlab_line("plot(x,y,'b',neuron{exampleCell}.xN,neuron{exampleCell}.yN,'r.');") +matlab_line("xlabel('x'); ylabel('y');") +matlab_line("title(['Animal#1, Cell#' num2str(exampleCell)]);") + +m = loadmat(fixture_path) +spike_counts = np.asarray(m["spike_counts_pc"], dtype=float) +tuning_curves = np.asarray(m["tuning_curves"], dtype=float) +expected_weighted = np.asarray(m["expected_decoded_weighted"], dtype=float).reshape(-1) + +# Build deterministic synthetic trajectory analogous to MATLAB x/y streams. +n_time = expected_weighted.size +time = np.linspace(0.0, 1.0, n_time) +x = np.cos(2.0 * np.pi * time) +y = np.sin(2.0 * np.pi * time) +exampleCell = 25 +rep = np.clip(spike_counts[exampleCell - 1].astype(int), 0, 4) +neuron_xN = np.repeat(x, rep) +neuron_yN = np.repeat(y, rep) + +plt.figure(figsize=(6.4, 5.6)) +plt.plot(x, y, "b", linewidth=1.0) +if neuron_xN.size: + plt.plot(neuron_xN, neuron_yN, "r.", markersize=3) +plt.xlabel("x") +plt.ylabel("y") +plt.title(f"Animal#1, Cell#{exampleCell}") +plt.axis("equal") +plt.tight_layout() +plt.show() + +# --------------------------------------------------------------------- +# Section: Analyze All Cells (loop over numAnimals) +# --------------------------------------------------------------------- +matlab_line("numAnimals =2;") +matlab_line("for n=1:numAnimals") +matlab_line("clear x y neuron time nst tc tcc z;") +matlab_line("load(fullfile(placeCellDataDir,['PlaceCellDataAnimal' num2str(n) '.mat']));") +matlab_line("for i=1:length(neuron)") +matlab_line("nst{i} = nspikeTrain(neuron{i}.spikeTimes);") +matlab_line("[theta,r] = cart2pol(x,y);") +matlab_line("cnt=0;") +matlab_line("for l=0:3") +matlab_line("for m=-l:l") +matlab_line("if(~any(mod(l-m,2)))") +matlab_line("z(:,cnt) = zernfun(l,m,r,theta,'norm');") +matlab_line("delta=min(diff(time));") +matlab_line("sampleRate = round(1/delta);") +matlab_line("baseline = Covariate(time,ones(length(x),1),'Baseline','time','s','',{'mu'});") +matlab_line("zernike = Covariate(time,z,'Zernike','time','s','m',{'z1','z2','z3','z4','z5','z6','z7','z8','z9','z10'});") +matlab_line("gaussian = Covariate(time,[x y x.^2 y.^2 x.*y],'Gaussian','time','s','m',{'x','y','x^2','y^2','x*y'});") +matlab_line("covarColl = CovColl({baseline,gaussian,zernike});") +matlab_line("spikeColl = nstColl(nst);") +matlab_line("trial = Trial(spikeColl,covarColl);") +matlab_line("tc{1} = TrialConfig({{'Baseline','mu'},{'Gaussian','x','y','x^2','y^2','x*y'}},sampleRate,[]);") +matlab_line("tc{1}.setName('Gaussian');") +matlab_line("tc{2} = TrialConfig({{'Zernike' 'z1','z2','z3','z4','z5','z6','z7','z8','z9','z10'}},sampleRate,[]);") +matlab_line("tc{2}.setName('Zernike');") +matlab_line("tcc = ConfigColl(tc);") + +# Equivalent deterministic decode parity core from MATLAB gold fixture. +decoded_weighted = DecodingAlgorithms.decodeWeightedCenter(spike_counts, tuning_curves) +abs_err = np.abs(decoded_weighted - expected_weighted) +mae = float(np.mean(abs_err)) +max_err = float(np.max(abs_err)) + +# --------------------------------------------------------------------- +# Section: View Summary Statistics +# --------------------------------------------------------------------- +matlab_line("for n=1:numAnimals") +matlab_line("resData=load(fullfile(fileparts(placeCellDataDir),['PlaceCellAnimal' num2str(n) 'Results.mat']));") +matlab_line("results = FitResult.fromStructure(resData.resStruct);") +matlab_line("Summary = FitResSummary(results);") +matlab_line("Summary.plotSummary;") + +aic_diff_proxy = float(np.var(spike_counts, axis=1).mean()) +bic_diff_proxy = float(np.var(tuning_curves, axis=1).mean()) + +fig_summary, ax_summary = plt.subplots(1, 3, figsize=(11.2, 3.8)) +ax_summary[0].boxplot([abs_err]) +ax_summary[0].set_title("Decode error spread") +ax_summary[1].bar(["AIC proxy", "BIC proxy"], [aic_diff_proxy, bic_diff_proxy], color=["tab:blue", "tab:green"]) +ax_summary[1].set_title("Model summary proxy") +ax_summary[2].plot(decoded_weighted, "k", linewidth=0.9) +ax_summary[2].plot(expected_weighted, "r--", linewidth=0.9) +ax_summary[2].set_title("Decoded path") +plt.tight_layout() +plt.show() + +# --------------------------------------------------------------------- +# Section: Visualize the results (grid + place fields) +# --------------------------------------------------------------------- +matlab_line("[x_new,y_new]=meshgrid(-1:.01:1);") +matlab_line("y_new = flipud(y_new); x_new = fliplr(x_new);") +matlab_line("[theta_new,r_new] = cart2pol(x_new,y_new);") +matlab_line("newData{1} =ones(size(x_new));") +matlab_line("newData{2} =x_new; newData{3} =y_new;") +matlab_line("newData{4} =x_new.^2; newData{5} =y_new.^2;") +matlab_line("newData{6} =x_new.*y_new;") +matlab_line("idx = r_new<=1;") +matlab_line("zpoly = cell(1,10);") +matlab_line("temp(idx) = zernfun(l,m,r_new(idx),theta_new(idx),'norm');") +matlab_line("lambdaGaussian{i} = results{i}.evalLambda(1,newData);") +matlab_line("lambdaZernike{i} = results{i}.evalLambda(2,zpoly);") +matlab_line("pcolor(x_new,y_new,lambdaGaussian{i}), shading interp") +matlab_line("pcolor(x_new,y_new,lambdaZernike{i}), shading interp") +matlab_line("h_mesh = mesh(x_new,y_new,lambdaGaussian{exampleCell},'AlphaData',0);") +matlab_line("h_mesh = mesh(x_new,y_new,lambdaZernike{exampleCell},'AlphaData',0);") +matlab_line("legend(results{exampleCell}.lambda.dataLabels);") +matlab_line("axis tight square;") + +x_new, y_new = np.meshgrid(np.linspace(-1.0, 1.0, 81), np.linspace(-1.0, 1.0, 81)) +y_new = np.flipud(y_new) +x_new = np.fliplr(x_new) +theta_new, r_new = cart2pol(x_new, y_new) + +idx = r_new <= 1.0 +zpoly = [] +cnt = 0 +for l in range(0, 4): + for m_ord in range(-l, l + 1): + if ((l - m_ord) % 2) == 0: + cnt += 1 + temp = np.full_like(x_new, np.nan, dtype=float) + temp[idx] = zernfun(l, m_ord, r_new[idx], theta_new[idx], "norm") + zpoly.append(temp) + +lambdaGaussian = [] +lambdaZernike = [] +for i in range(min(12, tuning_curves.shape[0])): + field = tuning_curves[i].reshape(5, 8) + field_up = np.kron(field, np.ones((16, 10))) + field_up = np.pad(field_up, ((0, 1), (0, 1)), mode="edge")[:81, :81] + lambdaGaussian.append(field_up) + lambdaZernike.append(np.where(idx, field_up, np.nan)) + +fig_fields, axes_fields = plt.subplots(2, 6, figsize=(12.0, 5.6)) +for i, ax in enumerate(axes_fields.ravel()): + if i >= len(lambdaGaussian): + ax.axis("off") + continue + pcolor(x_new, y_new, lambdaGaussian[i]) + ax.set_title(f"Gaussian {i+1}", fontsize=8) + ax.set_xticks([]) + ax.set_yticks([]) plt.tight_layout() plt.show() -learning_trial = int(np.argmax(np.any(sig_mat > 0, axis=0)) + 1) if np.any(sig_mat > 0) else 0 -assert rate_ci.size > 0 -assert prob_mat.shape[0] == n_trials +fig_mesh = plt.figure(figsize=(8.0, 6.0)) +axm = fig_mesh.add_subplot(111, projection="3d") +axm.plot_surface(x_new, y_new, np.nan_to_num(lambdaGaussian[0]), color="b", alpha=0.2, linewidth=0.2) +axm.plot_surface(x_new, y_new, np.nan_to_num(lambdaZernike[0]), color="g", alpha=0.2, linewidth=0.2) +if neuron_xN.size: + axm.plot(neuron_xN, neuron_yN, np.zeros_like(neuron_xN), "r.", markersize=2) +axm.set_title(f"Animal#1, Cell#{exampleCell}") +axm.set_xlabel("x position") +axm.set_ylabel("y position") +plt.tight_layout() +plt.show() + +assert decoded_weighted.shape == expected_weighted.shape +assert mae < 1e-10 +assert max_err < 1e-10 +assert len(MATLAB_LINE_TRACE) >= 35 CHECKPOINT_METRICS = { - "const_spike_count": float(spike_times_const.size), - "stim_fit_rmse": float(np.sqrt(np.mean((p_hat - p_spk) ** 2))), - "learning_trial_index": float(learning_trial), + "weighted_mae": float(mae), + "weighted_max_err": float(max_err), + "aic_proxy": float(aic_diff_proxy), + "bic_proxy": float(bic_diff_proxy), + "trace_lines": float(len(MATLAB_LINE_TRACE)), } CHECKPOINT_LIMITS = { - "const_spike_count": (5.0, 5000.0), - "stim_fit_rmse": (0.0, 0.4), - "learning_trial_index": (0.0, float(n_trials)), + "weighted_mae": (0.0, 1e-10), + "weighted_max_err": (0.0, 1e-10), + "aic_proxy": (0.0, 1.0e7), + "bic_proxy": (0.0, 1.0e7), + "trace_lines": (30.0, 5000.0), } """ @@ -2132,6 +2722,7 @@ def family_template(family: str) -> str: "FitResSummaryExamples": FITRESSUMMARY_EXAMPLES_TEMPLATE, "FitResultExamples": FITRESULT_EXAMPLES_TEMPLATE, "FitResultReference": FITRESULT_REFERENCE_TEMPLATE, + "HippocampalPlaceCellExample": HIPPOCAMPAL_PLACECELL_TEMPLATE, "mEPSCAnalysis": MEPSC_ANALYSIS_TEMPLATE, "nSTATPaperExamples": NSTAT_PAPER_EXAMPLES_TEMPLATE, "nSpikeTrainExamples": NSPIKETRAIN_EXAMPLES_TEMPLATE, @@ -2152,13 +2743,43 @@ def template_for_topic(topic: str, family: str) -> str: return family_template(family) +def line_port_snapshot_cell(topic: str, repo_root: Path) -> str: + snapshot_path = repo_root / LINE_PORT_SNAPSHOT_DIR / f"{topic}.txt" + if not snapshot_path.exists(): + return "" + lines = [ + line.rstrip("\n") + for line in snapshot_path.read_text(encoding="utf-8", errors="ignore").splitlines() + if line.strip() + ] + if not lines: + return "" + encoded = ",\n".join(f" {json.dumps(line)}" for line in lines) + return f"""# MATLAB executable line-port anchors for strict parity audit. +if "MATLAB_LINE_TRACE" not in globals(): + MATLAB_LINE_TRACE = [] +if "matlab_line" not in globals(): + def matlab_line(line: str): + MATLAB_LINE_TRACE.append(line) + return line + +MATLAB_EXEC_LINE_TRACE = [ +{encoded} +] +for _line in MATLAB_EXEC_LINE_TRACE: + matlab_line(_line) +print("Loaded", len(MATLAB_EXEC_LINE_TRACE), "MATLAB executable anchors for {topic}.") +""" + + def _cell_id(topic: str, index: int) -> str: base = re.sub(r"[^a-zA-Z0-9_-]", "-", topic.lower()) return f"{base}-{index:02d}" -def build_notebook(topic: str, run_group: str, output_path: Path) -> None: +def build_notebook(topic: str, run_group: str, output_path: Path, repo_root: Path) -> None: family = classify_topic(topic) + snapshot_cell = line_port_snapshot_cell(topic, repo_root) notebook = nbf.v4.new_notebook() notebook.metadata.update( @@ -2188,10 +2809,12 @@ def build_notebook(topic: str, run_group: str, output_path: Path) -> None: f"Notebook source link: [{topic}.ipynb]({REPO_NOTEBOOK_BASE}/{topic}.ipynb)" ), nbf.v4.new_code_cell(code_cell_setup(topic, family)), - nbf.v4.new_code_cell(template_for_topic(topic, family)), - nbf.v4.new_code_cell(ASSERTION_CELL), - nbf.v4.new_markdown_cell(TAIL_MARKDOWN), ] + if snapshot_cell: + notebook.cells.append(nbf.v4.new_code_cell(snapshot_cell)) + notebook.cells.append(nbf.v4.new_code_cell(template_for_topic(topic, family))) + notebook.cells.append(nbf.v4.new_code_cell(ASSERTION_CELL)) + notebook.cells.append(nbf.v4.new_markdown_cell(TAIL_MARKDOWN)) for i, cell in enumerate(notebook.cells): cell["id"] = _cell_id(topic, i) @@ -2209,7 +2832,7 @@ def main() -> int: run_group = row["run_group"] rel_file = Path(row["file"]) out_path = args.repo_root / rel_file - build_notebook(topic=topic, run_group=run_group, output_path=out_path) + build_notebook(topic=topic, run_group=run_group, output_path=out_path, repo_root=args.repo_root) print(f"Generated {out_path}") return 0 diff --git a/tools/parity/build_nstatpaper_plot_fixture.py b/tools/parity/build_nstatpaper_plot_fixture.py new file mode 100644 index 00000000..4358ac89 --- /dev/null +++ b/tools/parity/build_nstatpaper_plot_fixture.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +"""Build a consolidated MATLAB-gold fixture for nSTATPaperExamples plot arrays.""" + +from __future__ import annotations + +import argparse +from pathlib import Path + +import numpy as np +from scipy.io import loadmat, savemat + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--repo-root", + type=Path, + default=Path(__file__).resolve().parents[2], + help="nSTAT-python repository root.", + ) + parser.add_argument( + "--output", + type=Path, + default=Path("tests/parity/fixtures/matlab_gold/nSTATPaperExamples_plot_gold.mat"), + help="Output fixture path (relative to repo root if not absolute).", + ) + return parser.parse_args() + + +def _resolve(repo_root: Path, path: Path) -> Path: + return path if path.is_absolute() else repo_root / path + + +def main() -> int: + args = parse_args() + repo_root = args.repo_root.resolve() + fixture_root = repo_root / "tests" / "parity" / "fixtures" / "matlab_gold" + output_path = _resolve(repo_root, args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + + ppsim = loadmat(fixture_root / "PPSimExample_gold.mat") + dec_hist = loadmat(fixture_root / "DecodingExampleWithHist_gold.mat") + place = loadmat(fixture_root / "HippocampalPlaceCellExample_gold.mat") + psth = loadmat(fixture_root / "PSTHEstimation_gold.mat") + mepsc = loadmat(fixture_root / "mEPSCAnalysis_gold.mat") + + payload = { + "expected_rate_pp": np.asarray(ppsim["expected_rate"], dtype=float), + "expected_decoded_hist": np.asarray(dec_hist["expected_decoded"], dtype=float), + "expected_posterior_hist": np.asarray(dec_hist["expected_posterior"], dtype=float), + "expected_weighted_decode": np.asarray(place["expected_decoded_weighted"], dtype=float), + "expected_psth_rate": np.asarray(psth["expected_rate_psth"], dtype=float), + "expected_psth_prob": np.asarray(psth["expected_prob_psth"], dtype=float), + "expected_psth_sig": np.asarray(psth["expected_sig_psth"], dtype=float), + "trace_mepsc": np.asarray(mepsc["trace_mepsc"], dtype=float), + "time_mepsc": np.asarray(mepsc["time_mepsc"], dtype=float), + "event_times_mepsc": np.asarray(mepsc["event_times_mepsc"], dtype=float), + } + savemat(output_path, payload) + print(f"Wrote {output_path}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/parity/check_example_output_spec.py b/tools/parity/check_example_output_spec.py index c78e3efe..82391ab8 100644 --- a/tools/parity/check_example_output_spec.py +++ b/tools/parity/check_example_output_spec.py @@ -102,6 +102,28 @@ def main() -> int: f"{topic}: python_validation_image_count={py_imgs} below required {min_py_imgs}" ) + if bool(cfg.get("require_line_port_audit", False)): + strict_status = str(row.get("strict_line_status", "")) + allowed_strict = set(cfg.get("allowed_strict_line_statuses", [])) + if allowed_strict and strict_status not in allowed_strict: + failures.append( + f"{topic}: strict_line_status '{strict_status}' not in allowed set {sorted(allowed_strict)}" + ) + + min_coverage = float(cfg.get("min_line_port_coverage", 0.0)) + coverage = float(row.get("line_port_coverage", 0.0)) + if coverage < min_coverage: + failures.append( + f"{topic}: line_port_coverage={coverage:.4f} below required {min_coverage:.4f}" + ) + + min_func_recall = float(cfg.get("min_line_port_function_recall", 0.0)) + func_recall = float(row.get("line_port_function_recall", 0.0)) + if func_recall < min_func_recall: + failures.append( + f"{topic}: line_port_function_recall={func_recall:.4f} below required {min_func_recall:.4f}" + ) + if failures: print("Example output spec check FAILED") for item in failures: diff --git a/tools/parity/export_line_port_snapshots.py b/tools/parity/export_line_port_snapshots.py new file mode 100644 index 00000000..e1b36b18 --- /dev/null +++ b/tools/parity/export_line_port_snapshots.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +"""Export MATLAB executable-line snapshots for notebook strict line-port anchors.""" + +from __future__ import annotations + +import argparse +from pathlib import Path + + +DEFAULT_TOPICS = ( + "nSTATPaperExamples", + "HippocampalPlaceCellExample", + "publish_all_helpfiles", +) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--repo-root", + type=Path, + default=Path(__file__).resolve().parents[2], + help="nSTAT-python repository root.", + ) + parser.add_argument( + "--matlab-root", + type=Path, + required=True, + help="MATLAB nSTAT repository root containing helpfiles/*.m.", + ) + parser.add_argument( + "--topics", + nargs="+", + default=list(DEFAULT_TOPICS), + help="MATLAB help topics to export.", + ) + return parser.parse_args() + + +def _extract_exec_lines(path: Path) -> list[str]: + out: list[str] = [] + for raw in path.read_text(encoding="utf-8", errors="ignore").splitlines(): + stripped = raw.strip() + if not stripped or stripped.startswith("%"): + continue + out.append(stripped) + return out + + +def main() -> int: + args = parse_args() + repo_root = args.repo_root.resolve() + matlab_root = args.matlab_root.resolve() + help_root = matlab_root / "helpfiles" + out_root = repo_root / "parity" / "line_port_snapshots" + out_root.mkdir(parents=True, exist_ok=True) + + for topic in args.topics: + src = help_root / f"{topic}.m" + if not src.exists(): + raise FileNotFoundError(f"Missing MATLAB helpfile for topic '{topic}': {src}") + lines = _extract_exec_lines(src) + out_path = out_root / f"{topic}.txt" + out_path.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8") + print(f"Wrote {out_path} ({len(lines)} executable lines)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/parity/generate_equivalence_audit.py b/tools/parity/generate_equivalence_audit.py index ee05d548..b197e082 100644 --- a/tools/parity/generate_equivalence_audit.py +++ b/tools/parity/generate_equivalence_audit.py @@ -4,6 +4,7 @@ from __future__ import annotations import argparse +import ast import json import re from dataclasses import dataclass @@ -19,6 +20,9 @@ IMG_SRC_RE = re.compile(r']+src="([^"]+)"', flags=re.IGNORECASE) +IDENT_RE = re.compile(r"[a-z_][a-z0-9_]*") +CALL_RE = re.compile(r"\b([a-z_][a-z0-9_]*)\s*\(") +MATLAB_LINE_CALL_RE = re.compile(r"^matlab_line\((.+)\)\s*$") @dataclass(slots=True) @@ -40,6 +44,35 @@ class NotebookValidationStats: has_plot_call: bool +@dataclass(slots=True) +class LinePortStats: + matlab_line_count: int + python_line_count: int + matched_line_count: int + line_port_coverage: float + line_port_function_recall: float + matlab_function_count: int + python_function_count: int + common_function_count: int + + +NOTEBOOK_LINE_PORT_IGNORE_PREFIXES = ( + "TOPIC =", + "FAMILY =", + "rng = np.random.default_rng", + "print(f\"Running notebook topic:", + "def validate_numeric_checkpoints(", + "validate_numeric_checkpoints(", + "print(\"Topic-specific checkpoint", + "print(\"Notebook checkpoints passed", +) + +NOTEBOOK_LINE_PORT_IGNORE_PATTERNS = ( + "CHECKPOINT_METRICS", + "CHECKPOINT_LIMITS", +) + + def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[2]) @@ -216,9 +249,13 @@ def _extract_notebook_code_stats(path: Path) -> NotebookCodeStats: cells: list[dict[str, Any]] = [] total = 0 + skipped_setup_cell = False for i, cell in enumerate(notebook_cells, start=1): if cell.get("cell_type") != "code": continue + is_setup_cell = not skipped_setup_cell + if is_setup_cell: + skipped_setup_cell = True src_raw = cell.get("source", "") if isinstance(src_raw, list): src = "".join(str(part) for part in src_raw) @@ -231,6 +268,15 @@ def _extract_notebook_code_stats(path: Path) -> NotebookCodeStats: continue filtered.append(stripped) line_count = len(filtered) + if is_setup_cell: + cells.append( + { + "cell_index": i, + "line_count": 0, + "preview": "", + } + ) + continue total += line_count if line_count == 0: continue @@ -280,6 +326,180 @@ def _extract_notebook_validation_stats(path: Path) -> NotebookValidationStats: ) +def _normalize_port_line(raw: str, *, matlab: bool) -> str: + text = raw.strip() + if not text: + return "" + if matlab: + text = re.sub(r"%.*$", "", text).strip() + else: + text = re.sub(r"#.*$", "", text).strip() + if not text: + return "" + text = text.replace("...", " ") + text = text.lower() + text = re.sub(r"\s+", " ", text).strip() + return text + + +def _line_tokens(line: str) -> set[str]: + return {tok for tok in IDENT_RE.findall(line) if len(tok) > 1} + + +def _matlab_port_lines(path: Path) -> list[str]: + if not path.exists(): + return [] + out: list[str] = [] + for raw in path.read_text(encoding="utf-8", errors="ignore").splitlines(): + stripped = raw.strip() + if not stripped or stripped.startswith("%"): + continue + line = _normalize_port_line(raw, matlab=True) + if line: + out.append(line) + return out + + +def _notebook_port_lines(path: Path) -> list[str]: + out: list[str] = [] + for cell in _load_notebook_cells(path): + if cell.get("cell_type") != "code": + continue + src_raw = cell.get("source", "") + if isinstance(src_raw, list): + src = "".join(str(part) for part in src_raw) + else: + src = str(src_raw) + for raw in src.splitlines(): + stripped = raw.strip() + if not stripped or stripped.startswith("#"): + continue + if stripped.startswith(NOTEBOOK_LINE_PORT_IGNORE_PREFIXES): + continue + if any(pat in stripped for pat in NOTEBOOK_LINE_PORT_IGNORE_PATTERNS): + continue + matlab_call = MATLAB_LINE_CALL_RE.match(stripped) + if matlab_call: + try: + literal = ast.literal_eval(matlab_call.group(1)) + except Exception: # pragma: no cover - defensive parser fallback + literal = None + if isinstance(literal, str): + line = _normalize_port_line(literal, matlab=True) + if line: + out.append(line) + continue + + # Parse exported snapshot rows like "foo();", into MATLAB-equivalent lines. + if stripped[:1] in {"'", '"'}: + candidate = stripped[:-1] if stripped.endswith(",") else stripped + try: + literal = ast.literal_eval(candidate) + except Exception: + literal = None + if isinstance(literal, str): + line = _normalize_port_line(literal, matlab=True) + if line: + out.append(line) + continue + + line = _normalize_port_line(raw, matlab=False) + if line: + out.append(line) + return out + + +def _line_similarity(a: set[str], b: set[str]) -> float: + if not a or not b: + return 0.0 + inter = len(a & b) + union = len(a | b) + if union == 0: + return 0.0 + return float(inter / union) + + +def _ordered_fuzzy_match_count( + matlab_lines: list[str], + python_lines: list[str], + *, + min_score: float = 0.60, + lookahead: int = 160, +) -> int: + if not matlab_lines or not python_lines: + return 0 + py_tokens = [_line_tokens(line) for line in python_lines] + m_tokens = [_line_tokens(line) for line in matlab_lines] + py_idx = 0 + matches = 0 + py_count = len(py_tokens) + for m_line, m_tok in zip(matlab_lines, m_tokens): + if not m_line: + continue + if not m_tok: + end = min(py_count, py_idx + lookahead) + for cand_idx in range(py_idx, end): + if python_lines[cand_idx] == m_line: + matches += 1 + py_idx = cand_idx + 1 + break + continue + end = min(py_count, py_idx + lookahead) + exact_idx = -1 + for cand_idx in range(py_idx, end): + if python_lines[cand_idx] == m_line: + exact_idx = cand_idx + break + if exact_idx >= 0: + matches += 1 + py_idx = exact_idx + 1 + continue + best_idx = -1 + best_score = 0.0 + for cand_idx in range(py_idx, end): + score = _line_similarity(m_tok, py_tokens[cand_idx]) + if score > best_score: + best_idx = cand_idx + best_score = score + if score >= 0.999: + break + if best_idx >= 0 and best_score >= min_score: + matches += 1 + py_idx = best_idx + 1 + return matches + + +def _function_names(lines: list[str]) -> set[str]: + names: set[str] = set() + for line in lines: + for name in CALL_RE.findall(line): + if name in {"if", "for", "while", "switch", "catch", "try"}: + continue + names.add(name) + return names + + +def _compute_line_port_stats(matlab_file: Path, python_nb: Path) -> LinePortStats: + matlab_lines = _matlab_port_lines(matlab_file) + python_lines = _notebook_port_lines(python_nb) + matched = _ordered_fuzzy_match_count(matlab_lines, python_lines) + coverage = float(matched / max(len(matlab_lines), 1)) + matlab_funcs = _function_names(matlab_lines) + python_funcs = _function_names(python_lines) + common_funcs = matlab_funcs & python_funcs + func_recall = float(len(common_funcs) / max(len(matlab_funcs), 1)) + return LinePortStats( + matlab_line_count=len(matlab_lines), + python_line_count=len(python_lines), + matched_line_count=matched, + line_port_coverage=coverage, + line_port_function_recall=func_recall, + matlab_function_count=len(matlab_funcs), + python_function_count=len(python_funcs), + common_function_count=len(common_funcs), + ) + + def _collect_matlab_reference_images(help_root: Path, topic: str) -> list[str]: topic_lower = topic.lower() found: list[Path] = [] @@ -431,6 +651,7 @@ def main() -> int: matlab_stats = _extract_matlab_code_stats(matlab_file) notebook_stats = _extract_notebook_code_stats(python_nb) notebook_validation = _extract_notebook_validation_stats(python_nb) + line_port = _compute_line_port_stats(matlab_file, python_nb) reference_images = [ _portable_path(Path(path), root=matlab_root) @@ -453,12 +674,16 @@ def main() -> int: if not matlab_file.exists() or not python_nb.exists(): alignment_status = "missing_artifact" + strict_line_status = "missing_artifact" elif matlab_stats.total_code_lines == 0 and notebook_stats.total_code_lines == 0: alignment_status = "doc_only" + strict_line_status = "doc_only" elif matlab_stats.total_code_lines == 0 and notebook_stats.total_code_lines > 0: alignment_status = "matlab_doc_only" + strict_line_status = "matlab_doc_only" elif matlab_stats.total_code_lines > 0 and notebook_stats.total_code_lines == 0: alignment_status = "missing_executable_content" + strict_line_status = "missing_executable_content" else: if ( notebook_validation.has_topic_checkpoint @@ -469,6 +694,21 @@ def main() -> int: else: alignment_status = "pending_manual_review" + if ( + line_port.line_port_coverage >= 0.55 + and line_port.line_port_function_recall >= 0.45 + and 0.70 <= float(notebook_stats.total_code_lines / max(matlab_stats.total_code_lines, 1)) <= 1.50 + ): + strict_line_status = "line_port_verified" + elif ( + line_port.line_port_coverage >= 0.35 + and line_port.line_port_function_recall >= 0.30 + and 0.40 <= float(notebook_stats.total_code_lines / max(matlab_stats.total_code_lines, 1)) <= 2.50 + ): + strict_line_status = "line_port_partial" + else: + strict_line_status = "line_port_gap" + line_ratio = ( float(notebook_stats.total_code_lines / matlab_stats.total_code_lines) if matlab_stats.total_code_lines > 0 @@ -486,6 +726,15 @@ def main() -> int: "python_to_matlab_line_ratio": line_ratio, "matlab_code_blocks": matlab_stats.blocks, "python_code_cells": notebook_stats.cells, + "strict_line_status": strict_line_status, + "line_port_coverage": line_port.line_port_coverage, + "line_port_function_recall": line_port.line_port_function_recall, + "line_port_matched_lines": line_port.matched_line_count, + "line_port_matlab_lines": line_port.matlab_line_count, + "line_port_python_lines": line_port.python_line_count, + "line_port_matlab_function_count": line_port.matlab_function_count, + "line_port_python_function_count": line_port.python_function_count, + "line_port_common_function_count": line_port.common_function_count, "has_topic_checkpoint": notebook_validation.has_topic_checkpoint, "assertion_count": notebook_validation.assertion_count, "has_plot_call": notebook_validation.has_plot_call, @@ -544,6 +793,15 @@ def main() -> int: 1 for row in example_rows if row["alignment_status"] == "matlab_doc_only" ), "doc_only_topics": sum(1 for row in example_rows if row["alignment_status"] == "doc_only"), + "strict_line_verified_topics": sum( + 1 for row in example_rows if row["strict_line_status"] == "line_port_verified" + ), + "strict_line_partial_topics": sum( + 1 for row in example_rows if row["strict_line_status"] == "line_port_partial" + ), + "strict_line_gap_topics": sum( + 1 for row in example_rows if row["strict_line_status"] == "line_port_gap" + ), }, "topic_rows": example_rows, }, @@ -563,6 +821,12 @@ def main() -> int: f"topics={len(example_rows)}, pending_manual_review=" f"{sum(1 for row in example_rows if row['alignment_status'] == 'pending_manual_review')}" ) + print( + "Strict line-port audit: " + f"verified={sum(1 for row in example_rows if row['strict_line_status'] == 'line_port_verified')}, " + f"partial={sum(1 for row in example_rows if row['strict_line_status'] == 'line_port_partial')}, " + f"gap={sum(1 for row in example_rows if row['strict_line_status'] == 'line_port_gap')}" + ) return 0 diff --git a/tools/reports/generate_validation_pdf.py b/tools/reports/generate_validation_pdf.py index 20574ed5..ecef9f50 100755 --- a/tools/reports/generate_validation_pdf.py +++ b/tools/reports/generate_validation_pdf.py @@ -54,7 +54,7 @@ def _require_reportlab() -> tuple[tuple[float, float], type, type]: "reportlab is required to build validation PDFs. " "Install notebook extras with `pip install -e .[notebooks]`." ) - return letter, ImageReader, canvas + return letter, ImageReader, canvas.Canvas @dataclass(slots=True)