This really has nothing to do with the maturity of MATLAB ... every language that uses BLAS routines in the background will have the same issue. The explanation is because of the way real and complex variables are stored, and how they are multiplied in the background by the BLAS libraries. Real variables are stored in a contiguous block of memory with all the elements next to each other. Since R2018a Complex variables are stored in a contiguous block of memory with the real and imaginary parts interleaved. Matrix multiplication of arrays in MATLAB is accomplished by calling the appropriate BLAS library routine in the background. But there are no complex-real routines in this library. You either have to pass in two real variables, or two complex variables. So when you have a complex times real situation, the real variable first has to be deep copied to new memory in an interleaved fashion with 0's filling the imaginary spots, then the BLAS library is called with these inputs. So these deep copies (to twice the memory size) and all those 0 multiplies are the culprit, but if you have mixed complexity types and want to use BLAS routines in the background this penalty can't be avoided (and would be the same in any other language that used BLAS routines).
BTW, if possible you should be careful doing a' or conj(a) ahead of time as these can cause deep copies also. It might be best to use the transpX and transpY options of pagemtimes( ) to essentially get the same result without making explicit deep copies of the inputs.
Side Note: In MATLAB R2017b and earlier, complex variables were stored with the real and imaginary parts in separate blocks of memory, so mixed matrix multiplies could be accomplished by calling the real*real routine on the individual parts in the background, avoiding the deep copies and avoiding a lot of 0 multiplies. Bottom line is that mixed matrix multiplies in R2017b and earlier is faster than R2018a and later.
*** EDIT ***
If "a" is a complex vector and B is real with symmetric pages, you can mimic what happens in R2017b by splitting up "a" into its real and imaginary parts and doing the computations separately. This avoids all unnecessary deep data copies and 0 multiplies and should be significantly faster. E.g., in this particular case you would have
ar = real(a); ai = imag(a);
Then do the pagewise calculations ar'*B*ar + ai'*B*ai to get the result. Because of the B symmetry, all of the imaginary result can be shown to be 0, so this method avoids explicitly computing it entirely. And you don't even need pagemtimes for this. You can turn your B array into one big 2D array and do normal matrix multiplies. E.g.,
N = numel(a);
BN = reshape(B,N,[]);
result = ar' * reshape( ar' * BN, N, [] ) + ai' * reshape( ai' * BN, N, [] );
Test using reshape and normal matrix multiply:
a = rand(50,1) + rand(50,1)*1i;
B = B + pagetranspose(B);
ar = real(a); ai = imag(a);
result_r = ar' * reshape( ar' * BN, N, [] ) + ai' * reshape( ai' * BN, N, [] );
toc
Elapsed time is 0.193764 seconds.
Test using pagemtimes:
result_p = pagemtimes( a,'ctranspose',pagemtimes(B,a),'none');
toc
Elapsed time is 1.626860 seconds.
Compare:
result_r(1:10)
ans =
1.0e+03 *
1.1392 1.1815 1.1726 1.1622 1.1707 1.1688 1.1556 1.1768 1.1739 1.1910
reshape(result_p(1:10),1,[])
So, same result but a lot faster, and without those residual imaginary values that are only non-zero because of numerical effects. E.g.,
If the B pages are not symmetric, then the imaginary part of the result will not be 0. However, you can still probably save time by splitting things up and avoiding the intermediate 0 multiplies. E.g., just do the pagewise equivalent of:
ar'*B*ar + ai'*B*ai + (ar'*B*ai - ai'*B*ar)*1i
which would be:
ar = real(a); ai = imag(a);
result = complex(ar' * reshape( ar' * BN, N, [] ) + ai' * reshape( ai' * BN, N, [] ),...
ai' * reshape( ar' * BN, N, [] ) + ar' * reshape( ai' * BN, N, [] ));
toc
Elapsed time is 0.385149 seconds.
Still quite a bit faster than the pagemtimes( ) method with all those unnecessary 0 multiplies. When the B pages are symmetric, that last imaginary part is identically 0 and drops out.