Using findmin() in data from array composed of mutable struct elements - Julia - arrays

Suppose I have the following struct:
mutable struct Car
load
locale
availability
odometer
end
And I have created an array:
fleet = Vector{Car}(undef, num_cars)
for i in 1:num_cars
a, b, c, d = rand(4)
fleet[i] = Car(a, b, c, d)
end
How can I find the smallest (with findmin or similar) or biggest (with?) value from, for example, the odometer of all the cars in my array?
Basically, I want to be able to use conditional statements with struct arrays, e.g.: For every element in my struct array, get the ones that a data satisfy a condition and, from those, get the minimum value of another data.
Thanks!

Finding the minimum value is pretty straightforward, using the minimum function, with a mapping argument:
julia> minimum(x->x.odometer, fleet)
0.08468003971220694
If you also want the index of the minimum, you can use the findmin function. Unfortunately, this does not, for some reason, support a function argument, so you have to create a temporary array, and apply findmin to that:
julia> findmin(getfield.(fleet, :odometer))
(0.08468003971220694, 1)
You can also use getproperty instead of getfield, they do the same thing for your struct, I'm not certain which is preferable. Probably, the most idiomatic solution would be to define accessor functions instead of using the field values directly. For example:
odometer(car::Car) = car.odometer
minimum(odometer, fleet)
findmin(odometer.(fleet))
For maximum values, use maximum and findmax.

Julia is about performance.
You should avoid using untyped structs hence your type definition should be:
mutable struct Car
load::Float64
locale::Float64
availability::Float64
odometer::Float64
end
The code for creating Vector can be shorter:
cars = [Car(rand(4)...) for _ in 1:5]
The most efficient way to find the index of the minimum element is:
julia> Base.isless(c1::Car,c2::Car) = c1.odometer < c2.odometer
julia> findmin(cs)
(Car(0.7623514815463603, 0.7523019237133661, 0.37422766075278413, 0.49830949323733464), 3)

Related

Creating an Array based on range from existing array

Say I have a std::array
std::array<int,8> foo = {1,2,3,4,5,6,7,8};
Now Is it possible to create a new array from an existing array using a range say from index to 2 till 5. So my new array will have items {3,4,5,6}.
I am aware that I could accomplish this using the manual for loop copy mechanism but I wanted to know if there was a faster way of doing that
If you are expecting some easy syntax (like Python, Matlab or Fortran), no.
As #Sphinx said you can use copy.
std::array<int,8> foo = {1,2,3,4,5,6,7,8};
std::array<int,3> foo2;
std::copy(&foo[2], &foo[5], foo2.begin());
// or std::copy(foo.begin() + 2, foo.begin() + 5, foo2.begin());
but take into account that std::array sizes are compile time constants.
So you may need std::vector<int> if you want make the range size variable.

Minizinc: create an array of int pairs

What is the Minizinc syntax to create an array of n int pairs like this:
{(x1,y1), (x2,y2),....(xn,yn)}
and how can I access to a specific element j to get, for example, its y value?
In MiniZinc you would currently use multi-dimensional arrays for this purpose. If, for example, you want to create n pairs of integer variables you can use:
array [1..n, 1..2] of var int: pairs;
You could then access each pair, but also each element. If, for example, you want to access pair j, then you can use the statement pairs[j]. This is an array of dimensions 1..2; you can access the second element (y), using pairs[j][y].
This approach allows you to use the variables directly, but you can also use pairs for predicates that call for arrays.

How do I set a vector's elements to point to the first element in an array of arrays?

I've been learning Julia by trying to write a simple rigid body simulation, but I'm still somewhat confused about the assignment and mutating of variables.
I'm storing the points making up the shape of a body into an array of arrays where one vector holds the x,y,z coordinates of a point. For plotting the body with PyPlot the points are first transformed from local coordinates into world coordinates and then assigned to three arrays which hold the x, y, and z coordinates for the points respectively. I would like to have the three arrays only reference the array of arrays values instead of having copies of the values.
The relevant part of my code looks like this
type Rigidbody
n::Integer
k::Integer
bodyXYZ::Array{Array{Float64,1},2}
worldXYZ::Array{Array{Float64,1},2}
worldX::Array{Float64,2}
worldY::Array{Float64,2}
worldZ::Array{Float64,2}
Rotmat::Array{Float64,2}
x::Array{Float64,1}
end
# body.worldXYZ[1,1] = [x; y; z]
# and body.worldX[1,1] should be body.worldXYZ[1,1][1]
function body_to_world(body::Rigidbody)
for j in range(1, body.k)
for i in range(1, body.n)
body.worldXYZ[i,j] = body.x + body.Rotmat*body.bodyXYZ[i,j]
body.worldX[i,j] = body.worldXYZ[i,j][1]
body.worldY[i,j] = body.worldXYZ[i,j][2]
body.worldZ[i,j] = body.worldXYZ[i,j][3]
end
end
return nothing
end
After calling the body_to_world() and checking the elements with === they evaluate to true but if I then for example set
body.worldXYZ[1,1][1] = 99.999
the change is not reflected in body.worldX. The problem is probably something trivial but as can be seen from my code, I am a beginner and could use some help.
body.worldX[i,j] = body.worldXYZ[i,j][1]
You're setting a number to a number here. Numbers are not mutable, so body.worldX[i,j] won't refer back to body.worldXYZ[i,j][1]. What you're thinking of is that the value of an array will be a reference, but numbers don't have references, just the value themselves.
However, I would venture to say that if you're doing something like that, you're going about the problem wrong. You should probably be using types somewhere. Remember, types in Julia give good performance, so don't be afraid of them (and immutable types should be almost perfectly optimized after carneval's PR, so there's really no need to be afraid). Instead, I would make world::Array{Point,2} where
immutable Point{T}
x::T
y::T
z::T
end
Then you can get body.world[i,j].x for the x coordinate, etc. And then for free you can use map((i,j)->Ref(body.world[i,j].x),size(body.world)...) to get an array of references to the x's.
Or, you should be adding dispatches to your type. For example
import Base: size
size(RigidBody) = (n,k)
now size(body) outputs (n,k), as though it's an array. You can complete the array interface with getindex and setindex!. This kind of adding dispatches to your type will help clean up the code immensely.

How to "invert" an array in linear time functionally rather than procedurally?

Say I have an array of integers A such that A[i] = j, and I want to "invert it"; that is, to create another array of integers B such that B[j] = i.
This is trivial to do procedurally in linear time in any language; here's a Python example:
def invert_procedurally(A):
B = [None] * (max(A) + 1)
for i, j in enumerate(A):
B[j] = i
return B
However, is there any way to do this functionally (as in functional programming, using map, reduce, or functions like those) in linear time?
The code might look something like this:
def invert_functionally(A):
# We can't modify variables in FP; we can only return a value
return map(???, A) # What goes here?
If this is not possible, what is the best (most efficient) alternative when doing functional programming?
In this context are arrays mutable or immutable? Generally I'd expect the mutable case to be about as straightforward as your Python implementation, perhaps aside from a few wrinkles with types. I'll assume you're more interested in the immutable scenario.
This operation inverts the indices and elements, so it's also important to know something about what constitutes valid array indices and impose those same constraints on the elements. Haskell has a class for index constraints called Ix. Any Ix type is ordered and has a range implementation to make an ordered list of indices ranging from one specified index to another. I think this Haskell implementation does what you want.
import Data.Array.IArray
invertArray :: (Ix x) => Array x x -> Array x x
invertArray arr = listArray (low,high) newElems
where oldElems = elems arr
newElems = indices arr
low = minimum oldElems
high = maximum oldElems
Under the hood listArray uses zipWith and range to associate indices in the specified range to the listed elements. That part ought to be linear time, and so is the one-time operation of extracting elements and indices from an array.
Whenever the sets of the input arrays indices and elements differ some elements will be undefined, which for better or worse blow up faster than Python's None. I believe you could overcome the undefined issue by implementing new Ix a instances over the Maybe monad, for instance.
Quick side-note: check out the invPerm example in the Haskell 98 Library Report. It does something similar to invertArray, but assumes up front that input array's elements are a permutation of its indices.
A solution needing mapand 3 operations:
toTuples views an the array as a list of tuples (i,e) where i is the index and e the element in the array at that index.
fromTuples creates and loads an array from a list of tuples.
swap which takes a tuple (a,b) and returns (b,a)
Hence the solution would be (in Haskellish notation):
invert = fromTuples . map swap . toTuples

Matlab array of struct : Fast assignment

Is there any way to "vector" assign an array of struct.
Currently I can
edges(1000000) = struct('weight',1.0); //This really does not assign the value, I checked on 2009A.
for i=1:1000000; edges(i).weight=1.0; end;
But that is slow, I want to do something more like
edges(:).weight=[rand(1000000,1)]; //with or without the square brackets.
Any ideas/suggestions to vectorize this assignment, so that it will be faster.
Thanks in advance.
This is much faster than deal or a loop (at least on my system):
N=10000;
edge(N) = struct('weight',1.0); % initialize the array
values = rand(1,N); % set the values as a vector
W = mat2cell(values, 1,ones(1,N)); % convert values to a cell
[edge(:).weight] = W{:};
Using curly braces on the right gives a comma separated value list of all the values in W (i.e. N outputs) and using square braces on the right assigns those N outputs to the N values in edge(:).weight.
You can try using the Matlab function deal, but I found it requires to tweak the input a little (using this question: In Matlab, for a multiple input function, how to use a single input as multiple inputs?), maybe there is something simpler.
n=100000;
edges(n)=struct('weight',1.0);
m=mat2cell(rand(n,1),ones(n,1),1);
[edges(:).weight]=deal(m{:});
Also I found that this is not nearly as fast as the for loop on my computer (~0.35s for deal versus ~0.05s for the loop) presumably because of the call to mat2cell. The difference in speed is reduced if you use this more than once but it stays in favor of the for loop.
You could simply write:
edges = struct('weight', num2cell(rand(1000000,1)));
Is there something requiring you to particularly use a struct in this way?
Consider replacing your array of structs with simply a separate array for each member of the struct.
weights = rand(1, 1000);
If you have a struct member which is an array, you can make an extra dimension:
matrices = rand(3, 3, 1000);
If you just want to keep things neat, you could put these arrays into a struct:
edges.weights = weights;
edges.matrices = matrices;
But if you need to keep an array of structs, I think you can do
[edges.weight] = rand(1, 1000);
The reason that the structs in your example don't get initialized properly is that the syntax you're using only addresses the very last element in the struct array. For a nonexistent array, the rest of them get implicitly filled in with structs that have the default value [] in all their fields.
To make this behavior clear, try doing a short array with clear edges; edges(1:3) = struct('weight',1.0) and looking at each of edges(1), edges(2), and edges(3). The edges(3) element has 1.0 in its weight like you want; the others have [].
The syntax for efficiently initializing an array of structs is one of these.
% Using repmat and full assignment
edges = repmat(struct('weight', 1.0), [1 1000]);
% Using indexing
% NOTE: Only correct if variable is uninitialized!!!
edges(1:1000) = struct('weight', 1.0); % QUESTIONABLE
Note the 1:1000 instead of just 1000 when indexing in to the uninitialized edges array.
There's a problem with the edges(1:1000) form: if edges is already initialized, this syntax will just update the values of selected elements. If edges has more than 1000 elements, the others will be left unchanged, and your code will be buggy. Or if edges is a different type, you could get an error or weird behavior depending on its existing datatype. To be safe, you need to do clear edges before initializing using the indexing syntax. So it's better to just do full assignment with the repmat form.
BUT: Regardless of how you initialize it, an array-of-structs like this is always going to be inherently slow to work with for larger data sets. You can't do real "vectorized" operations on it because your primitive arrays are all broken up in to separate mxArrays inside each struct element. That includes the field assignment in your question – it is not possible to vectorize that. Instead, you should switch a struct-of-arrays like Brian L's answer suggests.
You can use a reverse struct and then do all operations without any errors
like this
x.E(1)=1;
x.E(2)=3;
x.E(2)=8;
x.E(3)=5;
and then the operation like the following
x.E
ans =
3 8 5
or like this
x.E(1:2)=2
x =
E: [2 2 5]
or maybe this
x.E(1:3)=[2,3,4]*5
x =
E: [10 15 20]
It is really faster than for_loop and you do not need other big functions to slow your program.

Resources