if-statement: how to rewrite in matlab - matlab

I'm newby in Matlab. I have took the work code with complex if-statement condition and need to rewrite it. This code should prepare some initial data to solve an optimization task. This if-statement condition looks like:
x=[784.8 959.2 468 572 279 341 139.5 170.5 76.5 93.5 45 55];
a=nchoosek(x,6); % all possible combinations from 6 elements of x
n=length(a);
q=[];
for i=1:n
if( ((a(i,1)==x(1)) & (a(i,2)==x(2))) |
((a(i,1)==x(3)) & (a(i,2)==x(4))) |
((a(i,1)==x(5)) & (a(i,2)==x(6))) |
((a(i,1)==x(7)) & (a(i,2)==x(8))) |
((a(i,2)==x(3)) & (a(i,3)==x(4))) |
((a(i,2)==x(5)) & (a(i,3)==x(6))) |
((a(i,2)==x(7)) & (a(i,3)==x(8))) |
((a(i,3)==x(3)) & (a(i,4)==x(4))) |
((a(i,3)==x(5)) & (a(i,4)==x(6))) |
((a(i,3)==x(7)) & (a(i,4)==x(8))) |
((a(i,3)==x(9)) & (a(i,4)==x(10)))|
((a(i,4)==x(5)) & (a(i,5)==x(6))) |
((a(i,4)==x(7)) & (a(i,5)==x(8))) |
((a(i,4)==x(9)) & (a(i,5)==x(10)))|
((a(i,5)==x(5)) & (a(i,6)==x(6))) |
((a(i,5)==x(7)) & (a(i,6)==x(8))) |
((a(i,5)==x(9)) & (a(i,6)==x(10)) |
((a(i,5)==x(11)) & (a(i,6)==x(12)))))
q(i,:)=a(i,:);
end;
end;
q;
R1=a-q;
R1(~any(R1,2),:) = [];
R1(:, ~any(R1)) = [];
Question: Could anyone give an idea how to rewrite if-statement to improve readability of code?

If I understood you correctly, what the convoluted if statement basically says
If "x(1) x(2)" or "x(3) x(4)" or ... "x(11) x(12)" appears anywhere consecutively in row i
Think about it:
((a(i,1)==x(1)) & (a(i,2)==x(2))) | ((a(i,1)==x(3)) & (a(i,2)==x(4))) |
((a(i,1)==x(5)) & (a(i,2)==x(6))) | ((a(i,1)==x(7)) & (a(i,2)==x(8)))
is no different from:
((a(i,1)==x(1)) & (a(i,2)==x(2))) | ((a(i,1)==x(3)) & (a(i,2)==x(4))) |
((a(i,1)==x(5)) & (a(i,2)==x(6))) | ((a(i,1)==x(7)) & (a(i,2)==x(8))) |
((a(i,1)==x(9)) & (a(i,2)==x(10))) | ((a(i,1)==x(11)) & (a(i,2)==x(12)))
since [x(9) x(10)] and [x(11) x(12)] will never appear at a(i, 1:2), so the line I added is always false and does not change the result of the chain of OR's. But if makes the logic much easier to understand. Same logic applies to a(i,2:3), a(i,3:4)..., complete those cases too and then you will get the first statement I made in this answer.
Then, instead of generating a directly from x, you should generate a from the INDEX of x, i.e. [1:12], as such:
a = nchoosek(1:length(x), 6);
Why? You said x consists of real numbers, and using == on real numbers does not guarantee success, and is a very bad practice in general.
Then, your target becomes:
find if sequence `[1 2]` or `[3 4]` or `[5 6]` ... exists in each line of `a`
which is equivalent to:
find if there is any odd number n followed by n+1
This logic can be represented as:
success = any (mod(a(:,1:end-1), 2) & diff(a,1,2)==1, 2)
Now success(i) will be true/false for the every a(i) that your statement evaluates to the same value. This method is better than your statement because it is very concise, automatically adapts to different sizes of x and does not need to run in a loop.
And if you want to get the actual combination of x values, just do
x(a(i)); % Get the ith permutation of x
x(a); % Get all permutation of x
x(a(success,:)); % Get all permutation of x that satisfy the requirement.
EDIT:
q = a; % q is basically a copy of a
q(~success,:) = 0; % except the `non-success` rows are zero
x(q) - x(a) % suppose q and a store index, this will give you the substraction.

Related

Multiple output under minizinc

Try to learn minizinc but after going through examples, may I just confirm that I actually have to write some procedural language if I want to get multiple output or there is a more "natural to minizinc" way to get it.
For example, suppose I want to have all distinct digits add up to 3 the answers should be 0+3 1+2 2+1 3+0 ...
My mininzinc here:
% how to generate more than one result meeting the constraints
int: n=3;
var 0..9: a;
var 0..9: b;
include "alldifferent.mzn";
constraint all_different([a, b]);
constraint a + b = n;
solve satisfy;
output [
"a + b = n \t\n",
show(a), " + ",
show(b), " = ",
show(n)];
produce only 3+0. How to get to the other answers? Thanks for any advice in advance.
I looked at a post for minizinc 1.6 and it seemed to say left out the output statement would produce all the output (Easy way to print full solution (all decision variables) in minizinc). It does not work. Only one is output.
First of all, the default is to print all variables and their values for a solution, not all solutions.
Use the option -a to get all solutions. mzn-gecode --help to see all options. In your case mzn-gecode -a test.mzn which gives:
a + b = n
3 + 0 = 3
----------
a + b = n
0 + 3 = 3
----------
a + b = n
2 + 1 = 3
----------
a + b = n
1 + 2 = 3
----------
==========
Under configuration there is an option to change the default from printing the first solution after satisfaction. Change it to user-defined-behaviour: print all solutions ... You can have output statement, btw, as well.

Remove '#NA' from Matlab Table

I have the following code to clean table data of error terms:
errorTerms={'#NA', '#NA', 'ActiveX VT_ERROR: '};
inputData=readtable(inputFile,'TreatAsEmpty',errorTerms);
However '#NA' terms remain.
I can get rid of them in this way:
inputData.GICS1=strrep(inputData.GICS1,'#NA','NaN');
But this requires several independent loops as I have many tables of different sizes.
Is there a more elegant way to import this data as tables? Or clean it?
The data looks like this:
Id Avg GICS1
a 3.0 #NA
b 5.6 Consumer Staples
c 4.8 Materials
d 3.1 Health Care
e 1.6 Energy
f 9.3 #NA
g 8.5 Industrials
h 7.0 Consumer Discretionary
You can use varfun to go through your table columns and apply a regex to each column using regexrep and your errorTerms array:
inputData = readtable('test.xlsx');
errorTerms = {'#NA', '#NA', 'ActiveX VT_ERROR: '};
expression = sprintf('(%s)', strjoin(errorTerms, '|'));
% Explicit loop
varnames = inputData.Properties.VariableNames;
for ii = 1:length(varnames)
try
inputData.(varnames{ii}) = regexprep(inputData.(varnames{ii}), expression, 'NaN');
catch err
switch err.identifier
case 'MATLAB:UndefinedFunction'
% Do nothing, skip loop iteration
otherwise
rethrow(err)
end
end
end
% % Only works for string data
% varnames = inputData.Properties.VariableNames;
% inputData = varfun(#(x) regexprep(x, expression, 'NaN'), inputData);
% inputData.Properties.VariableNames = varnames; % Variable names overwritten by varfun, set them back
Edit: I have added a try/catch block to account for mixed data types in your columns. I will caveat that this is a fairly greedy implementation, a more robust method would be to compare the error message to make sure regexprep is what is causing the issue but I'm lazy.

Multiple queries (OR statements) using find

Currently to perform multiple queries using find, I invoke each query individually separated by |
index = find(strcmp(data{:,{'type'}},'A') | strcmp(data{:,{'type'}},'B') | strcmp(data{:,{'type'}},'C') | strcmp(data{:,{'type'}},'D'));
To find all rows that where the field 'type' contains either A, B, C or D.
data is held in a table hence the usage of }.
Is there a more concise way of doing this without the need to specify the query in full each time?
You could use ismember instead of multiple uses of strcmp.
index = find(ismember(data{:,{'type'}}, {'A','B','C','D'}));
An alternative (because ismember will probably be slower than multiple uses of strcmp) would be to factor out the repeated code -
x = data{:, {'type'}}; %# This isn't valid MATLAB but whatever...
index = find(strcmp(x,'A') | strcmp(x,'B') | strcmp(x,'C') | strcmp(x,'D'));
You could also use multiple lines for readability
x = data{:, {'type'}}; %# This isn't valid MATLAB but whatever...
index = find(strcmp(x,'A') ...
| strcmp(x,'B') ...
| strcmp(x,'C') ...
| strcmp(x,'D'));

Convert matlab symbol to array of products

Can I convert a symbol that is a product of products into an array of products?
I tried to do something like this:
syms A B C D;
D = A*B*C;
factor(D);
but it doesn't factor it out (mostly because that isn't what factor is designed to do).
ans =
A*B*C
I need it to work if A B or C is replaced with any arbitrarily complicated parenthesized function, and it would be nice to do it without knowing what variables are in the function.
For example (all variables are symbolic):
D = x*(x-1)*(cos(z) + n);
factoring_function(D);
should be:
[x, x-1, (cos(z) + n)]
It seems like a string parsing problem, but I'm not confident that I can convert back to symbolic variables afterwards (also, string parsing in matlab sounds really tedious).
Thank you!
Use regexp on the string to split based on *:
>> str = 'x*(x-1)*(cos(z) + n)';
>> factors_str = regexp(str, '\*', 'split')
factors_str =
'x' '(x-1)' '(cos(z) + n)'
The result factor_str is a cell array of strings. To convert to a cell array of sym objects, use
N = numel(factors_str);
factors = cell(1,N); %// each cell will hold a sym factor
for n = 1:N
factors{n} = sym(factors_str{n});
end
I ended up writing the code to do this in python using sympy. I think I'm going to port the matlab code over to python because it is a more preferred language for me. I'm not claiming this is fast, but it serves my purposes.
# Factors a sum of products function that is first order with respect to all symbolic variables
# into a reduced form using products of sums whenever possible.
# #params orig_exp A symbolic expression to be simplified
# #params depth Used to control indenting for printing
# #params verbose Whether to print or not
def factored(orig_exp, depth = 0, verbose = False):
# Prevents sympy from doing any additional factoring
exp = expand(orig_exp)
if verbose: tabs = '\t'*depth
terms = []
# Break up the added terms
while(exp != 0):
my_atoms = symvar(exp)
if verbose:
print tabs,"The expression is",exp
print tabs,my_atoms, len(my_atoms)
# There is nothing to sort out, only one term left
if len(my_atoms) <= 1:
terms.append((exp, 1))
break
(c,v) = collect_terms(exp, my_atoms[0])
# Makes sure it doesn't factor anything extra out
exp = expand(c[1])
if verbose:
print tabs, "Collecting", my_atoms[0], "terms."
print tabs,'Seperated terms with ',v[0], ', (',c[0],')'
# Factor the leftovers and recombine
c[0] = factored(c[0], depth + 1)
terms.append((v[0], c[0]))
# Combines trivial terms whenever possible
i=0
def termParser(thing): return str(thing[1])
terms = sorted(terms, key = termParser)
while i<len(terms)-1:
if equals(terms[i][1], terms[i+1][1]):
terms[i] = (terms[i][0]+terms[i+1][0], terms[i][1])
del terms[i+1]
else:
i += 1
recombine = sum([terms[i][0]*terms[i][1] for i in range(len(terms))])
return simplify(recombine, ratio = 1)

Simplify Boolean expression with De Morgan's laws

I need to simplify this Boolean expression with De Morgan's laws.
¬c xor (¬b ∨ c)
Could someone help me?
(accidentally made two accounts, so just responding with this one)
Ive found the best way to visualize a logic formula you do not understand is to make a table for it.
In the case of XOR, it represents One variable or another, but not both. So, lets make a table for A XOR B
A | B | Result
T | T | F *1
T | F | T *2
F | T | T *3
F | F | F *4
To generate the smallest possible result from the above table we can first take the most complex result that takes into account each option. Converting each line into a logical statement is fairly easy.
First, throw out anything that results in a False, Then take those that result in true, and convert them into a logical statement separated by 'OR's. In this case, 1 and 4 are false, and 2 and 3 are true. This means we only need to create logical statements for 2 and 3. I think how to do so would be best explained by example
Lets say X, Y, and Z are our variables, and the table gave us the following rows as true:
T | T | F - X & Y & ¬Z
F | T | F - ¬X & Y & ¬Z
F | F | F - ¬X & ¬Y & ¬Z
then to complete, we simply 'OR' them together
(X & Y & ¬Z) V (¬X & Y & ¬Z) V (¬X & ¬Y & ¬Z)
as you can see, where the variable is true, you put the variable directly in, and where it is false, you put a '¬' before the variable. The statement above basically says...
(True when X=T,Y=T,Z=F: False otherwise) OR (True when X=F,Y=T,Z=F: False otherwise) OR (True when X=F,Y=F,Z=F: False otherwise)
So finally bringing it back to our XOR the table rows are...
*2 A & ¬B
*3 ¬A & B
and are combined to be...
(A & ¬B) V (¬A & B)
So, now that you have an explanation of what to do with xor, you can apply this example to your problem, and come up with a logical statement you can use De Morgan's laws on to simplify.
first you have to split up xor into its basic form.
XOR represents A or B where A != B. If you can do that you should have more luck using demorgans on the whole equation