(* ::Package:: *)

(* ::Section:: *)
(*Choice of symbols*)


(* ::Text:: *)
(*Sample a 0=BAD with probability p*)
(*Sample a 1=GOOD with probability 1-p = q*)
(*Pick a random 0 and resample.*)
(*All-1 state is the final good state.*)


(* ::Text:: *)
(*Goal: compute P(Z^n | start in 011..11) on the finite chain {1,...,n}*)
(*Method: sum over (k>=1) of P(vertex n gets zero for the first time at step k)*)
(*Let M be the Markov Chain on n-1 vertices such that if site n becomes zero, then it goes to a special state with no outgoing arrows*)
(*Furthermore the all-good state has no self-loop (but has incoming paths). This last thing seems like it is not required but removes the 1 eigenvalue so I guess it is good for inverting. Let si be the initial state, let sf be the final special state.*)
(*Goal is to compute sum over (k>=0) of  si . M^k . sf*)
(*This is equal to si . (1 - M)^(-1) . sf*)


(* ::Chapter:: *)
(*Markov Chain Matrix*)


(* Note that the matrix function requires at least n\[GreaterEqual]3 *)
generateInitialState[n_]:=SparseArray[{(1+FromDigits[Join[{0},ConstantArray[1,n-2]],2])->1},{2^(n-1)+1}]
generateFinalState[n_]:=SparseArray[{(2^(n-1)+1)->1},{2^(n-1)+1}]
(* Matrix as described above! *)
generateLeakingMatrix[nReal_,p_]:=Module[{n,mcM,bits,numBads,j1,j2,j3,newbits,prob},
n = nReal-1; (* See description above. When vertex n is resampled it is a special state. *)
mcM=SparseArray[{},{2^n+1,2^n+1},0];

Do[
    (* Compute neighbours of i *)
    bits=IntegerDigits[i,2,n];
    numBads=Count[bits,0];
    (* Special cases: j==1 and j==n *)
    If[bits[[1]]==0, (* j==1 *)
        newbits=bits;
        Do[
            newbits[[{1,2}]]={b1,b2};
            prob=((1-p)^(b1+b2)*(p)^(2-(b1+b2)))/numBads;
            (* Set the matrix entry *)
            mcM[[i+1,FromDigits[newbits,2]+1]]+=prob;
        ,{b1,0,1},{b2,0,1}];
    ];
    If[bits[[n]]==0, (* j==n *)
        (* This 'n' is actually the n-1 vertex. So we check if it sets the real n vertex to zero. *)
        (* Potentially go into special state *)
        newbits=bits;
        Do[
            prob=((1-p)^(b1+b2+b3)*(p)^(3-(b1+b2+b3)))/numBads;
            If[b3==0,
                (* Special state *)
                mcM[[i+1,-1]]+=prob;
            ,
                (* Normal state *)
                newbits[[{n-1,n}]]={b1,b2};
                (* Set the matrix entry *)
                mcM[[i+1,FromDigits[newbits,2]+1]]+=prob;
            ];
        ,{b1,0,1},{b2,0,1},{b3,0,1}];
    ];
    If[n>=3,
    Do[ (* 2 <= j <= n-1 *)
        (* For all sites, check if it is BAD *)
        If[bits[[j]]==0,
            newbits=bits;
            Do[
                newbits[[{j-1,j,j+1}]]={b1,b2,b3};
                prob=((1-p)^(b1+b2+b3)*(p)^(3-(b1+b2+b3)))/numBads;
                (* Set the matrix entry *)
                mcM[[i+1,FromDigits[newbits,2]+1]]+=prob;
            ,{b1,0,1},{b2,0,1},{b3,0,1}];
        ]; (* endif bits[[j]] *)
    ,{j,2,n-1}];];
,{i,0,2^n-1}];

mcM
]/;(nReal>=3)


(* ::Section:: *)
(*Symbolic evaluation*)


(* ::Subsection:: *)
(*Expected number of resamples using inverse method*)


(* This function definition causes automatic memoization of results *)
getIdMinusM[n_]:=getIdMinusM[n]=Module[{M,IdMinusM},
PrintTemporary["Generating matrix (Id-M) for n = ",n," and general p."];
M=generateLeakingMatrix[n,p];
IdMinusM=IdentityMatrix[Length[M],SparseArray]-M;
IdMinusM
]

(* Replacement rules do not work on sparsearrays. Instead we use this, taken shamelessly from stackoverflow *)
ReplaceElement[s_SparseArray, rule_]:=SparseArray[ArrayRules[s]/.rule,Dimensions[s]]

computeFullExpression[n_,pValue_]:=Module[{nSites,si,sf,IdMinusM,inv1},
(*PrintTemporary["Generating initial state for n = ",n,", p = ",pValue];*)
(*PrintTemporary["Getting (Id-M) matrix."];*)
IdMinusM=getIdMinusM[n];
(*PrintTemporary["Entering p into matrix"];*)
IdMinusM=Simplify[ReplaceElement[IdMinusM,p->pValue]];

si=generateInitialState[n];
sf=generateFinalState[n];
PrintTemporary["Solving (Id-M).x = b for n = ",n," , p = ",pValue];
inv1=LinearSolve[IdMinusM,sf];
(*PrintTemporary["Computing final inner product"];*)
Simplify[si.inv1]
]


generateLeakingMatrix[2,p]


(* ::Subsection:: *)
(*Compute and save coefficients*)


(* For n up to 10 it can be done symbolically in p *)
n2function[p_]=computeFullExpression[2,p];
(*
n3function[p_]=computeFullExpression[3,p];
n4function[p_]=computeFullExpression[4,p];
n5function[p_]=computeFullExpression[5,p];
n6function[p_]=computeFullExpression[6,p];
n7function[p_]=computeFullExpression[7,p];
n8function[p_]=computeFullExpression[8,p];
n9function[p_]=computeFullExpression[9,p];
n10function[p_]=computeFullExpression[10,p];
*)





For[i=1,i<500,i++,
pValue=0/1000+i/500;
Rvalue=computeFullExpression[12,pValue];
PutAppend[{12,pValue,Rvalue},"functionValuesN12.m"]
PutAppend[",","functionValuesN12.m"]
]
