J: Gauss-Jordan elimination - j

Task to code Gauss-Jordan method of solving linear system of algebraic equations is an exercise that I've selected to advance in learning J. System is Ax=b, where A is n-by-n matrix, b and unknown x are n-vectors. Firstly, I've started with the simplest form with control structures:
gj0 =: dyad :0 NB. usage is same to %.
y=.y,.b
for_d.i.#y do.
for_r.i.#y do.
if.r=d do.continue.end. NB. do not eliminate d'th row
t=.%/ (<"1(r,d),:(d,d)) { y
for_c.d}.>:i.#y do.
y=.(((<r,c){y)-(t*(<d,c){y)) (<r,c)} y
end.
y=.0 (<r,d)} y NB. ensure zero
end.
end. NB. now A is diagonal but not identity matrix, so:
x=.{:"1 y NB. x = b
for_r.i.#y do.
x=.((r{x)%(<r,r){y) r} x NB. divide by coefficients on diagonal
end.
)
Ab =: (".;._2) 0 :0
0.25 _0.16 _0.38 0.17
0.19 _0.22 _0.02 0.41
0.13 0.08 _0.08 _0.13
0.13 _0.1 _0.32 0.65
)
b =: 0.37 0.01 0.01 1.51
(,.".&.>)('A';'b';'gj0 A,.b';'b %. A')
┌────────┬──────────────────────┐
│A │0.25 _0.16 _0.38 0.17│
│ │0.19 _0.22 _0.02 0.41│
│ │0.13 0.08 _0.08 _0.13│
│ │0.13 _0.1 _0.32 0.65│
├────────┼──────────────────────┤
│b │0.37 0.01 0.01 1.51 │
├────────┼──────────────────────┤
│b gj0 A │_1 3 _2 2 │
├────────┼──────────────────────┤
│b %. A │_1 3 _2 2 │
└────────┴──────────────────────┘
Correct! Next I've decided to get rid of as many control structures as possible:
gj1 =:dyad :0
y=.y,.b
for_d.i.#y do.
for_r.d ({.,]}.~[:>:[) i.#y do. NB. for indices without d
t=.%/ (<"1(r,d),:(d,d)) { y
y=.((r{y)-(t*d{y)) r}y NB. no need to iterate for each column
y=.0 (<r,d)} y
end.
end.
({:"1 y)%(+/}:"1 y) NB. b divide by sum of each item of A (drop zeroes)
)
b gj1 A
_1 3 _2 2
OK, Now I can try to translate for_r.-loop into tacit form... but it seems like it will look more cumbersome and I think that I'm at a wrong way -- but what's study without mistakes? I really want to code Gauss-Jordan method tacitly to:
exercise in J coding
see if it is better in performance
try to understand code a few weeks later :)
Help me please write it to the end or point out a better approach.

Thanks to Eelvex, who advised me to look in addons/math/misc/linear.ijs, I've concluded the task with this nice code:
gj=: monad :0
I=. i.#y
for_i. I do. y=. y - (col - i=I) */ (i{y) % i{col=. i{"1 y end.
)
gj Ab
1 0 0 0 _1
0 1 0 0 3
0 0 1 0 _2
0 0 0 1 2
It has taken some time to understand verb pivot in linear.ijs - but pencil-paper method helps.

Related

Python 3.6 adjacency Matrix: How to obtain it in a better way

The problem starts with a classical csv file. An example can be:
date;origing;destiny;minutes;distance
19-02-2020;A;B;36;4
20-02-2020;A;B;33;4
24-02-2020;B;A;37;4
25-02-2020;A;C;20;7
27-02-2020;C;B;20;3
28-02-2020;A;B;37.2;4
28-02-2020;A;Z;44;10
My first idea consist in solving it in a classical programmaing way:
Loop + counter variables and represent de counter variables in a matrix like:
A B C Z
A 0 3 1 1
B 1 0 0 0
C 0 1 0 0
Z 0 0 0 0
My first question is if there is a better automatic way of implementing this in python instead os use classical programming algorithm based on loops and counters.
and what about obtaining more complex adjacence matrixes like the one that give you for example and average of times in the values?
There are packages like networkx, but you could use the groupby of pandas.
I don't think pandas with groupby is the fastest. I think networkx would be faster, but at least groupby is better than a loop (is my guess).
import pandas as pd
import numpy as np
M = pd.read_csv('../sample_data.csv', sep=';')
M['constant'] = 1
print(M)
date origing destiny minutes distance constant
0 19-02-2020 A B 36.0 4 1
1 20-02-2020 A B 33.0 4 1
2 24-02-2020 B A 37.0 4 1
3 25-02-2020 A C 20.0 7 1
4 27-02-2020 C B 20.0 3 1
5 28-02-2020 A B 37.2 4 1
6 28-02-2020 A Z 44.0 10 1
With groupby we can get counts;
counts = M.groupby(['origing','destiny']).count()[['constant']]
counts
constant
origing destiny
A B 3
C 1
Z 1
B A 1
C B 1
And store those values in a zero matrix
def key_map(key):
a,b = key
return (ord(a) - ord('A'),ord(b)-ord('A'))
will get the indicis, like
counts['constant'].keys().map(key_map).values
and we set those indicis to any values, i do the counts here, but you can use the same groupby to aggregate sum,average, or anything from other columns;
indici = np.array( [tuple(x) for x in counts['constant'].keys().map(key_map).values] )
indici = tuple(zip(*indici))
and store with
Z = np.zeros((26,26))
Z[ indici ] = counts['constant']
I only print first few with
print(Z[:3,:3])
[[0. 3. 1.]
[1. 0. 0.]
[0. 1. 0.]]

How can I make a square matrix from a nested dict?

I am recently using networkx module, and now I am about to get distance data among countries.
So the excel raw data is something like this:
Nat1 Nat2 Y/N
ABW ANT 0
ABW ARG 0
ABW BEK 1
ABW BHS 1
ABW BRA 0
...
ALB COL 0
ALB CYP 1
...
And thanks to GeckStar(Networkx: Get the distance between nodes), I managed to know how the dataset is coded, as a nested dictionary.
The problem is, I am not familiar with the dictionary. If it was a nested list, I can deal with it, but the nested dict... I need help from others.
So I checked what would this give to me if I code like this:
distance = dict(nx.all_pairs_shortest_path_length(graph))
df = pd.DataFrame(list(distance.items()))
df.to_excel("C_C.xlsx")
(FYI,
distance = dict(nx.all_pairs_shortest_path_length(graph))
will calculate a shortest path from a nation to other nation. So if a nation is not connected to the other nation, and needs a detour, it will has a value more than 1.)
Of course, it didn't go well.
0 1
0 ABW {'ABW':0, 'ANT': 1 ..., 'BHS': 2 ...}
1 ANT {'ANT':0, 'ABW': 1 ...}
...
3 BEL {'BEL':0, 'ABW':1, ... 'BHS':4, ...}
...
But I know there should be a way to make those data to a square matrix like this:
ABW ANT ARG BEL BHS ...
ABW 0 0 0 1 2 ...
ANT 0 0 1 0 1 ...
ARG 0 1 0 1 0 ...
BEL 2 0 1 0 4 ...
...
Can you guys enlighten me, please?
Thanks for your time to check this out, and Thank you for your solution in advance.
I just did a walkaround with a list.
dis = dict(nx.all_pairs_shortest_path_length(graph))
Nations = list(dis.keys())
master = [[""]]
for x in Nations:
master[0].append(x)
for Nat1 in dis:
master.append([Nat1])
for Nat2 in Nations:
master[-1].append(dis[Nat1][Nat2])
Thanks for everyone taking care of this problem.
Have a wonderful day!

Dissimilarity Matrix by appending several outputs from a function

v=: ((1 2);(3 4);(0 5);(2 1))
diff=: ([{]) ,. ]
direction_vector=: <"1 #: (-"0 #:(-/"2 #: (>"0 #: (diff))))
distance=: +/"1 #: *: #: (>"2 #:(direction_vector))
I want to get a dissimilarity matrix that looks like
(0 distance v),. (1 distance v),. (2 distance v) ,. (3 distance v)
I tried
i.4 distance v
which gave me an index error
Anyone can help me on this?
Thank you!
You are close, but you have two issues to deal with. One is that you want to complete the calculation of i. 4 before you apply distance (which is why you get the index error). Parenthesis to change the order of calculation are the solution to this.
i. 4 distance v
|index error: diff
| i.4 distance v
(i. 4) distance v
0 0 0 0
The second issue is that you want to apply each atom of i.4 to the whole of v and you do this by using " (rank) to specify 0 (atoms) for the left argument and _ (infinity) for the whole of the right argument.
(i. 4) distance"0 _ v
0 8 10 2
8 0 10 10
10 10 0 20
2 10 20 0

All list items up to, and including, the first repeated item

Consider:
x =. 0 1 2 3 4 1 3 4 99
v =. [ {.~ (>: # i.&1 # (##~. = #\))
v x NB. => 0 1 2 3 4 1
The behavior is correct. But as you can see, v is shamefully verbose. Is there a better solution?
You want the monad ~: (nub sieve):
v =: {.~ 1 + 0 i.~ ~:
x =: 0 1 2 3 4 1 3 4 99
v x
0 1 2 3 4 1
Code review:
Outside code-golf contexts, don't use #\ in place of i.##. It's too cutesy, hard to maintain, and won't be recognized by the special-code optimizer.
Don't assign to the names x, y, u, v, m, or n (except in special circumstances, and always locally in an explicit context).

How to build a scatter graph in excel with average y value for each x value

I am not sure that here is the best place to ask,
but I have summerized my program performance data in an excel file and I want to build a scatter graph.
For each x value I have 6 y values and I want my graph to contain the average of those 6 to each x.
Is there a way to do this in excel?
For example: I have
X Y
1 0.2
1 0
1 0
1 0.8
1 1.4
1 0
2 0.2
2 1.2
2 1
2 2.2
2 0
2 2.2
3 0.8
3 1.6
3 0
3 3.6
3 1.2
3 0.6
For each x I want my graph to contain the average y.
Thanks
Not certain what you want but suggest inserting a column (assumed to be B) immediately between your two existing ones and populating it with:
=AVERAGEIF(A:A,A2,C:C)
then plotting X against those values.
Or maybe better, just subtotal for each change in X with average for Y and plot that.

Resources