How to reduce coefficients to their lowest possible integers using Matlab - Balancing Chemical Equations - matlab

I am attempting to develop a Matlab program to balance chemical equations. I am able to balance them via solving a system of linear equations. Currently my output is a column vector with the coefficients.
My problem is that I need to return the smallest integer values of these coefficients. For example, if [10, 20, 30] was returned. I want [1, 2, 3] to be returned.
What is the best way to accomplish this?
I want this program to be fully autonomous once it is fed a matrix with the linear system. Thus I can not play around with the values, I need to automate this from the code. Thanks!
% Chemical Equation in Matrix Form
Chem = [1 0 0 -1 0 0 0; 1 0 1 0 0 -3 0; 0 2 0 0 -1 0 0; 0 10 0 0 0 -1 0; 0 35 4 -4 0 12 1; 0 0 2 -1 -3 0 2]
%set x4 = 1 then Chem(:, 4) = b and
b = Chem(:, 4); % Arbitrarily set x4 = 1 and set its column equal to b
Chem(:,4) = [] % Delete the x4 column from Chem and shift over
g = 1; % Initialize variable for LCM
x = Chem\b % This is equivalent to the reduced row echelon form of
% Chem | b
% Below is my sad attempt at factoring the values, I divide by the smallest decimal to raise all the values to numbers greater than or equal to 1
for n = 1:numel(x)
g = x(n)*g
M = -min(abs(x))
y = x./M
end
I want code that will take some vector with coefficients, and return an equivalent coefficient vector with the lowest possible integer coefficients. Thanks!

I was able to find a solution without using integer programming. I converted the non-integer values to rational expressions, and used a built-in matlab function to extract the denominator of each of these expressions. I then used a built in matlab function to find the least common multiples of these values. Finally, I multiplied the least common multiple by the matrix to find my answer coefficients.
% Chemical Equation in Matrix Form
clear, clc
% Enter chemical equation as a linear system in matrix form as Chem
Chem = [1 0 0 -1 0 0 0; 1 0 1 0 0 -3 0; 0 2 0 0 -1 0 0; 0 10 0 0 0 -1 0; 0 35 4 -4 0 -12 -1; 0 0 2 -1 -3 0 -2];
% row reduce the system
C = rref(Chem);
% parametrize the system by setting the last variable xend (e.g. x7) = 1
x = [C(:,end);1];
% extract numerator and denominator from the rational expressions of these
% values
[N,D] = rat(x);
% take the least common multiple of the first pair, set this to the
% variable least
least = lcm(D(1),D(2));
% loop through taking the lcm of the previous values with the next value
% through x
for n = 3:numel(x)
least = lcm(least,D(n));
end
% give answer as column vector with the coefficients (now factored to their
% lowest possible integers
coeff = abs(least.*x)

Related

Generating a Matrix from a given vector

Im trying to generate a specific type o matrix in Matlab.
I need to modify it for specific types of data with the following rules:
First I have to choose a grade g (max 6 let's say) then I have to choose the number of elements per row n (max 18) ;
These numbers are the powers of a specific polynomial of grade g ;
The sum per row in the matrix is not allowed to be bigger than the chosen g grade ;
The biggest element per row is the chosen g.
For g = 2, n = 2 the matrix will look like this:
A = [0 0;
0 1;
1 0;
0 2;
2 0;
1 1]
For g = 2, n = 3 the matrix will look like this:
A = [0 0 0;
0 0 1;
0 0 2;
0 1 0;
0 2 0;
1 0 0;
2 0 0;
0 1 1;
1 0 1;
1 1 0]
How can I generate all possible combinations of an array elements?
Ex : given v = [0 1 2];
Result = [0 0 0;
0 0 1;
0 1 0;
0 1 1;
1 0 0;
1 0 1;
1 1 0;
1 1 1;
0 0 2;
0 2 0;
2 0 0;
2 0 1;
2 1 1;
2 1 2;
...]
and so on...
I've tried this with perms, nchoosek, repelem, repmat, for-loops, unique, matrix concatenations, everything but I couldn't be able to find and algorithm.
You can first generate all n permutations of [0..g] with repetition and rearrangement, then select allowed combinations:
% all n permutations of [0..g] (with repetition and rearrangement)
powers = unique(nchoosek(repmat(0:g, 1, n), n), 'row');
% allowed set of powers
powers = powers(sum(powers, 2) <= g, :);
As I already said in comments, the above code is extremely time and memory inefficient. For example when I run it for g=6 and n=9, MATLAB gives the following error:
Error using zeros Requested 23667689815x9 (1587.0GB) array exceeds
maximum array size preference.
...
To reduce memory consumption, you can do the following:
% all n permutations of [0..g] (with repetition)
gPerms = nmultichoosek(0:g, n);
% allowed set of powers
allowed = gPerms(sum(gPerms, 2) <= g, :);
% all permutations of [1..n] (no repetition)
nPerms = perms(1:n);
% all n permutations of [0..g] (with repetition and rearrangement)
arranges = arrayfun(#(i) allowed(:, nPerms(i, :)), ...
1:size(nPerms, 1), 'UniformOutput', false)';
powers = cell2mat(arranges);
% unique set of permutations
powers = unique(powers, 'rows');
In the above code, first n permutations with repetition of g is generated using #knedlsepp's implementation. The filtered to keep only combinations that their sums is less than or equal to g. In next step all rearrangements of these combinations are calculated. It still takes more than 13 seconds here to find 5005 combinations for the g=6 and n=9 case.

How does Y = eye(K)(y, :); replace a "for" loop? Coursera

Working on an assignment from Coursera Machine Learning. I'm curious how this works... From an example, this much simpler code:
% K is the number of classes.
K = num_labels;
Y = eye(K)(y, :);
seems to be a substitute for the following:
I = eye(num_labels);
Y = zeros(m, num_labels);
for i=1:m
Y(i, :)= I(y(i), :);
end
and I have no idea how. I'm having some difficulty Googling this info as well.
Thanks!
Your variable y in this case must be an m-element vector containing integers in the range of 1 to num_labels. The goal of the code is to create a matrix Y that is m-by-num_labels where each row k will contain all zeros except for a 1 in column y(k).
A way to generate Y is to first create an identity matrix using the function eye. This is a square matrix of all zeroes except for ones along the main diagonal. Row k of the identity matrix will therefore have one non-zero element in column k. We can therefore build matrix Y out of rows indexed from the identity matrix, using y as the row index. We could do this with a for loop (as in your second code sample), but that's not as simple and efficient as using a single indexing operation (as in your first code sample).
Let's look at an example (in MATLAB):
>> num_labels = 5;
>> y = [2 3 3 1 5 4 4 4]; % The columns where the ones will be for each row
>> I = eye(num_labels)
I =
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
>> Y = I(y, :)
Y =
% 1 in column ...
0 1 0 0 0 % 2
0 0 1 0 0 % 3
0 0 1 0 0 % 3
1 0 0 0 0 % 1
0 0 0 0 1 % 5
0 0 0 1 0 % 4
0 0 0 1 0 % 4
0 0 0 1 0 % 4
NOTE: Octave allows you to index function return arguments without first placing them in a variable, but MATLAB does not (at least, not very easily). Therefore, the syntax:
Y = eye(num_labels)(y, :);
only works in Octave. In MATLAB, you have to do it as in my example above, or use one of the other options here.
The first set of code is Octave, which has some additional indexing functionality that MATLAB does not have. The second set of code is how the operation would be performed in MATLAB.
In both cases Y is a matrix generated by re-arranging the rows of an identity matrix. In both cases it may also be posible to calculate Y = T*y for a suitable linear transformation matrix T.
(The above assumes that y is a vector of integers that are being used as an indexing variables for the rows. If that's not the case then the code most likely throws an error.)

Quick Assembly of sparse matrix

I have indices
I = [nGrid x 9] matrix % mesh on fine grid (9 point rectangle)
J = [nGrid x 4] matrix % mesh on coarse grid (4 point rectangle)
Here, nGrid is large number depending on the mesh (e.g. 1.e05)
Then I want to do like
R_ref = [4 x 9] matrix % reference restriction matrix from fine to coarse
P_ref = [9 x 4] matrix % reference prolongation matrix from coarse to fine
R = sparse(size) % n_Coarse x n_Fine
P = sparse(size) % n_Fine x n_Coarse
for k = 1 : nGrid % number of elements on coarse grid
R(I(k,:),J(k,:)) = R_ref;
P(J(k,:),I(k,:)) = P_ref;
end
size is predetermined number.
Note that even if there is same index in (I,J), I do not want to accumulate. I just want to put stencils Rref and Pref at each indices respectively.
I know that this is very slow due to the data structure of sparse.
Usually, I use
sparse(row,col,entry,n_row,n_col)
I can use this by manipulate I, J, R_ref, P_ref by bsxfun and repmat.
However, this cannot be done because of the accumulation of sparse function
(if there exists (i,j) such that [row(i),col(i)]==[row(j),col(j)], then R(row(i),row(j)) = entry(i)+entry(j))
Is there any suggestion for this kind of assembly procedure?
Sample code
%% INPUTS
% N and M could be much larger
N = 2^5+1; % number of fine grid in x direction
M = 2^5+1; % number of fine grid in y direction
% [nOx * nOy] == nGrid
nOx = floor((M)/2)+1; % number of coarse grid on x direction
nOy = floor((N)/2)+1; % number of coarse grid on y direction
Rref = [4 4 -1 4 -2 0 -1 0 0
-1 -1 -2 4 4 4 -1 4 4
-1 -1 4 -2 4 -2 4 4 -1
0 4 4 0 0 4 -1 -2 -1]/8;
Pref = [2 1 0 1 0 0 0 0 0
0 0 0 1 1 1 0 1 2
0 0 1 0 1 0 2 1 0
0 2 1 0 0 1 0 0 0]'/2;
%% INDEX GENERATION
tri_ref = reshape(bsxfun(#plus,[0,1,2]',[0,N,2*N]),[],1)';
TRI_ref = reshape(bsxfun(#plus,[0,1]',[0,nOy]),[],1)';
I = reshape(bsxfun(#plus,(1:2:N-2)',0:2*N:(M-2)*N),[],1);
J = reshape(bsxfun(#plus,(1:nOy-1)',0:nOy:(nOx-2)*nOy),[],1);
I = bsxfun(#plus,I,tri_ref);
J = bsxfun(#plus,J,TRI_ref);
%% THIS PART IS WHAT I WANT TO CHANGE
R = sparse(nOx*nOy,N*M);
P = R';
for k = 1 : size(I,1)
R(J(k,:),I(k,:)) = Rref;
P(I(k,:),J(k,:)) = Pref;
end

Feature mapping using multi-variable polynomial

Consider we have a data-matrix of data points and we are interested to map those data points into a higher dimensional feature space. We can do this by using d-degree polynomials. Thus for a sequence of data points the new data-matrix is
I have studied a relevant script (Andrew Ng. online course) that make such a transform for 2-dimensional data points to a higher feature space. However, I could not figure out a way to generalize in arbitrary higher dimensional samples, . Here is the code:
d = 6;
m = size(D,1);
new = ones(m);
for k = 1:d
for l = 0:k
new(:, end+1) = (x1.^(k-l)).*(x2.^l);
end
end
Can we vectorize this code? Also given a data-matrix could you please suggest a way on how we can transform data points of arbitrary dimension to a higher one using a d-dimensional polynomial?
PS: A generalization of d-dimensional data points would be very helpful.
This solution can handle k variables and generate all the terms of a degree d polynomial where k and d are non-negative integers. Most of the code length is due to the combinatoric complexity of generating all the terms of a degree d polynomial in k variables.
It takes an n_obs by k data matrix X where n_obs is the number of observations and k is the number of variables.
Helper function
This function generates all possible rows such that every entry is a non-negative integer and the row sums to a positive integer:
the row [0, 1, 3, 0, 1] corresponds to (x1^0)*(x1^1)*(x2^3)*(x4^0)*(x5^1)
The function (which almost certainly could be written more efficiently) is:
function result = mg_sums(n_numbers, d)
if(n_numbers<=1)
result = d;
else
result = zeros(0, n_numbers);
for(i = d:-1:0)
rc = mg_sums(n_numbers - 1, d - i);
result = [result; i * ones(size(rc,1), 1), rc];
end
end
Initialization code
n_obs = 1000; % number observations
n_vars = 3; % number of variables
max_degree = 4; % order of polynomial
X = rand(n_obs, n_vars); % generate random, strictly positive data
stacked = zeros(0, n_vars); %this will collect all the coefficients...
for(d = 1:max_degree) % for degree 1 polynomial to degree 'order'
stacked = [stacked; mg_sums(n_vars, d)];
end
Final Step: Method 1
newX = zeros(size(X,1), size(stacked,1));
for(i = 1:size(stacked,1))
accumulator = ones(n_obs, 1);
for(j = 1:n_vars)
accumulator = accumulator .* X(:,j).^stacked(i,j);
end
newX(:,i) = accumulator;
end
Use either method 1 or method 2.
Final Step: Method 2 (requires all data in data matrix X is strictly positive (The problem is that if you have 0 elements, the -inf doesn't propagate properly when you call the matrix algebra routines.)
newX = real(exp(log(X) * stacked')); % multiplying log of data matrix by the
% matrix of all possible exponent combinations
% effectively raises terms to powers and multiplies them!
Example Run
X = [2, 3, 5];
max_degree = 3;
The stacked matrix and the polynomial term it represents are:
1 0 0 x1 2
0 1 0 x2 3
0 0 1 x3 5
2 0 0 x1.^2 4
1 1 0 x1.*x2 6
1 0 1 x1.*x3 10
0 2 0 x2.^2 9
0 1 1 x2.*x3 15
0 0 2 x3.^2 25
3 0 0 x1.^3 8
2 1 0 x1.^2.*x2 12
2 0 1 x1.^2.*x3 20
1 2 0 x1.*x2.^2 18
1 1 1 x1.*x2.*x3 30
1 0 2 x1.*x3.^2 50
0 3 0 x2.^3 27
0 2 1 x2.^2.*x3 45
0 1 2 x2.*x3.^2 75
0 0 3 x3.^3 125
If data matrix X is [2, 3, 5] this correctly generates:
newX = [2, 3, 5, 4, 6, 10, 9, 15, 25, 8, 12, 20, 18, 30, 50, 27, 45, 75, 125];
Where the 1st column is x1, 2nd is x2, 3rd is x3, 4th is x1.^2, 5th is x1.*x2 etc...

Matlab calculate 3D similarity transformation. fitgeotrans for 3D

How can I calculate in MatLab similarity transformation between 4 points in 3D?
I can calculate transform matrix from
T*X = Xp,
but it will give me affine matrix due to small errors in points coordinates. How can I fit that matrix to similarity one? I need something like fitgeotrans, but in 3D
Thanks
If I am interpreting your question correctly, you seek to find all coefficients in a 3D transformation matrix that will best warp one point to another. All you really have to do is put this problem into a linear system and solve. Recall that warping one point to another in 3D is simply:
A*s = t
s = (x,y,z) is the source point, t = (x',y',z') is the target point and A would be the 3 x 3 transformation matrix that is formatted such that:
A = [a00 a01 a02]
[a10 a11 a12]
[a20 a21 a22]
Writing out the actual system of equations of A*s = t, we get:
a00*x + a01*y + a02*z = x'
a10*x + a11*y + a12*z = y'
a20*x + a21*y + a22*z = z'
The coefficients in A are what we need to solve for. Re-writing this in matrix form, we get:
[x y z 0 0 0 0 0 0] [a00] [x']
[0 0 0 x y z 0 0 0] * [a01] = [y']
[0 0 0 0 0 0 x y z] [a02] [z']
[a10]
[a11]
[a12]
[a20]
[a21]
[a22]
Given that you have four points, you would simply concatenate rows of the matrix on the left side and the vector on the right
[x1 y1 z1 0 0 0 0 0 0] [a00] [x1']
[0 0 0 x1 y1 z1 0 0 0] [a01] [y1']
[0 0 0 0 0 0 x1 y1 z1] [a02] [z1']
[x2 y2 z2 0 0 0 0 0 0] [a10] [x2']
[0 0 0 x2 y2 z2 0 0 0] [a11] [y2']
[0 0 0 0 0 0 x2 y2 z2] [a12] [z2']
[x3 y3 z3 0 0 0 0 0 0] * [a20] = [x3']
[0 0 0 x3 y3 z3 0 0 0] [a21] [y3']
[0 0 0 0 0 0 x3 y3 z3] [a22] [z3']
[x4 y4 z4 0 0 0 0 0 0] [x4']
[0 0 0 x4 y4 z4 0 0 0] [y4']
[0 0 0 0 0 0 x4 y4 z4] [z4']
S * a = T
S would now be a matrix that contains your four source points in the format shown above, a is now a vector of the transformation coefficients in the matrix you want to solve (ordered in row-major format), and T would be a vector of target points in the format shown above.
To solve for the parameters, you simply have to use the mldivide operator or \ in MATLAB, which will compute the least squares estimate for you. Therefore:
a = S^{-1} * T
As such, simply build your matrix like above, then use the \ operator to solve for your transformation parameters in your matrix. When you're done, reshape T into a 3 x 3 matrix. Therefore:
S = ... ; %// Enter in your source points here like above
T = ... ; %// Enter in your target points in a right hand side vector like above
a = S \ T;
similarity_matrix = reshape(a, 3, 3).';
With regards to your error in small perturbations of each of the co-ordinates, the more points you have the better. Using 4 will certainly give you a solution, but it isn't enough to mitigate any errors in my opinion.
Minor Note: This (more or less) is what fitgeotrans does under the hood. It computes the best homography given a bunch of source and target points, and determines this using least squares.
Hope this answered your question!
The answer by #rayryeng is correct, given that you have a set of up to 3 points in a 3-dimensional space. If you need to transform m points in n-dimensional space (m>n), then you first need to add m-n coordinates to these m points such that they exist in m-dimensional space (i.e. the a matrix in #rayryeng becomes a square matrix)... Then the procedure described by #rayryeng will give you the exact transformation of points, you then just need to select only the coordinates of the transformed points in the original n-dimensional space.
As an example, say you want to transform the points:
(2 -2 2) -> (-3 5 -4)
(2 3 0) -> (3 4 4)
(-4 -2 5) -> (-4 -1 -2)
(-3 4 1) -> (4 0 5)
(5 -4 0) -> (-3 -2 -3)
Notice that you have m=5 points which are n=3-dimensional. So you need to add coordinates to these points such that they are n=m=5-dimensional, and then apply the procedure described by #rayryeng.
I have implemented a function that does that (find it below). You just need to organize the points such that each of the source-points is a column in a matrix u, and each of the target points is a column in a matrix v. The matrices u and v are going to be, thus, 3 by 5 each.
WARNING:
the matrix A in the function may require A LOT of memory for moderately many points nP, because it has nP^4 elements.
To overcome this, for square matrices u and v, you can simply use T=v*inv(u) or T=v/u in MATLAB notation.
The code may run very slowly...
In MATLAB:
u = [2 2 -4 -3 5;-2 3 -2 4 -4;2 0 5 1 0]; % setting the set of source points
v = [-3 3 -4 4 -3;5 4 -1 0 -2;-4 4 -2 5 -3]; % setting the set of target points
T = findLinearTransformation(u,v); % calculating the transformation
You can verify that T is correct by:
I = eye(5);
uu = [u;I((3+1):5,1:5)]; % filling-up the matrix of source points so that you have 5-d points
w = T*uu; % calculating target points
w = w(1:3,1:5); % recovering the 3-d points
w - v % w should match v ... notice that the error between w and v is really small
The function that calculates the transformation matrix:
function [T,A] = findLinearTransformation(u,v)
% finds a matrix T (nP X nP) such that T * u(:,i) = v(:,i)
% u(:,i) and v(:,i) are n-dim col vectors; the amount of col vectors in u and v must match (and are equal to nP)
%
if any(size(u) ~= size(v))
error('findLinearTransform:u','u and v must be the same shape and size n-dim vectors');
end
[n,nP] = size(u); % n -> dimensionality; nP -> number of points to be transformed
if nP > n % if the number of points to be transform exceeds the dimensionality of points
I = eye(nP);
u = [u;I((n+1):nP,1:nP)]; % then fill up the points to be transformed with the identity matrix
v = [v;I((n+1):nP,1:nP)]; % as well as the transformed points
[n,nP] = size(u);
end
A = zeros(nP*n,n*n);
for k = 1:nP
for i = ((k-1)*n+1):(k*n)
A(i,mod((((i-1)*n+1):(i*n))-1,n*n) + 1) = u(:,k)';
end
end
v = v(:);
T = reshape(A\v, n, n).';
end