Related
I've been trying to use a customized proposal distribution that generates the proposed arrays so we can use them and test or sample them in a Metropolis-Hastings algorithm, with a log_target function, i wrote the metropolis-hastings code manually and it works fine, tho it doesn't give the same satisfactory results as using Klara.jl which is a closed package now. The proposal distribution is written like this
using Distributions
nonneg(v) = all(v.>=0) ? true : false
struct OrthoNNDist <: DiscreteMultivariateDistribution
x0::Vector{Float64}
oc::Array{Float64,2}
x1s::Array
prob::Float64
#return a new uniform distribution with all vectors in x1s orthogonal to oc
function OrthoNNDist(x0::Vector{Float64}, oc::Array{Float64,2})
x1s = []
for i = 1:size(oc)[2]
x1 = x0 + oc[:, i]
if nonneg(x1)
push!(x1s, x1)
end
x1 = x0 - oc[:, i]
if nonneg(x1)
push!(x1s, x1)
end
end
new(x0, oc, x1s, 1.0/length(x1s))
end
end
Base.length(d::OrthoNNDist) = length(d.x0)
Distributions.rand(d::OrthoNNDist) = rand(d.x1s)
Distributions.pdf(d::OrthoNNDist, x::Vector) = x in d.x1s ? d.prob : 0.0
Distributions.pdf(d::OrthoNNDist) = fill(d.prob, size(d.x1s))
Distributions.logpdf(d::OrthoNNDist, x::Vector) = log(pdf(d, x))
you can test it using for example x0=[1.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0, 1.0] and mat = [0 1 0 0 1 1 0 0 1 0 0 0; 1 0 0 0 1 0 0 0 0 0 0 1; 1 0 1 0 0 0 0 1 0 0 1 0; 0 1 0 0 0 0 1 0 0 0 1 0; 0 0 0 1 0 0 0 1 1 0 0 0; 0 0 0 1 0 0 0 0 0 0 0 1; 0 0 1 0 0 1 0 0 0 1 0 0; 0 0 0 0 0 0 1 0 0 1 0 0]. we can fix the mat and let x0 to be the variable by writing for example proposal(x::Vector)= OrthoNNDist(x,mat).
If we have a log_target function in general called logtarget(x::Vector) and using this proposal distribution above and i want to use it in AdaptiveMCMC package or any other package that can be used in this case with a minimum example, i tried AdvancedMH but a part of my target function uses JuMP hence i can't get the gradient of the target, i've read the Mamba documentation but i couldn't understand it exactly i would with a minimum example in this case of a customized proposal function and a target function, AdaptiveMCMC looks more simple but i keep getting some MethodError regarding the distribution it's supposed to be a function but mine it's not as you can see in the code above. i can provide here an example of the code using Klara with comments.
function qrelay(alpha, delta, name)
n = 2
chi = fill(sqrt(0.06), n)
phi = im * tanh(chi)
omega = 1.0 / prod(cosh(chi))^2
syms, op = qrelay_op(n, phi, alpha, delta) #it gives an array
op_a, op_ab, mat, coef = op_mat(op) #array, array, matrice, array of coefficients
op_q2 = [syms.apH[1], syms.apV[1], syms.bpH[end], syms.bpV[end]] #array
op_q1 = [syms.apH[2:end]..., syms.apV[2:end]..., syms.bpH[1:end-1]..., syms.bpV[1:end-1]...] #array
mask_q1 = [op in op_q1 for op in op_a]; #array
mask_q2 = [op in op_q2 for op in op_a]; #array
qq = [x in syms.apH || x in syms.bpV ? 1 : 0 for x in op_a] #array
pdet0 = pdet_maker(0.04, 1e-5) #it gives a probability
qrs = QSampler(mat, coef, omega, pdet0) #calling a module QSampler
targetcache = Dict{Vector{Int}, Float64}()
function plogtarget(na::Vector{Int})
get!(targetcache, na) do
log(qrs.prob(qq, na, mask_q1) * qrs.prob(na))
end
end
# plogtarget(na::Vector{Int}) = log(qrs.prob(qq, na, mask_q1) * qrs.prob(na))
p = BasicDiscMuvParameter(:p, logtarget=plogtarget)
model = likelihood_model([p], isindexed=false)
sampler = MH(qrs.psetproposal, symmetric=false) #this is where the proposal function is called qrs.psetproposal(x::Vector)= qrs.OthoNNDist(x, mat)
mcrange = BasicMCRange(nsteps=2^20 + 2^10, burnin=2^10, thinning=2^5)
v0 = Dict(:p=>zeros(qq))
outopts = Dict{Symbol, Any}(
:monitor=>[:value, :logtarget],
:diagnostics=>[:accept],
:destination=>:iostream,
:filepath=>"$dataname/"*name
)
job = BasicMCJob(model, sampler, mcrange, v0, outopts=outopts)
funcQ(v) = qrs.prob(qq, v, mask_q2) #it returns a probability
return qrs, job, funcQ
end
This piece of code won't work of course because Klara is closed but i just put it here to give you a clearer view of how the code was working before.
This question already has answers here:
How do I get the two last dimensions of an N-D array as a 2D array?
(3 answers)
Closed 7 years ago.
I have a 4D array in matlab something like this,
test(:,:,1,1) =
0
test(:,:,2,1) =
0
test(:,:,3,1) =
0
test(:,:,4,1) =
0
test(:,:,5,1) =
0
test(:,:,1,2) =
0
test(:,:,2,2) =
0
test(:,:,3,2) =
0
test(:,:,4,2) =
0
test(:,:,5,2) =
0
test(:,:,1,3) =
0
test(:,:,2,3) =
0
test(:,:,3,3) =
0
test(:,:,4,3) =
0
test(:,:,5,3) =
0
test(:,:,1,4) =
0
test(:,:,2,4) =
0
test(:,:,3,4) =
0
test(:,:,4,4) =
0
test(:,:,5,4) =
0
test(:,:,1,5) =
0
test(:,:,2,5) =
0
test(:,:,3,5) =
0
test(:,:,4,5) =
0
test(:,:,5,5) =
0
K>> test
test(:,:,1,1) =
0
test(:,:,2,1) =
0
test(:,:,3,1) =
0
test(:,:,4,1) =
0
test(:,:,5,1) =
0
test(:,:,1,2) =
0
test(:,:,2,2) =
0
test(:,:,3,2) =
0
test(:,:,4,2) =
0
test(:,:,5,2) =
0
test(:,:,1,3) =
0
test(:,:,2,3) =
0
test(:,:,3,3) =
0
test(:,:,4,3) =
0
test(:,:,5,3) =
0
test(:,:,1,4) =
0
test(:,:,2,4) =
0
test(:,:,3,4) =
0
test(:,:,4,4) =
0
test(:,:,5,4) =
0
test(:,:,1,5) =
0
test(:,:,2,5) =
0
test(:,:,3,5) =
0
test(:,:,4,5) =
0
test(:,:,5,5) =
0
Now I want to get an array, something like this
ans =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
Is there a simple way to convert the dimentions.
I am very much new to multidimensional arrays in Matlab.
Please let me know if anyone knows of any solution.
Use the squeeze function, it will return a matrix of size [5 4] when you input a matrix of size [1 1 5 4]
I need to pre-allocate arrays in my code below.
I don't quite understand how to pre-allocate arrays in multi-loop iteration.
a=0:1:2;
b=0:1:2;
c=0:1:2;
xx1=[];yy1=[];zz1=[];xx2=[];yy2=[];zz2=[];
for k=1:length(c)-1;
z1=c(k); z2=c(k+1);
for w=1:length(b)-1;
y1=b(w); y2=b(w+1);
for q=1:length(a)-1;
x1=a(q); x2=a(q+1);
xx1=[xx1;x1]; xx2=[xx2;x2];
yy1=[yy1;y1]; yy2=[yy2;y2];
zz1=[zz1;z1]; zz2=[zz2;z2];
end
end
end
The expected results are:
[xx1 xx2 yy1 yy2 zz1 zz2]
ans =
0 1 0 1 0 1
1 2 0 1 0 1
0 1 1 2 0 1
1 2 1 2 0 1
0 1 0 1 1 2
1 2 0 1 1 2
0 1 1 2 1 2
1 2 1 2 1 2
Increase a counter in the innermost loop to keep track of which entry of xx1 etc you should fill.
a = 0:1:2;
b = 0:1:2;
c = 0:1:2;
xx1 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1); %// preallocate
xx2 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1);
yy1 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1);
yy2 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1);
zz1 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1);
zz2 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1);
n = 0; %// initiallize counter
for k=1:length(c)-1;
z1=c(k); z2=c(k+1);
for w=1:length(b)-1;
y1=b(w); y2=b(w+1);
for q=1:length(a)-1;
n = n + 1; %// increase counter;
x1 = a(q);
x2 = a(q+1);
xx1(n) = x1; %// fill values
xx2(n) = x2;
yy1(n) = y1;
yy2(n) = y2;
zz1(n) = z1;
zz2(n) = z2;
end
end
end
Anyway, it can be done without loops, adapting the procedure given in this answer. This has two advantages:
It may be faster if a, b, c are large.
The same code works for any number of vectors, not just 3. Simply define vectors1 and vectors2 accordingly in the code below.
Code without loops:
a = 0:1:2;
b = 0:1:2;
c = 0:1:2;
vectors1 = { a(1:end-1), b(1:end-1), c(1:end-1) };
vectors2 = { a(2:end), b(2:end), c(2:end) };
n = numel(vectors1);
combs1 = cell(1,n);
[combs1{:}] = ndgrid(vectors1{end:-1:1});
combs1 = reshape(cat(n+1, combs1{:}),[],n);
combs2 = cell(1,n);
[combs2{:}] = ndgrid(vectors2{end:-1:1});
combs2 = reshape(cat(n+1, combs2{:}),[],n);
result(:,2:2:2*n) = combs2;
result(:,1:2:2*n) = combs1;
I would like to generate matrix of size (n(n-1)/2, n) that looks like this (n=5 in this case):
-1 1 0 0 0
-1 0 1 0 0
-1 0 0 1 0
-1 0 0 0 1
0 -1 1 0 0
0 -1 0 1 0
0 -1 0 0 1
0 0 -1 1 0
0 0 -1 0 1
0 0 0 -1 1
This is what I, quickly, came up with:
G = [];
for i = 1:n-1;
for j = i+1:n
v = sparse(1,i,-1,1,n);
w = sparse(1,j,1,1,n);
vw = v+w;
G = [G; vw];
end
end
G = full(G);
It works, but is there a faster/cleaner way of doing it?
Use nchoosek to generate the indices of the columns that will be nonzero:
n = 5; %// number of columns
ind = nchoosek(1:n,2); %// ind(:,1): columns with "-1". ind(:,2): with "1".
m = size(ind,1);
rows = (1:m).'; %'// row indices
G = zeros(m,n);
G(rows + m*(ind(:,1)-1)) = -1;
G(rows + m*(ind(:,2)-1)) = 1;
You have two nested loops, which leads to O(N^2) complexity of non-vectorized operations, which is too much for this task. Take a look that your matrix actually has a rectursive pattern:
G(n+1) = [ -1 I(n)]
[ 0 G(n)];
where I(n) is identity matrix of size n. That's how you can express this pattern in matlab:
function G = mat(n)
% Treat original call as G(n+1)
n = n - 1;
% Non-recursive branch for trivial case
if n == 1
G = [-1 1];
return;
end
RT = eye(n); % Right-top: I(n)
LT = repmat(-1, n, 1); % Left-top: -1
RB = mat(n); % Right-bottom: G(n), recursive
LB = zeros(size(RB, 1), 1); % Left-bottom: 0
G = [LT RT; LB RB];
end
And it gives us O(N) complexity of non-vectorized operations. It probably will waste some memory during recursion and matrix composition if Matlab is not smart enought to factor these out. If it is critical, you may unroll recursion into loop and iteratively fill up corresponding places in your original pre-allocated matrix.
Is there a way to insert an element into an array after verifying a certain element value? For example, take
A = [0 0 1 1 0 1 0]
After each 1 in the array, I want to insert another 1 to get
Anew = [0 0 1 1 1 1 0 1 1 0]
However I want a way to code this for a general case (any length 1 row array and the ones might be in any order).
A = [0 0 1 1 0 1 1];
i = (A == 1); % Test for number you want insert after
t = cumsum(i);
idx = [1 (2:numel(A)) + t(1:end-1)];
newSize = numel(A) + sum(i);
N = ones(newSize,1)*5; % Make this number you want to insert
N(idx) = A
Output:
N =
0 0 1 5 1 5 0 1 5 0
I made the inserted number 5 and split things onto multiple lines so it's easy to see what's going on.
If you wanted to do it in a loop (and this is how I would do it in real life where no-one can see me showing off)
A = [0 0 1 1 0 1 0];
idx = (A == 1); % Test for number you want insert after
N = zeros(1, numel(A) + sum(idx));
j = 1;
for i = 1:numel(A)
N(j) = A(i);
if idx(i)
j = j+1;
N(j) = 5; % Test for number you want to insert after
end
j = j+1;
end
N
Output:
N =
0 0 1 5 1 5 0 1 5 0
This code is not the most elegant, but it'll answer your question...
A=[0 0 1 1 0 1 0];
AA=[];
for ii=1:length(A);
AA=[AA A(ii)];
if A(ii)
AA=[AA 1];
end
end
I'm sure there will be also a vectorized way...
This should do the trick:
>> A = [0 0 1 1 0 1 0]
>>
>> sumA = sum(A);
>> Anew = zeros(1, 2*sumA+sum(~A));
>> I = find(A) + (0:sumA-1);
>> Anew(I) = 1;
>> Anew(I+1) = 8.2;
Anew =
0 0 1 8.2 1 8.2 0 1 8.2 0