Weedpacket So [1,3,5,7], [[1,3,5,7]] and [[1],[3],[5],[7]] are all the same thing to octave and its matrix multiplication operation treats them all as 1×4 matrices
Yes.
Weedpacket So dot([1,1,1,1],[1,3,5,7]) == [1,1,1,1]*[1;3;5;7], right?
Yes. Both of those appear to return a scalar value. HOWEVER, if you reverse the order of operands, they return different results:
octave:82> dot([1,1,1,1],[1,3,5,7])
ans = 16
octave:83> [1,1,1,1]*[1;3;5;7]
ans = 16
octave:84> dot([1,3,5,7], [1,1,1,1])
ans = 16
octave:85> dot([1;3;5;7], [1,1,1,1])
ans = 16
octave:86> [1;3;5;7]*[1,1,1,1]
ans =
1 1 1 1
3 3 3 3
5 5 5 5
7 7 7 7
Weedpacket What do dot([1;1;1;1],[1;3;5;7]), dot([1,1,1,1],[1;3;5;7]), and dot([1;1;1;1],[1,3,5,7]) produce?
octave:86> dot([1;1;1;1],[1;3;5;7])
ans = 16
octave:87> dot([1,1,1,1],[1;3;5;7])
ans = 16
octave:88> dot([1;1;1;1],[1,3,5,7])
ans = 16
Weedpacket (Incidentally, personally I've been using a different notation again; I've been translating it into PHP and what I thought was octave before learning about ; for the sake of not adding another notation into the mix.)
I appreciate your efforts, and have only slowly come to realize where my confusion lies. You are enormously helpful, and hardly to blame for Octave's quirks or dissimilarities to PHP--or my confused & anemic descriptions.
Weedpacket ended up with two distinct functions to do the same job
The dot
function seems like a convenience function more than anything. I haven't seen it used in the code I've been looking at. Like many PHP functions, it surely has its own quirks to which one must become accustomed.
Weedpacket The NumPHP and NumPY libraries don't do this confounding-vectors-with-matrices thing; they retain rank information
This may indeed be the desired behavior from a sort of abstract mathematical perspective, but I would point out that matrix multiplication is not symmetric -- see those first dot-versus-multiplication results at the top of this post. I apologize that I'm unable to foresee the broader mathematical/code significance of this. My comprehension of matrix operations and their relation to the underlying algegbra is dim, at best. I remain confused about what it means that dot always returns a scalar (as NumPY presumably does) when multiplying these vectors/singleton matrices, whereas the octave/matlab multiplication operation returns a scalar in one case and a 4x4 matrix when the operands are reversed.
Weedpacket Well, octave was built with maths processing in mind, and its matrix handling is probably heavily optimised. PHP ... isn't. (Actually, that splitting of dot product and matrix multiplication, and limiting itself to rank-2, may have been deliberate performance decisions.)
Quite frankly, I am far more interested in performance for the task at hand which has two main parts:
- convert this svm code from octave/matlab PHP such that it doesn't require installation of additional PHP or exec functions
- insure that performance is reasonable for common use cases
I'm far less interested in any mathematical orthodoxy as far as distinctions between a vector and a 1xn or nx1 matrix. In fact, you might say I'm sort of accustomed to the quirky octave behaviors and rather hooked on the stubborn refusal of the octave multiplier operation to proceed if the dimensions don't match. When the code makes assumptions about what I'm trying to multiply, this, to me, introduces ambiguity. I like that [1,1,1,1] * [1;3;5;7]
and [1;3;5;7]*[1,1,1,1]
produce different results. When they don't, I feel like things are getting unpredictable.
There can be no question that octave is highly optimized for multiplying a matrix times its transpose -- at least compared to these PHP libs I've identified. Octave is three or four orders of magnitude faster on a 1375x1966 matrix. I wrote my own multiply function, and it takes over ten minutes on this laptop to multiply this matrix times its transpose. The transposition happens in about 0.14 seconds. It's the multiplication of 1375x1966 matrix by 1966x1375 matrix that takes so long. My function:
public function multiply($matrix) {
$iteration_count = $this->colCount();
if ($iteration_count !== $matrix->rowCount()) {
throw new Exception('nonconformant arguments (op1 is ' . $this->sizeAsString() . ', op2 is ' . $matrix->sizeAsString() . ')');
}
$result_rows = $this->rowCount();
$result_cols = $matrix->colCount();
$ret_array = [];
for($i=0; $i<$result_rows; $i++) {
$ret_array[$i] = array_fill(0, $result_cols, 0);
for($j=0; $j<$result_cols; $j++) {
for($k=0; $k<$iteration_count; $k++) {
$ret_array[$i][$j] += ($this->data[$i][$k] * $matrix->data[$k][$j]);
}
}
}
return new static($ret_array);
}
multiply in 734.63768601418 seconds
I looked into the PHP-ML code, which had the fastest multiplication operation in my earlier testing, and appropriated its multiplication algorithm to produce this function:
public function multiply2($matrix) {
// this fn largely borrowed from php-ml, which said:
/*
- To speed-up multiplication, we need to avoid use of array index operator [ ] as much as possible( See #255 for details)
- A combination of "foreach" and "array_column" works much faster then accessing the array via index operator
*/
$iteration_count = $this->colCount();
if ($iteration_count !== $matrix->rowCount()) {
throw new Exception('nonconformant arguments (op1 is ' . $this->sizeAsString() . ', op2 is ' . $matrix->sizeAsString() . ')');
}
$result_cols = $matrix->colCount();
$product = [];
foreach ($this->data as $row => $rowData) {
for ($col = 0; $col < $result_cols; ++$col) {
$columnData = array_column($matrix->data, $col);
$sum = 0;
foreach ($rowData as $key => $valueData) {
$sum += $valueData * $columnData[$key];
}
$product[$row][$col] = $sum;
}
}
return new self($product);
}
It's nearly 3 times faster:
multiply2 in 288.33355784416 seconds
However, as I mentioned previously, octave performs a calculation like this instantly. If I had to guess, I'd say octave is probably capitalizing on some kind of contiguous memory structure where there's no need to traverse/search an array to locate a particular index. I expect there might also be some shrewd mathematical optimizations -- there's probably a symmetry that results when you multiply a matrix by its transpose, and perhaps they're using some kind of middle-out optimization or something. I seriously doubt it'll be possible to massage these PHP multiplication functions to boost performance by 3 orders of magnitude, but I'm open to suggestions. This is a drag.