Hello again! In this video, we are going to continue looking at further numerical algorithms.
We are going to look at the inner_product algorithm in more depth.
So, to remind ourselves, this takes two ranges of iterators.
It multiplies together corresponding elements from each iterator range, and then it adds them all up, and
it adds them to this initial value of the sum.
This can actually be written as two separate algorithm calls.
We could have a call to transform() which does the multiplication and a call to accumulate(), which adds
up the results of the multiplications.
So we would have this transform() call, which takes the two containers as the input range.
It has a temporary vector as the destination.
And then it calls the library function object, "multiplies" for int.
So we need to pass an object of that.
And then the accumulate() call will take this temporary vector as the iterator range, initial value
zero, and it will use the library function object "plus" to add them up.
And, just to show they give the same result, here is the code that we had for inner_product() before, and
then we are going to write this as a call to transform() and accumulate().
So let's see if we get the same answers.
And yes, we do.
If we want to overload inner_product(), we actually have two possibilities.
We can overload the addition and the multiplication.
We can replace the multiplication step by some kind of transform operation.
So this will take two arguments of the element type, and combine them, and return a value of some return
type.
This will usually be the same type as the elements, but it does not have to be.
For example, we could have two vectors of real numbers and we could return a complex number.
We can also replace the addition by some kind of "accumulate" operation.
So this will take two arguments, of the returned type from this operation, and it will return a value of
the result type.
Again, this can be a different type from the one it was given.
So this will collect up all the results of these transformations, and reduce them down to a single final
result.
So this is sometimes called a "reduce" operation.
As an example of this, let's imagine that we have conducted a scientific experiment.
We have our results and we have stored them in a vector.
We have also calculated the results we expected to get, according to theory, and we have those in another
vector and we want to find out how accurate our results are.
Or if you prefer, how accurate the theory is! So we want to find the biggest error in the results.
So that means the maximum difference between one of our results and the expected theoretical result.
And we can do that by using inner_product(). We can have a transform operation, which will find the differences
between elements in the two vectors, and then a "reduce" or "accumulate" operation, which will go through
all those differences and pick out the biggest one.
And then the result of that will be the maximum error in the data.
So we need to replace the multiplication by some function, which will find the difference between the
corresponding elements, and we need to replace addition by something that finds the largest difference.
And by the way, I found this on a blog, so credit to the original author. So when we overload
innre_product(), we add two extra arguments.
The first one is the replacement for the reduce operation, and the second one is the replacement for
the transform operation.
So, our transform operation is going to be called with one element from each vector.
We want to find the difference, so we subtract them, and then we want to ignore the of this result,
so we use fabs(), which will return the absolute value.
The reduce operation is going to be called with two of these differences as arguments, and we want to
find the biggest one so we can just use the library max() function for that.
So here is that code, we have our vector with the theoretical predictions, the nice straight line.
And then another vector with our actual results, which is pretty close, actually.
So here is our call to inner_product(). We give those two vectors as the input ranges.
We have zero as the starting value, because we want to find a difference that is bigger than 0.
We have this function, which is going to be called with one element from each vector.
And it is going to return the difference, ignoring sign.
Then we have this function, which would be called with two of these differences as arguments, and
we return the biggest one.
So let's find out what the biggest difference is.
And the answer is 0.03.
So let's check this. So that is 0.01, 0.02, 0.03, 0.01, 0.02.
So yes, that is the biggest discrepancy.
So that is correct.
If you have done parallel programming, this may seem a bit familiar.
This is actually quite a common pattern.
So if you have a problem which involves a large calculation, sometimes you can break it down into smaller
calculations, which are independent of each other.
So you do not need to wait for one part to complete before you can do the next.
And if that is the case, you can perform each of these sub-calculations on its own processor core,
so they can all be done concurrently.
And then you combine together the results of these sub-calculations, and that will give you the final
result,
the answer to your calculation.
So we could do this with inner_product(), with transform() and accumulate().
Unfortunately, as we saw when we looked at the accumulates algorithm function, it is not really suitable
for parallel processing.
C++ 17 has a new algorithm function, transform_reduce().
This is exactly the same as inner_product, except it uses reduce() instead of accumulate().
And as we saw when we discussed accumulate(), reduce() is compatible with parallel processing.
So this can give much better results than using inner_product() for this kind of programming idiom.
OK, so that is it for this video.
I will see you next time.
Meanwhile, keep coding!