- Edited
Soooo I have been able to download and make/build the latest OpenBlas and build it both on Ubuntu 20 and MacOs (Catalina, I think? With intel chip) and get it to work with ghostjat/np on both platforms.
The steps, should anyone be curious. These are the ubuntu steps but should also work with little modification on MacOs -- e.g., download the file with your browser instead of using wget
#make a dir for openblas:
mkdir openblas
cd openblas
#download latest OpenBlas, extract it, cd into the new dir and make the project.
# make operation takes awile, and your machine will need a compiler and probably gfortrain, etc.:
wget https://github.com/xianyi/OpenBLAS/releases/download/v0.3.21/OpenBLAS-0.3.21.tar.gz
tar -xvzf OpenBLAS-0.3.21.tar.gz
cd OpenBLAS-0.3.21
make
Take note of the library's location, which will probably have a name specific to your machine's CPU architecture. In my case, this is it: libopenblas_sandybridgep-r0.3.21.so
Take note of the full path to that lib:
/home/sneakyimp/biz/machine-learning/openblas/OpenBLAS-0.3.21/libopenblas_sandybridgep-r0.3.21.so
We can now put that shared lib path in the blas.h file included with the ghostjat/np PHP code. I repeat here the steps to set up a brand new ghostjat/np file:
cd ..
mkdir np
cd np
# you may need to get composer, see: https://getcomposer.org/download/
./composer.phar require ghostjat/np:dev-main
#edit the blas.h file that comes with it:
nano vendor/ghostjat/np/src/core/blas.h
In the blas.h file, change the FFI_LIB to point to the full path of the OpenBLAS lib you just built:
#define FFI_LIB "/home/sneakyimp/biz/machine-learning/openblas/OpenBLAS-0.3.21/libopenblas_sandybridgep-r0.3.21.so"
Now you should be able to run this PHP file, bar.php which performs a matrix multiplication:
<?php
require __DIR__ . '/vendor/autoload.php';
use Np\matrix;
// NOTE THIS IS MY DATA YOU'LL NEED TO COOK YOUR OWN
// OR REFER BACK TO OUR PRIOR CORRESPONDENCE
$data = json_decode(file_get_contents(__DIR__ . '/../training_data_sets.json'), TRUE);
// more trial and error...cooked this up after reading the ghostjat/np source code
Np\core\blas::$ffi_blas = FFI::load(__DIR__ . '/vendor/ghostjat/np/src/core/blas.h');
$ta = matrix::ar($data['Xtrain']);
$start = microtime(true);
$tb = $ta->transpose(); // to generate random 2d matrix
echo "transpose elapsed time: " . (microtime(true) - $start) . "seconds\n";
$start = microtime(true);
$tc = $ta->dot($tb);
echo "dot elapsed time: " . (microtime(true) - $start) . "seconds\n";
$arr = $tc->asArray();
echo "rows: " . sizeof($arr) . "\n";
echo "cols: " . sizeof($arr[0]) . "\n";
$json = json_encode($arr, JSON_PRETTY_PRINT);
file_put_contents(__DIR__ . '/np-product.json', $json);
echo "json md5 is " . md5($json) . "\n";
While I am delighted that bar.php runs and produces the correct output, I would point out that I'm just using the blas.h file that is distributed with ghostjat/np. I'm still quite concerned that blas.h might be totally mismatched against the latest OpenBlas. I tried using the cblas.h that is distributed with OpenBLAS, but FFI::load complains about it. Here's a typical error:
PHP Fatal error: Uncaught FFI\ParserException: ';' expected, got '<STRING>' at line 8 in /home/jaith/biz/machine-learning/np2/bar.php:9
Stack trace:
#0 /home/jaith/biz/machine-learning/np2/bar.php(9): FFI::load()
#1 {main}
It would appear that FFI has some stunted/limited ability to parse these h files, but its limitations are not very clear, nor am I sure how to convert the OpenBlas header file to something FFI can digest. The documentation offers a little help:
C preprocessor directives are not supported, i.e. #include, #define and CPP macros do not work, except for special cases listed below.
The header file should contain a #define statement for the FFI_SCOPE variable, e.g.: #define FFI_SCOPE "MYLIB". Refer to the class introduction for details.
The header file may contain a #define statement for the FFI_LIB variable to specify the library it exposes. If it is a system library only the file name is required, e.g.: #define FFI_LIB "libc.so.6". If it is a custom library, a relative path is required, e.g.: #define FFI_LIB "./mylib.so".
There's also a user comment that seems promising:
Since #include's and #define's other than FFI_LIB and FFI_SCOPE are not supported in the header, you may want to use the C preprocessor to pre-process your headers in order to resolve all #include's and macros.
I use -D"attribute(ARGS)=" to remove function attributes as well, which are not supported by FFI either.
This is my script:
echo '#define FFI_SCOPE "YOUR_SCOPE"' > header-ffi.h
echo '#define FFI_LIB "/path/to/your_lib.so"' >> header-ffi.h
cpp -P -C -D"attribute(ARGS)=" header_original >> header-ffi.h
Unfortunately, I tried it and, although it produced a 15,000-line header-ffi.h when I ran it on cblas.h, this file also produced FFI errors. I've not been able to use any blas.h/cblas.h file from CBLAS or OpenBLAS and get it working. Any advice on how I might do this would be much appreciated.
I'm also a bit concerned about using OpenBLAS for a couple of reasons:
- this is a big project, probably overkill. It takes a while to make and the shared lib
libopenblas_sandybridgep-r0.3.21.so
is 14MB - I worry a bit about security. The contributors include quite a few far flung devs and the Chinese Academy of Sciences
I have been able to build CBLAS on my ubuntu machine by first separately downloading and building BLAS and editing the CBLAS Makefile to refer to the static library it produces in this line:
BLLIB = /home/sneakyimp/biz/machine-learning/blas/BLAS-3.11.0/blas_LINUX.a
This won't build on MacOS, complaining about the previously mentioned fortran stuff, but on Ubuntu 20 it seems to build. Unforunately, it does not produce any .so shared libraries. It just produces a static library, lib/cblas_LINUX.a
. I've tried that with ghostjat but it fails.
Is there some simple flag I can provide to make so that CBLAS will build the shared libraries (i.e., the .so files)? Can anyone suggest an approach to creating a blas.h file for ghostjat/np that better matches either OpenBLAS or CBLAS?