Given (rectangular) adjacency matrix m, how to construct adjacency list in q language?
In QIdioms wiki I've found solution in the k language which when run through q console with k) command gives me 'vs error:
m:(1 0 1;1 0 1)
k) (^m)_vs &,/m
'vs
Result should be:
0 0 1 1
0 2 0 2
This is what I was able to replicate in q:
k) &,/m
0 2 3 5
q) where raze m
0 2 3 5
k's^ a.k.a. shape verb is missing in q so I just did:
k) (^m)
000b
000b
q) 2 3#0b
000b
000b
Now, since:
q) parse "vs"
k) {x\:y}
I tried unsuccessfully both:
q) (2 3#0b) _vs where raze m
'
q) (2 3#0b) _\: where raze m
'type
Note that QIdioms wiki has q solution for inverse problem: from adj.list to adj.matrix.
You've got errors because original Q idioms are written in k2 - an old version of k which modern kdb+ version don't support. A current version of k is k4 and it's not backwards-compatible with k2.
For instance, X _vs Y where X and Y are integer atoms or lists in old k2 behaved like X vs Y will behave in kdb+ starting from 3.4t 2015.12.13: http://code.kx.com/q/ref/lists/#vs:
Since 3.4t 2015.12.13: For integer types, computes the base representation of Y in the radices X.
Another example. Indeed ^ in k2 was a shape operator, but it is not any longer. In k2 ^m would have returned 2 3 for a matrix m from your example whereas current implementation behaves like q's not null as far as I understand.
Now, back to your original question, "how to construct adjacency list in q language". One way to do it is this:
q)lm:{flip raze(til count x),''where each x}
or
k)lm:{+,/(!#x),''&:'x}
UPDATE: Here's how it works. If we were to build an adjacency list using any "verbose" language we would do something like this:
for i = 0 to <number of rows> - 1 <---- (1)
for j = 0 to <number of columns> - 1 <---- (2)
if M[i;j] <> 0 <---- (3)
print i, j
In an array language like q (1) can be "translated" into til count M because count will return the number of elements at top level, i.e. the number of rows. (2) and (3) combined can be represented by where each M. Indeed, for every row we return positions of non-zero elements. Given an original matrix m we will get:
til count m -> 0 1
where each m -> (0 2; 0 2)
All we need to do is join row and column indexes. We can't use just ,' because it will join 0 with the first 0 2 and 1 with the second 0 2 resulting in (0 0 2; 1 0 2). We need to go one level deeper, joining every element from the left with every element of every element of a nested list (0 2; 0 2) from the right, hence double apostrophes in ,''.
I hope it makes sense now.
Personally, I would not use flip (or + in k), I can't read an adjacency matrix in this form:
0 0 1 1
0 2 0 2
I think this is much more readable:
0 0
0 2
1 0
1 2
But it's up to you of course.
Related
I'm attempting the following as a hobby, not as homework. In Computer Programming with MATLAB: J. Michael Fitpatrick and Akos Ledeczi, there is a practice problem that asks this:
Write a function called alternate that takes two positive integers, n and m, as input arguments (the function does not have to check the format of the input) and returns one matrix as an output argument. Each element of the n-by-m output matrix for which the sum of its indices is even is 1.
All other elements are zero.
A previous problem was similar, and I wrote a very simple function that does what it asks:
function A = alternate(n,m)
A(1:n,1:m)=0;
A(2:2:n,2:2:m)=1;
A(1:2:n,1:2:m)=1;
end
Now my question is, is that good enough? It outputs exactly what it asks for, but it's not checking for the sum. So far we haven't discussed nested if statements or anything of that sort, we just started going over very basic functions. I feel like giving it more functionality would allow it to be recycled better for future use.
Great to see you're learning, step 1 in learning any programming language should be to ensure you always add relevant comments! This helps you, and anyone reading your code. So the first improvement would be this:
function A = alternate(n,m)
% Function to return an n*m matrix, which is 1 when the sum of the indices is even
A(1:n,1:m)=0; % Create the n*m array of zeros
A(2:2:n,2:2:m)=1; % All elements with even row and col indices: even+even=even
A(1:2:n,1:2:m)=1; % All elements with odd row and col indicies: odd+odd=even
end
You can, however, make this more concise (discounting comments), and perhaps more clearly relate to the brief:
function A = alternate(n,m)
% Function to return an n*m matrix, which is 1 when the sum of the indices is even
% Sum of row and col indices. Uses implicit expansion (R2016b+) to form
% a matrix from a row and column array
idx = (1:n).' + (1:m);
% We want 1 when x is even, 0 when odd. mod(x,2) is the opposite, so 1-mod(x,2) works:
A = 1 - mod( idx, 2 );
end
Both functions do the same thing, and it's personal preference (and performance related for large problems) which you should use.
I'd argue that, even without comments, the alternative I've written more clearly does what it says on the tin. You don't have to know the brief to understand you're looking for the even index sums, since I've done the sum and tested if even. Your code requires interpretation.
It can also be written as a one-liner, whereas the indexing approach can't be (as you've done it).
A = 1 - mod( (1:n).' + (1:m), 2 ); % 1 when row + column index is even
Your function works fine and output the desired result, let me propose you an alternative:
function A = alternate(n,m)
A = zeros( n , m ) ; % pre-allocate result (all elements at 0)
[x,y] = meshgrid(1:m,1:n) ; % define a grid of indices
A(mod(x+y,2)==0) = 1 ; % modify elements of "A" whose indices verify the condition
end
Which returns:
>> alternate(4,5)
ans =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
initialisation:
The first line is the equivalent to your first line, but it is the cannonical MATLAB way of creating a new matrix.
It uses the function zeros(n,m).
Note that MATLAB has similar functions to create and preallocate matrices for different types, for examples:
ones(n,m) Create
a matrix of double, size [n,m] with all elements set to 1
nan(n,m) Create a
matrix of double, size [n,m] with all elements set to NaN
false(n,m) Create a
matrix of boolean size [n,m] with all elements set to false
There are several other matrix construction predefined function, some more specialised (like eye), so before trying hard to generate your initial matrix, you can look in the documentation if a specialised function exist for your case.
indices
The second line generate 2 matrices x and y which will be the indices of A. It uses the function meshgrid. For example in the case shown above, x and y look like:
| x = | y = |
| 1 2 3 4 5 | 1 1 1 1 1 |
| 1 2 3 4 5 | 2 2 2 2 2 |
| 1 2 3 4 5 | 3 3 3 3 3 |
| 1 2 3 4 5 | 4 4 4 4 4 |
odd/even indices
To calculate the sum of the indices, it is now trivial in MATLAB, as easy as:
>> x+y
ans =
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
Now we just need to know which ones are even. For this we'll use the modulo operator (mod) on this summed matrix:
>> mod(x+y,2)==0
ans =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
This result logical matrix is the same size as A and contain 1 where the sum of the indices is even, and 0 otherwise. We can use this logical matrix to modify only the elements of A which satisfied the condition:
>> A(mod(x+y,2)==0) = 1
A =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
Note that in this case the logical matrix found in the previous step would have been ok since the value to assign to the special indices is 1, which is the same as the numeric representation of true for MATLAB. In case you wanted to assign a different value, but the same indices condition, simply replace the last assignment:
A(mod(x+y,2)==0) = your_target_value ;
I don't like spoiling the learning. So let me just give you some hints.
Matlab is very efficient if you do operations on vectors, not on individual elements. So, why not creating two matrices (e.g. N, M) that holds all the indices? Have a look at the meshgrid() function.
Than you might be able find all positions with an even sum of indices in one line.
Second hint is that the outputs of a logic operation, e.g. B = A==4, yields a logic matrix. You can convert this to a matrix of zeros by using B = double(B).
Have fun!
I have a set of independent binary random variables (say A,B,C) which take a positive value with some probability and zero otherwise, for which I have generated a matrix of 0s and 1s of all possible combinations of these variables with at least a 1 i.e.
A B C
1 0 0
0 1 0
0 0 1
1 1 0
etc.
I know the values and probabilities of A,B,C so I can calculate E(X) and E(X^2) for each. I want to treat each combination in the above matrix as a new random variable equal to the product of the random variables which are present in that combination (show a 1 in the matrix). For example, random variable Row4 = A*B.
I have created a matrix of the same size to the above, which shows the relevant E(X)s instead of the 1s, and 1s instead of the 0s. This allows me to easily calculate the vector of Expected values of the new random variables (one per combination) as the product of each row. I have also generated a similar matrix which shows E(X^2) instead of E(X), and another one which shows prob(X>0) instead of E(X).
I'm looking for a Matlab script that computes the Covariance matrix of these new variables i.e. taking each row as a random variable. I presume it will have to use the formula:
Cov(X,Y)=E(XY)-E(X)E(Y)
For example, for rows (1 1 0) and (1 0 1):
Cov(X,Y)=E[(AB)(AC)]-E(X)E(Y)
=E[(A^2)BC]-E(X)E(Y)
=E(A^2)E(B)E(C)-E(X)E(Y)
These values I already have from the matrices I've mentioned above. For each Covariance, I'm just unsure how to know which two variables appear in both rows, because for those I will have to select E(X^2) instead of E(X).
Alternatively, the above can be written as:
Cov(X,Y)=E(X)E(Y)*[1/prob(A>0)-1]
But the problem remains as the probabilities in the denominator will only be the ones of the variables which are shared between two combinations.
Any advice on how automate the computation of the Covariance matrix in Matlab would be greatly appreciated.
I'm pretty sure this is not the most efficient way to do that but that's a start:
Assume r1...n the combinations of the random variables, R is the matrix:
A B C
r1 1 0 0
r2 0 1 0
r3 0 0 1
r4 1 1 0
If you have the vector E1, E2 and ER as:
E1 = [E(A) E(B) E(C) ...]
E2 = [E(A²) E(B²) E(C²) ...]
ER = [E(r1) E(r2) E(r3) ...]
If you want to compute E(r1,r2) you can:
1) Extract the R1 and R2 columns from R
v1 = R(1,:)
v2 = R(2,:)
2) Sum both vectors in vs
vs = v1 + v2
3) Loop in vs, if you see a 2 that means the value in R2 has to be used, if you see a 1 it is the value in R1, if it is 0 do not use the value.
4) Using the loop, compute your E(r1,r2) as wanted.
I'm trying to get a logical matrix as a result of a condition that is specific for each column M(:,i) of the original matrix, based on the value of the same index i in vector N, that is, N(i).
I have looked this up online, but can't find anything quite like it. There must be a simple and clean way of doing this.
M =
3 -1 100 8
200 2 300 4
-10 0 0 400
N =
4 0 90 7
and my desired solution is, for each column of M(:,i), the values less than N(i):
1 1 0 0
0 0 0 1
1 0 1 0
It's a standard use-case for bsxfun:
O = bsxfun(#lt, M, N)
Here #lt is calling the "less than" function, i.e. it is the function handle to the < operator. bsxfun will then "expand" N along its singleton dimension by applying the function #lt to each row of M and the whole of N.
Note that you can easily achieve the same thing using a for-loop:
O = zeros(size(M));
for row = 1:size(M,1)
O(row,:) = M(row,:) < N;
end
Or by using repmat:
O = M < repmat(N, size(M,1), 1);
but in MATLAB the bsxfun is usually the most efficient.
Possible two-line solution using arrayfun to apply the comparison to each column and index pair:
T = arrayfun(#(jj)M(:,jj) < N(jj), 1:numel(N), 'UniformOutput', false);
result = cat(2,T{:});
Edit: Of course, the bsxfun solution is much more efficient.
A matrix has m rows and n columns (n being a number not exceeding 10), and the nth column contains either 1 or 0 (binary). I want to use this binary as a decision to take out the associated row (if 1, or otherwise if 0). I understand that this can be done through iteration with the use of the IF conditional.
However, this may become impractical with matrices whose number of rows m gets into the hundreds (up to 1000). What other procedures are available?
You can use logical datatypes for indexing. For example,
M =
1 2 0
4 5 1
7 8 0
M = [1 2 0;4 5 1;7 8 0];
v = (M(:,n) == 1);
M(v,2) = 1;
M =
1 2 0
4 1 1
7 8 0
Now you have set all the elements in column 2 to 1 if the corresponding element in column n is true.
Note that the v = (M(:,n) == 1) converts the nth column to a logical vector. You can accomplish the same with v = logical(M(:,n));
I would recommend this blog entry for a detailed look at logical indexing.
Update:
If you want to erase rows, then use:
M(v,:) = [];
From the exercises in a book I am using to learn MATLAB:
Given x = [3 15 9 12 -1 0 -12 9 6 1],
provide the command(s) that will
A) set the values of x that are
positive to zero
B) set values that are multiples of 3
to 3 (rem will help here)
C) multiply the values of x that are
even by 5
D) extract the values of x that are
greater than 10 into a vector called
y
E) set the values in x that are less
than the mean to zero
F) set the values in x that are above the mean to their difference from the mean
Question a) will teach you the following elements:
find a function that returns indexes given a condition, in your case x>0
use indexing in order to set selected values in x to 0
to be continued ...
x = [3 15 9 12 -1 0 -12 9 6 1]
vi = (x < 0) % statement that returns a boolean, gives a vector like
% [0 0 0 0 1 0 1 0 0 0]
x(vi) = -x(vi) % does the operation (negating in this case) on the relevant
% values of x (those with a 1 from above)
Without actually doing your homework, they all follow the above pattern.
I agree with the comments to your question, that is not necessarily the right way to go if you really want to learn something.
As to answer your question, MATLAB has a fantastic function browser I strongly suggest you take a look at it. With well chosen keywords you can go a long way. :)