Eigen array shift gives strange result - c++

I'm almost sure it's a trivial problem, but I can't understand what wrong is going on.
I have a simple array in Eigen that I need to right-shift, so that I can always insert new sample at position 0:
#include <Eigen/Dense>
using namespace Eigen;
typedef VectorXd vec;
// The actual buffer
int N = 10;
vec _x(N);
_x = vec::Constant(0.0);
// This is just for dimonstrative purpose
for (int i = 0; i < N; i++)
_x(seq(1, N - 1)) = _x(seq(0, N - 2));
_x(0) = (double) i;
}
What I got is that, at each cycle, it is copying twice each element to be shifted:
--- After cycle 1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
--- After cycle 2: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
--- After cycle 3: [2, 1, 1, 0, 0, 0, 0, 0, 0, 0]
--- After cycle 4: [3, 2, 2, 1, 1, 0, 0, 0, 0, 0]
--- ...
--- After cycle 10: [9, 8, 8, 7, 7, 6, 6, 5, 5, 4]
I'm pretty sure I'm messing something around with Eigen. What's wrong with this approach?

As you found out yourself, you have an aliasing problem -- which can be solved by adding an eval() at the end of the expression. However, this (usually) requires an extra memory allocation. If you want to move the elements in-place, you can use std::copy_backward:
std::copy_backward(_x.begin(), _x.end()-1, _x.end());
But this is also not optimal, since it likely does not vectorize well.
The actual solution to your problem should be to re-think how you can collect your data without always having to copy everything.
For example, have a bigger array which you start filling backwards from position end()-10, and always just use a .segment() of that array.
If you only rarely need to actually use the array, use some kind of ring-buffer which you only copy to an Eigen structure when needed.

It was tricky and trivial at the same time. The problem is into the aliasing that Eigen vectors undergo when performing block assignment.
The solution is to compute .eval() at the right-hand operator in order to let Eigen evaluate the new array first, and only then assign it to the left-hand block:
_x(seq(1, N - 1)) = _x(seq(0, N - 2)).eval();
Hope this can be useful for someone

First, as other say, aliasing is the problem, don't get caught in the "easy syntax" of the library.
Expression templates can lead to bad code.
Also mutation and aliasing are incompatible.
My solution would be to forget about this feature and go back to the basics of algorithms.
If you want to shift elements, well std::shift_ the elements then:
#include <Eigen/Dense>
#include <iostream>
int main() {
using namespace Eigen;
typedef VectorXd vec;
// The actual buffer
int N = 10;
vec _x(N);
// This is just for dimonstrative purpose
for (int i = 0; i < N; i++) {
// _x(seq(1, N - 1)) = _x(seq(0, N - 2));
std::shift_right(_x.begin(), _x.end(), 1);
_x(0) = static_cast<double>(i);
std::cout << '[' << _x << ']' << std::endl;
}
}
https://godbolt.org/z/rnhsPxdsE
more idiomatic:
for(int i = 0; i != N; i++) {
*(std::shift_right(_x.begin(), _x.end(), 1) - 1) = static_cast<double>(i);
}

Related

Create Array as Vector with 10 Million elements and assign random numbers without duplicates

I try to code myself a Table with random generated Numbers. While that is simple as it is, causing that Vector not having any duplicates isn't as easy as I thought. So far my Code looks like that:
QStringList generatedTable;
srand (QTime::currentTime().msec());
std::vector<int> array(10000000);
for(std::size_t i = 0; i < array.size(); i++){
array[i] = (rand() % 10000000000)+1;
}
It generates numbers just fine, but because I'm generating a large amount of array elements (10 Million), even though I'm using 10 Billion possible numbers, it will create duplicates. I already browsed a bit and found something that seems handy to use, but doesn't work properly in my Program. The code is from another stackoverflow User:
#include<iostream>
#include<algorithm>
#include<functional>
#include<set>
int main()
{
int arr[] = {0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1};
std::set<int> duplicates;
auto it = std::remove_if(std::begin(arr), std::end(arr), [&duplicates](int i) {
return !duplicates.insert(i).second;
});
size_t n = std::distance(std::begin(arr), it);
for (size_t i = 0; i < n; i++)
std::cout << arr[i] << " ";
return 0;
}
It basically moves all the duplicates to the end of the Array, but for some reason does it not work anymore when the array gets bigger. The code will always place the iterator n at 32.768 as long the Array stays above a Million. Under a Million it drops slightly to ~31.000. So while the code is nice it doesn't really help me alot. Does someone have a better option I could use? Since I'm still a Qt and C++ beginner do I not know how to solve that problem properly.
If you want to sample N integers without replacement from the range [low, high) you can write this:
std::vector<int> array(N); // or reserve space for N elements up front
auto gen = std::mt19937{std::random_device{}()};
std::ranges::sample(std::views::iota(low, high),
array.begin(),
N,
gen);
std::ranges::shuffle(array, gen); // only if you want the samples in random order
Here's a demo.
Note that this requires C++20, otherwise the range to be sampled from can't be generated lazily, which would require it to be stored in memory. If you want to write something similar before C++20, you can use the range-v3 library.
The simplest but at the same time most efficient thing is to implement a binary search tree. Generate the random number in your range and check if it's not already there. Note that the operations are performed in a time O(n)

C++ move a range by a non-negative displacement?

I was faced with a need to move a range of elements backward by a certain (possibly zero) displacement. To illustrate, here is some code:
std::vector<int> arr[4] = {{1}, {2}, {3}, {4}};
std::move(arr + 2, arr + 4, arr + 2 - k); // k can be either 0, 1, or 2,
// depending on how far much I want to move the elements
This works as expected when k is nonzero, but fails when k is zero (I expect the move to become a no-op when k=0).
The following code, when compiled in GCC will cause arr[2] and arr[3] to lose all their elements:
int main() {
std::vector<int> arr[4] = {{1}, {2}, {3}, {4}};
std::move(arr + 2, arr + 4, arr + 2);
for(int i = 0; i < 4; ++i) std::cout << arr[i].size() << ' '; // prints "1 1 0 0 "
std::cout << std::endl;
}
C++ reference says that std::move's destination should not be in the range [first, last), and my code has been in violation of this.
After some digging, I found that self move assignment (i.e. x = std::move(x)) is not expected to be a no-op, so that seems to be why I'm not allowed to do a no-op range std::move.
This issue would also happen if I want to move a range of elements forward by a certain (possibly zero) displacement.
To solve this problem, I could check explicitly that k is nonzero before moving. But is this the idiomatic C++ way?
Yes, such a check would be very typical. Even if x[i]=x[i+0]; were a no-op you would still want to skip the loop processing if possible and that's what the zero-displacement check would provide.

Add an element at the beginning of an array and shift the rest but keep the size

I need to create a vector/array in the following format:
arr[10] = [1, 2, 3, 4, 5, 6, 7, 8, 9 ,10]
I want to add a new element at the beginning after which all elements are shifted to the right and the last element is deleted. The result should be:
arr[10] = [new_int, 1, 2, 3, 4, 5, 6, 7, 8, 9]
How can I do this in c++? Do I have to write a function or there is already an existing one like .append or .pushback?
If it's an std::vector, the trivial solution is:
vec.pop_back();
vec.insert(vec.begin(), new_int);
The asymptotic complexity is the same as any other method to accomplish this (pop_back() is O(1), insert in head is O(n), no reallocation is ever performed) but it temporarily touches the vector length for no good reason and does overall more bookkeeping.
A better solution (that is fine both for std::vector, std::array as well as for C-style array) is:
std::copy_backward(std::begin(vec), std::end(vec)-1, std::begin(vec)+1);
vec[0] = new_int;
This, again, should have O(n) complexity, but a smaller "offset" (it does exactly what it needs to do, nothing more).
Now, if we move to different data structures the situation is different; with an std::deque you can do as #JesperJuhl shows in his answer; pushing/popping at both ends of a deque costs amortized O(1), so it's reasonably fast for most uses.
Still, if your buffer is fixed in size, generally the natural data structure for the operation you describe is the fixed-size circular buffer; it is not provided by the standard library, but there's an implementation in Boost (besides, it's also a nice exercise to write it yourself, with iterators and everything).
#include <algorithm>
#include <iterator>
...
std::rotate( std::begin(arr), std::end(arr)-1, std::end(arr));
arr[0] = new_int;
The easiest would be to use a std::deque. Then it becomes as simple as
my_deque.pop_back(); // get rid of last element.
my_deque.push_front(42); // put the new element at the front
You can use vector using push and pop. If you want to do it using arrays then you can:
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9 ,10};
int new_value = 20;
for(int i(0); i < 10 - 2; i++){
arr[i + 1] ^= arr[i];
arr[i] ^= arr[i + 1];
arr[i + 1] ^= arr[i];
}
Or you can use a temporary variable instead of XOR operator:
/*
int tmp = arr[i + 1];
arr[i + 1] = arr[i];
arr[i] = tmp;
*/
arr[0] = new_value;
for(int i = 0; i < 10; i++)
std::cout << arr[i] << ", ";
std::vector<int> arr;
arr.insert(arr.begin(), newvalue);
arr.pop_back();

Bijective mapping of integers

English is not my native language: sorry for my mistakes. Thank you in advance for your answers.
I'm learning C++ and I'm trying to check to what extent two sets with the same number of integers--in whatever order--are bijective.
Example :
int ArrayA [4] = { 0, 0, 3, 4 };
int ArrayB [4] = { 4, 0, 0, 3 };
ArrayA and ArrayB are bijective.
My implementation is naive.
int i, x=0;
std::sort(std::begin(ArrayA), std::end(ArrayA));
std::sort(std::begin(ArrayB), std::end(ArrayB));
for (i=0; i<4; i++) if (ArrayA[i]!=ArrayB[i]) x++;
If x == 0, then the two sets are bijective. Easy.
My problem is the following: I would like to count the number of bijections between the sets, and not only the whole property of the relationship between ArrayA and ArrayB.
Example :
int ArrayA [4] = { 0, 0, 0, 1 }
int ArrayB [4] = { 3, 1, 3, 0 }
Are the sets bijective as a whole? No. But there are 2 bijections (0 and 0, 1 and 1).
With my code, the output would be 1 bijection. Indeed, if we sort the arrays, we get:
ArrayA = 0, 0, 0, 1;
ArrayB = 0, 1, 3, 3.
A side-by-side comparaison shows only a bijection between 0 and 0.
Then, my question is:
Do you know a method to map elements between two equally-sized sets and count the number of bijections, whatever the order of the integers?
Solved!
The answer given by Ivaylo Strandjev works:
Sort the sets,
Use the std::set_intersection function,
Profit.
You need to count the number of elements that are contained in both sets. This is called set intersection and it can be done with a standard function - set_intersection, part of the header algorithm. Keep in mind you still need to sort the two arrays first.

pointer to sub-row of Eigen MatrixXd that behaves like a VectorXd

I have an Eigen MatrixXd and need a pointer to some subsequent entries of some row. I would like to be able to use this pointer. I have something like this:
Eigen::MatrixXd* matrix = new MatrixXd(3, 3);
(*matrix) << 1, 2, 3,
4, 5, 6,
7, 8, 9;
Block<MatrixXd, 1, Dynamic, false, true> full_row = (*matrix).row(1);
// this gives me the full row. I am interested only in the row containing 5 6.
Block<MatrixXd> part_row = (*matrix).block(1, 1, 1, 2);
// this gives me the partial row that I want, but now i need two indices to
// access an element.
part_row(0, 1) = 3; // works
part_row(1) = 3; // gives compiler error
I would like to be able to directly access the partial row, without having to copy the values. This is really important, since it has to be done often and I cannot afford to copy vectors back and forth. (I believe I cannot expect the compiler to optimize out the copying, since the sizes of the matrices are generally unknown). Any help is greatly appreciated. Cheers!
You need to specify that your submatrix is a vector:
Block<MatrixXd,1,Dynamic> part_row(*matrix, 1, 1, 1, 2);