├── .gitignore ├── CLnets.cpp ├── CLnets.hpp ├── Makefile ├── Makefile.osx ├── README.md ├── build_utils.hpp ├── caffeLink.cpp ├── caffeLink.hpp ├── libLink_inputs.cpp ├── libLink_inputs.hpp ├── libLink_outputs.cpp ├── libLink_start.cpp ├── module ├── caffeLink.m └── demo │ ├── imageNet.nb │ ├── imageNet.pdf │ ├── liblink-test.nb │ ├── mnist.nb │ └── mnist.pdf ├── utils.cpp └── utils.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ 2 | /build/ 3 | /dist/ 4 | .dep.inc -------------------------------------------------------------------------------- /CLnets.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include 6 | #include "caffe/caffe.hpp" 7 | 8 | #include "build_utils.hpp" 9 | #include "CLnets.hpp" 10 | 11 | template 12 | CLnets::CLnets() 13 | { 14 | this->net = NULL; 15 | } 16 | 17 | template 18 | bool CLnets::prepareNetString(char* paramStr) 19 | { 20 | caffe::NetParameter param; 21 | bool success = google::protobuf::TextFormat::ParseFromString(paramStr, ¶m); 22 | if(!success) { 23 | printf("ERR in %s: Could not creat net\n", __FUNCTION__); 24 | return false; 25 | } 26 | 27 | if(this->net) 28 | delete this->net; 29 | 30 | this->net = new caffe::Net(param); 31 | 32 | return true; 33 | } 34 | 35 | template 36 | bool CLnets::prepareNetFile(char* path) 37 | { 38 | if(this->net) 39 | delete this->net; 40 | 41 | this->net = new caffe::Net(path); 42 | 43 | return true; 44 | } 45 | 46 | template 47 | bool CLnets::initParamDataLUT(int** pd) 48 | { 49 | long unsigned int li; 50 | std::string name; 51 | caffe::Layer* layer; 52 | long unsigned int layerNum = net->layers().size(); 53 | 54 | int paramBlobCnt = 0; 55 | int *tmp; 56 | tmp = (int*) realloc(*pd, sizeof(int) * (layerNum + 1)); 57 | 58 | if (!tmp) { 59 | printf("%s: allocation failed\n", __FUNCTION__); 60 | return false; 61 | } 62 | *pd = tmp; 63 | 64 | for (li = 0; li < layerNum; li++) { 65 | (*pd)[li] = paramBlobCnt; 66 | 67 | name = net->layer_names()[li]; 68 | layer = net->layer_by_name(name).get(); 69 | 70 | paramBlobCnt += layer->blobs().size(); 71 | } 72 | /* stores total count of parameter blobs in last cell */ 73 | (*pd)[layerNum] = paramBlobCnt; 74 | 75 | return true; 76 | } 77 | 78 | template 79 | void CLnets::loadNet(char* path) 80 | { 81 | this->net->CopyTrainedLayersFrom(path); 82 | } 83 | 84 | template 85 | void CLnets::trainNet(caffe::SolverParameter *param, 86 | int trainMode, char* path) 87 | { 88 | // possibly overide mode 89 | if (param->solver_mode() == caffe::SolverParameter_SolverMode_GPU) { 90 | caffe::Caffe::set_mode(caffe::Caffe::GPU); 91 | caffe::Caffe::SetDevice(param->device_id()); 92 | printf("Solver uses GPU with device id %d.\n", param->device_id()); 93 | } else { 94 | printf("Solver uses CPU.\n"); 95 | } 96 | 97 | printf("Starting Optimization\n"); 98 | caffe::shared_ptr > 99 | solver(caffe::GetSolver(*param)); 100 | 101 | switch (trainMode) { 102 | case TRAIN_NEW: 103 | solver->Solve(); 104 | break; 105 | case TRAIN_SNAPSHOT: 106 | solver->Solve(path); 107 | break; 108 | case TRAIN_WEIGHTS: 109 | solver->net()->CopyTrainedLayersFrom(path); 110 | solver->Solve(); 111 | break; 112 | } 113 | 114 | printf("Optimization Done.\n"); 115 | } 116 | 117 | template 118 | bool CLnets::exportNet(char* path) 119 | { 120 | std::ofstream fCaffeModel(path, std::ios::binary); 121 | if(fCaffeModel.fail()){ 122 | printf("ERR in %s: Could not open/write " 123 | "to a file: %s\n", __FUNCTION__, path); 124 | return false; 125 | } 126 | 127 | caffe::NetParameter netPar; 128 | this->net->ToProto(&netPar, false); 129 | netPar.SerializeToOstream(&fCaffeModel); 130 | 131 | fCaffeModel.close(); 132 | return true; 133 | } 134 | 135 | template 136 | void CLnets::testNet() 137 | { 138 | Dtype loss; 139 | net->ForwardPrefilled(&loss); 140 | // printf("loss: %.3f\n", loss); 141 | } 142 | 143 | template 144 | void CLnets::printNetInfo() 145 | { 146 | long unsigned int i; 147 | const std::vector& names = net->blob_names(); 148 | 149 | long unsigned int bi; 150 | long unsigned int li; 151 | int parIdx = 0; 152 | std::string name; 153 | caffe::Blob* bl; 154 | caffe::Layer* layer; 155 | std::vector*> laTopVecs, laBotVecs; 156 | long unsigned int layerNum = net->layers().size(); 157 | 158 | printf("Each layer has bottom blobs as input and top blobs as output. Some\n" 159 | "layers have learneable parameter - weights etc.\n" 160 | "Names (%lu): ", names.size()); 161 | for (i = 0; i < names.size(); i++) 162 | printf("%s%s", names[i].c_str(), (i < names.size() - 1) ? " " : "\n"); 163 | 164 | printf("\n"); 165 | 166 | for (li = 0; li < layerNum; li++) { 167 | 168 | name = net->layer_names()[li]; 169 | layer = net->layer_by_name(name).get(); 170 | laTopVecs = net->top_vecs()[li]; 171 | laBotVecs = net->bottom_vecs()[li]; 172 | /* print layer info*/ 173 | printf("%2lu: %8s, %14s, par.: %2lu, top: %2lu, bot.: %2lu\n", 174 | li, name.c_str(), layer->type_name().c_str(), 175 | layer->blobs().size(), laTopVecs.size(), laBotVecs.size()); 176 | /* and its parameter blobs */ 177 | for (bi = 0; bi < layer->blobs().size(); bi++) { 178 | bl = layer->blobs()[bi].get(); 179 | printf("%12s par[%lu (%2d)]: (%4d, %4d, %4d, %4d)\n", "", 180 | bi, parIdx++, 181 | bl->num(), bl->channels(), bl->height(), bl->width()); 182 | } 183 | 184 | /* and its bottom blobs */ 185 | for (bi = 0; bi < laBotVecs.size(); bi++) { 186 | bl = laBotVecs[bi]; 187 | printf("%12s bot[%lu, %2lu]: (%4d, %4d, %4d, %4d)\n", "", bi, 188 | li, bl->num(), bl->channels(), bl->height(), bl->width()); 189 | } 190 | /* and its top blobs */ 191 | for (bi = 0; bi < laTopVecs.size(); bi++) { 192 | bl = laTopVecs[bi]; 193 | printf("%12s top[%lu, %2lu]: (%4d, %4d, %4d, %4d)\n", "", bi, 194 | li, bl->num(), bl->channels(), bl->height(), bl->width()); 195 | } 196 | } 197 | 198 | printf("\ninput blobs info:\n vector len: %lu\n", net->input_blobs().size()); 199 | for (bi = 0; bi < net->input_blobs().size(); bi++) { 200 | bl = net->input_blobs()[bi]; 201 | printf("%6s input[%lu]: (%4d, %4d, %4d, %4d)\n", "", bi, 202 | bl->num(), bl->channels(), bl->height(), bl->width()); 203 | } 204 | 205 | } 206 | 207 | template 208 | int CLnets::getLayerIdx(char* name) 209 | { 210 | long unsigned int li; 211 | long unsigned int layerNum = net->layers().size(); 212 | std::string otherName; 213 | 214 | for (li = 0; li < layerNum; li++) { 215 | otherName = net->layer_names()[li]; 216 | 217 | if(strcmp(name, otherName.c_str()) == 0) 218 | return li; 219 | } 220 | 221 | printf("ERR in %s: Wrong layer name\n", __FUNCTION__); 222 | return -1; 223 | } 224 | 225 | template 226 | bool CLnets::getLayBlobSize(int** dims, int* dimSize, 227 | std::vector*> blobs) 228 | { 229 | unsigned int i, di; 230 | 231 | *dimSize = blobs.size() * 4; 232 | if(*dimSize == 0){ 233 | return true; 234 | } 235 | 236 | *dims = NULL; 237 | *dims = (int*) malloc(sizeof(int) * *dimSize); 238 | 239 | if(!*dims){ 240 | printf("ERR in %s: Allocation failed\n", __FUNCTION__); 241 | return false; 242 | } 243 | 244 | di = 0; 245 | for(i = 0; i < blobs.size(); i++){ 246 | (*dims)[di++] = blobs[i]->num(); 247 | (*dims)[di++] = blobs[i]->channels(); 248 | (*dims)[di++] = blobs[i]->height(); 249 | (*dims)[di++] = blobs[i]->width(); 250 | } 251 | 252 | return true; 253 | } 254 | 255 | template 256 | bool CLnets::getTopBlobSize(int** dims, int* dimSize, unsigned int layerIdx) 257 | { 258 | if(layerIdx >= net->layers().size()){ 259 | printf("ERR in %s: Wrong layer index\n", __FUNCTION__); 260 | return false; 261 | } 262 | 263 | return getLayBlobSize(dims, dimSize, net->top_vecs()[layerIdx]); 264 | } 265 | 266 | template 267 | bool CLnets::getBottomBlobSize(int** dims, int* dimSize, unsigned int layerIdx) 268 | { 269 | if(layerIdx >= net->layers().size()){ 270 | printf("ERR in %s: Wrong layer index\n", __FUNCTION__); 271 | return false; 272 | } 273 | 274 | return getLayBlobSize(dims, dimSize, net->bottom_vecs()[layerIdx]); 275 | } 276 | 277 | template 278 | bool CLnets::getParamBlobSize(int** dims, int* dimSize, unsigned int layerIdx) 279 | { 280 | unsigned int i, di; 281 | std::vector > > parBl; 282 | if(layerIdx >= net->layers().size()){ 283 | printf("ERR in %s: Wrong layer index\n", __FUNCTION__); 284 | return false; 285 | } 286 | 287 | parBl = net->layers()[layerIdx].get()->blobs(); 288 | 289 | *dimSize = parBl.size() * 4; 290 | if(*dimSize == 0) 291 | return true; 292 | 293 | *dims = NULL; 294 | *dims = (int*) malloc(sizeof(int) * *dimSize); 295 | 296 | if(!*dims){ 297 | printf("ERR in %s: Allocation failed\n", __FUNCTION__); 298 | return false; 299 | } 300 | 301 | di = 0; 302 | for(i = 0; i < parBl.size(); i++){ 303 | (*dims)[di++] = parBl[i].get()->num(); 304 | (*dims)[di++] = parBl[i].get()->channels(); 305 | (*dims)[di++] = parBl[i].get()->height(); 306 | (*dims)[di++] = parBl[i].get()->width(); 307 | } 308 | 309 | return true; 310 | } 311 | 312 | template 313 | bool CLnets::getInputSize(int** dims, int* dimSize) 314 | { 315 | if(net->input_blobs().size() < 1){ 316 | printf("ERR in %s: net has no input blobs and probaly uses data layer\n", 317 | __FUNCTION__); 318 | return false; 319 | } 320 | 321 | return getLayBlobSize(dims, dimSize, net->input_blobs()); 322 | } 323 | 324 | template 325 | bool CLnets::getTopBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx) 326 | { 327 | if (layerIdx >= net->layers().size()) { 328 | printf("ERR in %s: Wrong layer index\n", __FUNCTION__); 329 | return false; 330 | } 331 | 332 | if (blobIdx >= net->top_vecs()[layerIdx].size() 333 | || net->top_vecs()[layerIdx].size() == 0) { 334 | printf("ERR in %s: Wrong blob index\n", __FUNCTION__); 335 | return false; 336 | } 337 | 338 | *data = (Dtype*) net->top_vecs()[layerIdx][blobIdx]->cpu_data(); 339 | return true; 340 | } 341 | 342 | template 343 | bool CLnets::getBottomBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx) 344 | { 345 | if (layerIdx >= net->layers().size()) { 346 | printf("ERR in %s: Wrong layer index\n", __FUNCTION__); 347 | return false; 348 | } 349 | 350 | if (blobIdx >= net->bottom_vecs()[layerIdx].size() 351 | || net->bottom_vecs()[layerIdx].size() == 0) { 352 | printf("ERR in %s: Wrong blob index\n", __FUNCTION__); 353 | return false; 354 | } 355 | 356 | *data = (Dtype*) net->bottom_vecs()[layerIdx][blobIdx]->cpu_data(); 357 | return true; 358 | } 359 | 360 | template 361 | bool CLnets::getParamBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx) 362 | { 363 | if (layerIdx >= net->layers().size()) { 364 | printf("ERR in %s: Wrong layer index\n", __FUNCTION__); 365 | return false; 366 | } 367 | 368 | if (blobIdx >= net->layers()[layerIdx].get()->blobs().size() 369 | || net->layers()[layerIdx].get()->blobs().size() == 0) { 370 | printf("ERR in %s: Wrong blob index\n", __FUNCTION__); 371 | return false; 372 | } 373 | 374 | *data = (Dtype*) net->layers()[layerIdx].get()->blobs()[blobIdx].get()->cpu_data(); 375 | return true; 376 | } 377 | 378 | template 379 | bool CLnets::setTopBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx) 380 | { 381 | if (layerIdx >= net->layers().size()) { 382 | printf("ERR in %s: Wrong layer index\n", __FUNCTION__); 383 | return false; 384 | } 385 | 386 | if (blobIdx >= net->top_vecs()[layerIdx].size() 387 | || net->top_vecs()[layerIdx].size() == 0) { 388 | printf("ERR in %s: Wrong blob index\n", __FUNCTION__); 389 | return false; 390 | } 391 | 392 | net->top_vecs()[layerIdx][blobIdx]->set_cpu_data(*data); 393 | return true; 394 | } 395 | 396 | template 397 | bool CLnets::setBottomBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx) 398 | { 399 | if (layerIdx >= net->layers().size()) { 400 | printf("ERR in %s: Wrong layer index\n", __FUNCTION__); 401 | return false; 402 | } 403 | 404 | if (blobIdx >= net->bottom_vecs()[layerIdx].size() 405 | || net->bottom_vecs()[layerIdx].size() == 0) { 406 | printf("ERR in %s: Wrong blob index\n", __FUNCTION__); 407 | return false; 408 | } 409 | 410 | net->bottom_vecs()[layerIdx][blobIdx]->set_cpu_data(*data); 411 | return true; 412 | } 413 | 414 | template 415 | bool CLnets::setParamBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx) 416 | { 417 | if (layerIdx >= net->layers().size()) { 418 | printf("ERR in %s: Wrong layer index\n", __FUNCTION__); 419 | return false; 420 | } 421 | 422 | if (blobIdx >= net->layers()[layerIdx].get()->blobs().size() 423 | || net->layers()[layerIdx].get()->blobs().size() == 0) { 424 | printf("ERR in %s: Wrong blob index\n", __FUNCTION__); 425 | return false; 426 | } 427 | 428 | net->layers()[layerIdx].get()->blobs()[blobIdx].get()->set_cpu_data(*data); 429 | return true; 430 | } 431 | 432 | template 433 | bool CLnets::setInput(Dtype **inputData) 434 | { 435 | if(net->input_blobs().size() < 1){ 436 | printf("ERR in %s: net has no input blobs and probaly uses data layer\n", 437 | __FUNCTION__); 438 | return false; 439 | } 440 | 441 | net->input_blobs()[0]->set_cpu_data(*inputData); 442 | return true; 443 | } 444 | 445 | INSTANTIATE_CLASS(CLnets); 446 | -------------------------------------------------------------------------------- /CLnets.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CLNETS_HPP 3 | #define CLNETS_HPP 4 | 5 | #include "caffe/caffe.hpp" 6 | 7 | template 8 | class CLnets { 9 | public: 10 | explicit CLnets(); 11 | virtual ~CLnets(){}; 12 | 13 | caffe::Net *net; 14 | 15 | bool exportNet(char* path); 16 | 17 | bool prepareNetString(char* paramStr); 18 | bool prepareNetFile(char* path); 19 | bool initParamDataLUT(int** pd); 20 | void loadNet(char* path); 21 | 22 | void testNet(); 23 | void trainNet(caffe::SolverParameter *param, int trainMode, char* path); 24 | 25 | void printNetInfo(); 26 | 27 | int getLayerNum() 28 | { 29 | return net->layers().size(); 30 | } 31 | int getLayerIdx(char* name); 32 | 33 | bool getTopBlobSize(int** dims, int* dimSize, unsigned int layerIdx); 34 | bool getBottomBlobSize(int** dims, int* dimSize, unsigned int layerIdx); 35 | bool getParamBlobSize(int** dims, int* dimSize, unsigned int layerIdx); 36 | bool getInputSize(int** dims, int* dimSize); 37 | 38 | bool getTopBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx); 39 | bool getBottomBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx); 40 | bool getParamBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx); 41 | 42 | bool setTopBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx); 43 | bool setBottomBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx); 44 | bool setParamBlob(Dtype** data, unsigned int layerIdx, unsigned int blobIdx); 45 | bool setInput(Dtype **input); 46 | 47 | 48 | private: 49 | static Dtype varType; 50 | 51 | bool getLayBlobSize(int** dims, int* dimSize, std::vector*> blobs); 52 | 53 | DISABLE_COPY_AND_ASSIGN(CLnets); 54 | }; 55 | 56 | #endif /* CLNETS_HPP */ 57 | 58 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Selects compiler. 2 | CXX=g++ 3 | CXXFLAGS=-c -Wall 4 | 5 | # Change this to your Mathematica installation path. Something like: 6 | # /SystemFiles/IncludeFiles/C 7 | MATH_IDIR=/media/data/prog/mathematica-10/SystemFiles/IncludeFiles/C 8 | # Change these 3 lines to point to your Caffe directory with builded library 9 | # and headers. 10 | CAFFE_DIR=../../fel-lin/caffe 11 | CAFFE_IDIR=$(CAFFE_DIR)/include 12 | CAFFE_LDIR=$(CAFFE_DIR)/build/lib 13 | 14 | CBLAS_LIB= 15 | # To test whether cblas_sgemm() causes kernel to crash when called from 16 | # Mathematica, uncomment next 2 lines to build with Cblas and complete 17 | # cblas_test(). You neet to select proper cblas implementation. 18 | #CBLAS_LIB=-lcblas -latlas 19 | #CXXFLAGS+=-D CBLAS_TEST 20 | 21 | DIST_DIR=dist 22 | BUILD_DIR=build 23 | 24 | IDIR=-I$(CAFFE_IDIR) -I$(MATH_IDIR) 25 | LDIR=-L$(CAFFE_LDIR) 26 | LIBS=$(CBLAS_LIB) -lcaffe -shared -fPIC 27 | 28 | LDFLAGS=$(IDIR) $(LDIR) $(LIBS) 29 | SOURCES=caffeLink.cpp CLnets.cpp utils.cpp\ 30 | libLink_inputs.cpp libLink_outputs.cpp libLink_start.cpp 31 | OBJECTS=$(SOURCES:%.cpp=$(BUILD_DIR)/%.o) 32 | LIBNAME=libcaffeLink.so 33 | 34 | 35 | 36 | all: dirs $(SOURCES) $(LIBNAME) 37 | 38 | $(LIBNAME): $(OBJECTS) 39 | $(CXX) $(LDFLAGS) $(OBJECTS) -o $(DIST_DIR)/$@ 40 | 41 | $(BUILD_DIR)/%.o: %.cpp 42 | $(CXX) $(CXXFLAGS) $(LDFLAGS) $< -o $@ 43 | 44 | clean: clean.obj 45 | rm -rf $(DIST_DIR)/$(LIBNAME) 46 | 47 | clean.obj: 48 | rm -rf $(OBJECTS) 49 | 50 | 51 | dirs: 52 | mkdir -p $(DIST_DIR) 53 | mkdir -p $(BUILD_DIR) -------------------------------------------------------------------------------- /Makefile.osx: -------------------------------------------------------------------------------- 1 | # OS X Makefile 2 | # works on OS X 10.10.1 Yosemite, 3 | # Caffe dependences installed using Homebrew (see Caffe installation instructions), 4 | # using clang and libstdc++ (not libc++) 5 | 6 | # Selects compiler. 7 | CXX=g++ 8 | CXXFLAGS=-c -Wall 9 | 10 | # Change this to your Mathematica installation path. Something like: 11 | # /SystemFiles/IncludeFiles/C 12 | MATH_IDIR=/Applications/Mathematica.app/SystemFiles/IncludeFiles/C/ 13 | # Change these 3 lines to point to your Caffe directory with builded library (Caffe's make distribute) 14 | # and headers. 15 | CAFFE_DIR=/Users/drchajan/devel/C/caffe/distribute 16 | CAFFE_IDIR=$(CAFFE_DIR)/include 17 | CAFFE_LDIR=$(CAFFE_DIR)/lib 18 | 19 | CUDA_IDIR=/Developer/NVIDIA/CUDA-6.5/include 20 | BLAS_IDIR=/System/Library/Frameworks/Accelerate.framework/Versions/Current/Frameworks/vecLib.framework/Versions/Current/Headers/ 21 | 22 | DIST_DIR=dist 23 | BUILD_DIR=build 24 | 25 | IDIR=-I$(CAFFE_IDIR) -I$(MATH_IDIR) -I$(CUDA_IDIR) -I$(BLAS_IDIR) 26 | LDIR=-L$(CAFFE_LDIR) 27 | 28 | 29 | LDFLAGS=$(IDIR) $(LDIR) -stdlib=libstdc++ -lstdc++ -lprotobuf -lglog -lcaffe -shared -fPIC 30 | SOURCES=caffeLink.cpp CLnets.cpp utils.cpp\ 31 | libLink_inputs.cpp libLink_outputs.cpp libLink_start.cpp 32 | OBJECTS=$(SOURCES:%.cpp=$(BUILD_DIR)/%.o) 33 | LIBNAME=libcaffeLink.dylib 34 | 35 | 36 | 37 | all: dirs $(SOURCES) $(LIBNAME) 38 | 39 | $(LIBNAME): $(OBJECTS) 40 | $(CXX) $(LDFLAGS) $(OBJECTS) -o $(DIST_DIR)/$@ 41 | 42 | $(BUILD_DIR)/%.o: %.cpp 43 | $(CXX) $(CXXFLAGS) $(LDFLAGS) $< -o $@ 44 | 45 | clean: clean.obj 46 | rm -rf $(DIST_DIR)/$(LIBNAME) 47 | 48 | clean.obj: 49 | rm -rf $(OBJECTS) 50 | 51 | 52 | dirs: 53 | mkdir -p $(DIST_DIR) 54 | mkdir -p $(BUILD_DIR) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CaffeLink 2 | ========= 3 | 4 | Mathematica library link wrapper for [Caffe](https://github.com/BVLC/caffe) 5 | 6 | This library allows using Caffe directly from [Mathematica](http://www.wolfram.com/mathematica/). CaffeLink can be also used as regular library from C++ applications. The interface and function calls are basicly the same as in Mathematica. 7 | 8 | ### Installation 9 | Assuming you have successfully build Caffe, you should have everything needed by CaffeLink. 10 | 11 | 1. Edit makefile as required 12 | * path to Mathematica C headers 13 | * path to Caffe and its headers 14 | 2. `make` 15 | 3. Copy or create link to `libcaffeLink.so` somewhere in `$LibraryPath` 16 | * eg: `/home/alfons/.Mathematica/SystemFiles/LibraryResources/Linux-x86-64/` 17 | * or: `/Users/alfons/Library/Mathematica/Applications/IPCU/LibraryResources/MacOSX-x86-64/` 18 | 4. Test installation with [liblink-test.nb](../master/module/demo/liblink-test.nb) 19 | 20 | ### Usage examples 21 | * [LeNet](http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf) MNIST example based on [Caffe example](https://github.com/BVLC/caffe/tree/master/examples/mnist) 22 | * Mathematica notebook: [mnist.nb](../master/module/demo/mnist.nb) 23 | * Pdf: [mnist.pdf](../master/module/demo/mnist.pdf) 24 | 25 | * [AlexNet](http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks) ImageNet example based on [Caffe example](http://nbviewer.ipython.org/github/BVLC/caffe/blob/master/examples/filter_visualization.ipynb) 26 | * Mathematica notebook: [imageNet.nb](../master/module/demo/imageNet.nb) 27 | * Pdf: [imageNet.pdf](../master/module/demo/imageNet.pdf) -------------------------------------------------------------------------------- /build_utils.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef BUILD_UTILS_HPP 3 | #define BUILD_UTILS_HPP 4 | 5 | /* Mathematica library link necessary agruments. */ 6 | #define LIB_LINK_ARGS WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res 7 | 8 | #ifndef INSTANTIATE_CLASS 9 | // Instantiate a class with float and double specifications. 10 | #define INSTANTIATE_CLASS(classname) \ 11 | template class classname; \ 12 | template class classname 13 | #endif 14 | 15 | #define TRAIN_NEW 0 16 | #define TRAIN_SNAPSHOT 1 17 | #define TRAIN_WEIGHTS 2 18 | 19 | #define MSG_WRONG_ARGS "Wrong arguments, check stdout." 20 | 21 | #endif /* BUILD_UTILS_HPP */ 22 | 23 | -------------------------------------------------------------------------------- /caffeLink.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "WolframLibrary.h" 8 | #include "caffe/caffe.hpp" 9 | 10 | #include "caffeLink.hpp" 11 | #include "build_utils.hpp" 12 | #include "utils.hpp" 13 | #include "CLnets.hpp" 14 | 15 | 16 | static bool useDoubles; 17 | static bool inited = false; 18 | 19 | static CLnets netsD; 20 | static CLnets netsF; 21 | 22 | static float *inputDataF; 23 | static float **paramDataF; 24 | static int *pd; 25 | 26 | extern "C" bool initParamDataF(); 27 | extern "C" void freeParamDataF(); 28 | 29 | extern "C" void initCaffeLink_(bool useDoublesPar, bool useGPU, int devID) 30 | { 31 | if (!inited) { 32 | useDoubles = useDoublesPar; 33 | inited = true; 34 | printf("CaffeLink initiation:\n"); 35 | } else 36 | printf("CaffeLink already initiated:\n"); 37 | 38 | if (useDoubles) 39 | printf(" Net data type: double\n"); 40 | else 41 | printf(" Net data type: float\n"); 42 | 43 | if(useGPU){ 44 | caffe::Caffe::set_mode(caffe::Caffe::GPU); 45 | caffe::Caffe::SetDevice(devID); 46 | printf(" Mode: GPU, dev ID: %d\n", devID); 47 | } 48 | else{ 49 | printf(" Mode: CPU\n"); 50 | caffe::Caffe::set_mode(caffe::Caffe::CPU); 51 | } 52 | 53 | caffe::Caffe::set_phase(caffe::Caffe::TEST); 54 | } 55 | 56 | extern "C" bool isUsingDouble() 57 | { 58 | return useDoubles; 59 | } 60 | 61 | extern "C" bool prepareNetString_(char* paramStr) 62 | { 63 | if (useDoubles) { 64 | if (netsD.prepareNetString(paramStr)) 65 | return netsD.initParamDataLUT(&pd); 66 | } else { 67 | freeParamDataF(); 68 | if (netsF.prepareNetString(paramStr) && netsF.initParamDataLUT(&pd)) 69 | return initParamDataF(); 70 | } 71 | 72 | return false; 73 | } 74 | 75 | extern "C" bool prepareNetFile_(char* path) 76 | { 77 | if (useDoubles) { 78 | if (netsD.prepareNetFile(path)) 79 | return netsD.initParamDataLUT(&pd); 80 | 81 | } else { 82 | freeParamDataF(); 83 | if (netsF.prepareNetFile(path) && netsF.initParamDataLUT(&pd)) 84 | return initParamDataF(); 85 | } 86 | 87 | return false; 88 | } 89 | 90 | extern "C" bool initParamDataF() 91 | { 92 | int i; 93 | paramDataF = (float**) malloc(sizeof(float*) * pd[getLayerNum_()]); 94 | if (!paramDataF) { 95 | printf("%s: allocation failed\n", __FUNCTION__); 96 | return false; 97 | } 98 | 99 | for (i = 0; i < pd[getLayerNum_()]; i++) 100 | paramDataF[i] = NULL; 101 | 102 | return true; 103 | } 104 | 105 | extern "C" void freeParamDataF() 106 | { 107 | int i; 108 | if(!pd || !paramDataF) 109 | return; 110 | 111 | for(i = 0; i < pd[getLayerNum_()]; i++) 112 | if(paramDataF[i]) 113 | free(paramDataF[i]); 114 | 115 | free(paramDataF); 116 | } 117 | 118 | extern "C" void loadNet_(char* path) 119 | { 120 | printf("net data src: %s\n", path); 121 | if(useDoubles) 122 | netsD.loadNet(path); 123 | else 124 | netsF.loadNet(path); 125 | } 126 | 127 | extern "C" void testNet_() 128 | { 129 | caffe::Caffe::set_phase(caffe::Caffe::TEST); 130 | 131 | if(useDoubles) 132 | netsD.testNet(); 133 | else 134 | netsF.testNet(); 135 | } 136 | 137 | extern "C" 138 | bool trainNet_(char *param, bool paramIsFile, int trainMode, char* path) 139 | { 140 | bool success; 141 | caffe::SolverParameter solverParam; 142 | 143 | if (paramIsFile) { 144 | success = caffe::ReadProtoFromTextFile(param, &solverParam); 145 | } else { 146 | success = google::protobuf::TextFormat::ParseFromString(param, &solverParam); 147 | if (!success) { 148 | printf("ERR in %s: Could not create solver params\n", __FUNCTION__); 149 | return false; 150 | } 151 | } 152 | 153 | if(useDoubles) 154 | netsD.trainNet(&solverParam, trainMode, path); 155 | else 156 | netsF.trainNet(&solverParam, trainMode, path); 157 | 158 | return true; 159 | } 160 | 161 | extern "C" bool exportNet_(char* path) 162 | { 163 | if(useDoubles) 164 | return netsD.exportNet(path); 165 | else 166 | return netsF.exportNet(path); 167 | } 168 | 169 | extern "C" void printNetInfo_() 170 | { 171 | /* 172 | * Top and bottom blobs can be accesed via layer index throught net in 173 | * net.top_vecs()[laIdx]. 174 | * Learned parameters can be acceses from layers or directly from net: 175 | * net.layers[laIdx].get().blobs() 176 | * net.params() 177 | * but vector in net.params() is a list of all parameter blobs so indexing 178 | * is not simple since some layers has more than one par. bl. (filters 179 | * and bias...) and some has none. 180 | * 181 | * Blob is caffe's ultimate data storage. Data are interpeted as 4D array 182 | * stored in 1D: 183 | * [image, channel, row, collumn]. 184 | * Which would be in 1D array as: 185 | * blob.data = {img1, img2, ...}, 186 | * img1 = {A,B,...}, 187 | * channel A = {row1, row2, ...}, 188 | * row1 = {col1, col2, ...} 189 | */ 190 | 191 | if (useDoubles) 192 | netsD.printNetInfo(); 193 | else 194 | netsF.printNetInfo(); 195 | } 196 | 197 | extern "C" int getLayerNum_() 198 | { 199 | if (useDoubles) 200 | return netsD.getLayerNum(); 201 | else 202 | return netsF.getLayerNum(); 203 | } 204 | 205 | extern "C" int getLayerIdx_(char* name) 206 | { 207 | if (useDoubles) 208 | return netsD.getLayerIdx(name); 209 | else 210 | return netsF.getLayerIdx(name); 211 | } 212 | 213 | extern "C" int* getParamDataLUT() 214 | { 215 | return pd; 216 | } 217 | 218 | extern "C" bool getBlobSize_(int *blobSize, bool (*getBlSize)(int**, int*, int), 219 | int layerIdx, int blobIdx) 220 | { 221 | int i; 222 | int *dims, dimSize; 223 | if(!getBlSize(&dims, &dimSize, layerIdx) || dimSize == 0) 224 | return false; 225 | 226 | *blobSize = 1; 227 | for(i = blobIdx * 4; i < (blobIdx + 1) * 4; i++) 228 | *blobSize *= dims[i]; 229 | 230 | free(dims); 231 | return true; 232 | } 233 | 234 | extern "C" bool getTopBlobSize_(int** dims, int* dimSize, int layerIdx) 235 | { 236 | if (useDoubles) 237 | return netsD.getTopBlobSize(dims, dimSize, layerIdx); 238 | else 239 | return netsF.getTopBlobSize(dims, dimSize, layerIdx); 240 | } 241 | 242 | extern "C" bool getBottomBlobSize_(int** dims, int* dimSize, int layerIdx) 243 | { 244 | if (useDoubles) 245 | return netsD.getBottomBlobSize(dims, dimSize, layerIdx); 246 | else 247 | return netsF.getBottomBlobSize(dims, dimSize, layerIdx); 248 | } 249 | 250 | extern "C" bool getParamBlobSize_(int** dims, int* dimSize, int layerIdx) 251 | { 252 | if (useDoubles) 253 | return netsD.getParamBlobSize(dims, dimSize, layerIdx); 254 | else 255 | return netsF.getParamBlobSize(dims, dimSize, layerIdx); 256 | } 257 | 258 | extern "C" bool getInputSize_(int** dims, int* dimSize, int layerIdx) 259 | { 260 | if (useDoubles) 261 | return netsD.getInputSize(dims, dimSize); 262 | else 263 | return netsF.getInputSize(dims, dimSize); 264 | } 265 | 266 | extern "C" bool getTopBlob_(double** data, int layerIdx, int blobIdx) 267 | { 268 | if (useDoubles) 269 | return netsD.getTopBlob(data, layerIdx, blobIdx); 270 | else 271 | return netsF.getTopBlob((float**) data, layerIdx, blobIdx); 272 | } 273 | 274 | extern "C" bool getBottomBlob_(double** data, int layerIdx, int blobIdx) 275 | { 276 | if (useDoubles) 277 | return netsD.getBottomBlob(data, layerIdx, blobIdx); 278 | else 279 | return netsF.getBottomBlob((float**) data, layerIdx, blobIdx); 280 | } 281 | 282 | extern "C" bool getParamBlob_(double** data, int layerIdx, int blobIdx) 283 | { 284 | if (useDoubles) 285 | return netsD.getParamBlob(data, layerIdx, blobIdx); 286 | else 287 | return netsF.getParamBlob((float**) data, layerIdx, blobIdx); 288 | } 289 | 290 | extern "C" bool setTopBlob_(double** data, int layerIdx, int blobIdx) 291 | { 292 | if (useDoubles) 293 | return netsD.setTopBlob(data, layerIdx, blobIdx); 294 | else { 295 | float* tmp; 296 | int blobSize; 297 | if (!getBlobSize_(&blobSize, *getTopBlobSize_, layerIdx, blobIdx)) 298 | return false; 299 | if (!doublesToFloats(*data, &tmp, blobSize)) { 300 | return false; 301 | } 302 | return netsF.setTopBlob(&tmp, layerIdx, blobIdx); 303 | } 304 | } 305 | 306 | extern "C" bool setBottomBlob_(double** data, int layerIdx, int blobIdx) 307 | { 308 | if (useDoubles) 309 | return netsD.setBottomBlob(data, layerIdx, blobIdx); 310 | else { 311 | float* tmp; 312 | int blobSize; 313 | if (!getBlobSize_(&blobSize, *getBottomBlobSize_, layerIdx, blobIdx)) 314 | return false; 315 | if (!doublesToFloats(*data, &tmp, blobSize)) { 316 | return false; 317 | } 318 | return netsF.setBottomBlob(&tmp, layerIdx, blobIdx); 319 | } 320 | } 321 | 322 | extern "C" bool setParamBlob_(double** data, int layerIdx, int blobIdx) 323 | { 324 | if (useDoubles) 325 | return netsD.setParamBlob(data, layerIdx, blobIdx); 326 | else { 327 | int blobSize; 328 | int paramDataIdx = getParamDataLUT()[layerIdx] + blobIdx; 329 | 330 | if (!getBlobSize_(&blobSize, *getParamBlobSize_, layerIdx, blobIdx)) 331 | return false; 332 | if (!doublesToFloats(*data, ¶mDataF[paramDataIdx], blobSize)) { 333 | return false; 334 | } 335 | return netsF.setParamBlob(¶mDataF[paramDataIdx], layerIdx, blobIdx); 336 | } 337 | } 338 | 339 | extern "C" bool setInput_(double **inputData) 340 | { 341 | if(useDoubles) 342 | return netsD.setInput(inputData); 343 | else{ 344 | int blobSize; 345 | if (!getBlobSize_(&blobSize, *getInputSize_, 0, 0)) 346 | return false; 347 | if (!doublesToFloats(*inputData, &inputDataF, blobSize)) { 348 | return false; 349 | } 350 | return netsF.setInput(&inputDataF); 351 | } 352 | } -------------------------------------------------------------------------------- /caffeLink.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: caffeLink.hpp 3 | * Author: kerhy 4 | * 5 | * Created on November 4, 2014, 9:56 PM 6 | */ 7 | 8 | #ifndef CAFFELINK_HPP 9 | #define CAFFELINK_HPP 10 | 11 | #include "WolframLibrary.h" 12 | 13 | 14 | 15 | extern "C" { 16 | 17 | /** 18 | * Sets flag for data type in net. Doubles: no conversion double->float, more 19 | * memory. Float: conversion and copy double->float, less memory. 20 | * @param useDoublesPar \c true for \c doubles 21 | * @param useGPU \c true for GPU mode 22 | * @param devID id of cuda device 23 | */ 24 | void initCaffeLink_(bool useDoublesPar, bool useGPU, int deviceID); 25 | 26 | /** 27 | * Returns \c true if net data type is \c double, \c false otherwise. 28 | * @return 29 | */ 30 | bool isUsingDouble(); 31 | 32 | /** 33 | * Calls \c CLnets.prepareNetString with correct type. 34 | * Creates new net from protobuffer parameters in \n string. 35 | * @param paramStr protobuffer parameters 36 | * @return \c true on success, \c false otherwise 37 | */ 38 | bool prepareNetString_(char* paramStr); 39 | 40 | /** 41 | * Calls \c CLnets.prepareNetFile with correct type. 42 | * Creates new net from protobuffer parameters in file. 43 | * @param path 44 | * @return \c true on success, \c false otherwise 45 | */ 46 | bool prepareNetFile_(char* path); 47 | 48 | /** 49 | * Calls \c CLnets.loadNet with correct type. 50 | * Loads previously learned net from caffemodel file. The caffemodel must agree 51 | * with prepared net. This function should be called only after some net was 52 | * alredy initialized (prepared). 53 | * @param path 54 | */ 55 | void loadNet_(char* path); 56 | 57 | /** 58 | * Calls \c CLnets.testNet with correct type. 59 | * Runs net in test mode. 60 | */ 61 | void testNet_(); 62 | 63 | /** 64 | * Parses solver parameters from file or string. 65 | * Calls \c CLnets.trainNet with correct type. 66 | * @param param whole solver protobuffer or path to protobuffer file 67 | * @param paramIsFile whether \c contains path or protobuffer 68 | * @param trainMode TRAIN_NEW, TRAIN_SNAPSHOT or TRAIN_WEIGHTS 69 | * @param path path to snapshot or weights if selected in \c trainMode 70 | * @return \c true on success, \c false otherwise 71 | */ 72 | bool trainNet_(char *param, bool paramIsFile, int trainMode, char* path); 73 | 74 | /** 75 | * Calls \c CLnets.exportNet with correct type. 76 | * Exports net to given file path. 77 | * @param path 78 | * @return \c true on success, \c false otherwise 79 | */ 80 | bool exportNet_(char* path); 81 | 82 | /** 83 | * Calls \c CLnets.printNetInfo with correct type. 84 | * Prints net and layer parameters: layer names, top, bottom counts... 85 | */ 86 | void printNetInfo_(); 87 | 88 | /** 89 | * Calls \c CLnets.getLayerNum with correct type. 90 | * Returns number of layers. 91 | * @return 92 | */ 93 | int getLayerNum_(); 94 | 95 | /** 96 | * Returns index of layer with given name. 97 | * @param name layer name 98 | * @return 99 | */ 100 | int getLayerIdx_(char* name); 101 | 102 | /** 103 | * Returns array of size num. layers + 1 with starting indices of parameter 104 | * data of each layer. Last cell contains size of parameter data array. 105 | * @return 106 | */ 107 | int* getParamDataLUT(); 108 | 109 | /** 110 | * Calculates blob size using given function and stores it to given pointer. 111 | * Returns false on failure 112 | * or if size == 0. 113 | * 114 | * @param blobSize num * ch * w * h 115 | * @param getBlSize f. p. relevant to blob type 116 | * @param layerIdx layer index 117 | * @param blobIdx blob index 118 | * @return \c true on success, \c false otherwise 119 | */ 120 | bool getBlobSize_(int *blobSize, 121 | bool (*getBlSize)(int**, int*, int), int layerIdx, int blobIdx); 122 | 123 | /** 124 | * Stores dimensions of all top blobs in layer \c layerIdx into pointer \c dims. 125 | * @param dims array of blob dimensons 126 | * @param dimSize length of \c dims 127 | * @param layerIdx layer index 128 | * @return \c true on success, \c false otherwise 129 | */ 130 | bool getTopBlobSize_(int** dims, int* dimSize, int layerIdx); 131 | 132 | /** 133 | * Stores dimensions of all bottom blobs in layer \c layerIdx into pointer \c dims. 134 | * @param dims array of blob dimensons 135 | * @param dimSize length of \c dims 136 | * @param layerIdx layer index 137 | * @return \c true on success, \c false otherwise 138 | */ 139 | bool getBottomBlobSize_(int** dims, int* dimSize, int layerIdx); 140 | 141 | /** 142 | * Stores dimensions of all param blobs in layer \c layerIdx into pointer \c dims. 143 | * @param dims array of blob dimensons 144 | * @param dimSize length of \c dims 145 | * @param layerIdx layer index 146 | * @return \c true on success, \c false otherwise 147 | */ 148 | bool getParamBlobSize_(int** dims, int* dimSize, int layerIdx); 149 | 150 | /** 151 | * Stores dimensions of input blobs into pointer \c dims. 152 | * @param dims array of blob dimensons 153 | * @param dimSize length of \c dims 154 | * @param layerIdx not used 155 | * @return \c true on success, \c false otherwise 156 | */ 157 | bool getInputSize_(int** dims, int* dimSize, int layerIdx); 158 | 159 | /** 160 | * Stores pointer to cpu data into \c data of top blob \c blobIdx in 161 | * layer \c layerIdx. 162 | * @param data 163 | * @param layerIdx layer index 164 | * @param blobIdx blob index 165 | * @return \c true on success, \c false otherwise 166 | */ 167 | bool getTopBlob_(double** data, int layerIdx, int blobIdx); 168 | 169 | /** 170 | * Stores pointer to cpu data into \c data of bottom blob \c blobIdx in 171 | * layer \c layerIdx. 172 | * @param data 173 | * @param layerIdx layer index 174 | * @param blobIdx blob index 175 | * @return \c true on success, \c false otherwise 176 | */ 177 | bool getBottomBlob_(double** data, int layerIdx, int blobIdx); 178 | 179 | /** 180 | * Stores pointer to cpu data into \c data of param blob \c blobIdx in 181 | * layer \c layerIdx. 182 | * @param data 183 | * @param layerIdx layer index 184 | * @param blobIdx blob index 185 | * @return \c true on success, \c false otherwise 186 | */ 187 | bool getParamBlob_(double** data, int layerIdx, int blobIdx); 188 | 189 | /** 190 | * Sets \c data as cpu data in top blob \c blobIdx in layer \c layerIdx. 191 | * @param data 192 | * @param layerIdx layer index 193 | * @param blobIdx blob index 194 | * @return \c true on success, \c false otherwise 195 | */ 196 | bool setTopBlob_(double** data, int layerIdx, int blobIdx); 197 | 198 | /** 199 | * Sets \c data as cpu data in bottom blob \c blobIdx in layer \c layerIdx. 200 | * @param data 201 | * @param layerIdx layer index 202 | * @param blobIdx blob index 203 | * @return \c true on success, \c false otherwise 204 | */ 205 | bool setBottomBlob_(double** data, int layerIdx, int blobIdx); 206 | 207 | /** 208 | * Sets \c data as cpu data in param blob \c blobIdx in layer \c layerIdx. 209 | * @param data 210 | * @param layerIdx layer index 211 | * @param blobIdx blob index 212 | * @return \c true on success, \c false otherwise 213 | */ 214 | bool setParamBlob_(double** data, int layerIdx, int blobIdx); 215 | 216 | /** 217 | * * Calls \c CLnets.setInput with correct type. 218 | * Inserts input for net - whole batch of images. InputData is float or double 219 | * 1D array with size of B * K * H * W where B is batch size (num. of images), K 220 | * is num of channels in each image and H and W are dimensions of all images. 221 | * Data layout: {img01, img02, ...}, img01={ch01, ch02, ...}, 222 | * ch01={row1, row2, ...}, row1 = {col1, col2, ...} 223 | * @param inputData 224 | * @return \c true on success, \c false otherwise 225 | */ 226 | bool setInput_(double **inputData); 227 | 228 | } 229 | 230 | #endif /* CAFFELINK_HPP */ 231 | 232 | -------------------------------------------------------------------------------- /libLink_inputs.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "WolframLibrary.h" 6 | #include "caffe/caffe.hpp" 7 | 8 | #include "build_utils.hpp" 9 | #include "caffeLink.hpp" 10 | #include "libLink_inputs.hpp" 11 | 12 | static MTensor inputBlobMT; 13 | static MTensor *paramDataMT; 14 | 15 | extern "C" bool initParamDataMT() 16 | { 17 | int i; 18 | int *pd; 19 | pd = getParamDataLUT(); 20 | 21 | paramDataMT = (MTensor*) malloc(sizeof(MTensor) * pd[getLayerNum_()]); 22 | if (!paramDataMT) { 23 | printf("%s: allocation failed\n", __FUNCTION__); 24 | return false; 25 | } 26 | 27 | for (i = 0; i < pd[getLayerNum_()]; i++) 28 | paramDataMT[i] = NULL; 29 | 30 | return true; 31 | } 32 | 33 | extern "C" void freeParamDataMT(WolframLibraryData libData) 34 | { 35 | int i; 36 | int *pd; 37 | pd = getParamDataLUT(); 38 | if(!pd || !paramDataMT) 39 | return; 40 | 41 | for(i = 0; i < pd[getLayerNum_()]; i++) 42 | if(paramDataMT[i]) 43 | libData->MTensor_free(paramDataMT[i]); 44 | 45 | free(paramDataMT); 46 | } 47 | 48 | /** Mathematica librarylink wrapper for \c setTopBlob_.*/ 49 | extern "C" DLLEXPORT int setTopBlob(LIB_LINK_ARGS) 50 | { 51 | MTensor blobMT; 52 | int layerIdx; 53 | int blobIdx; 54 | double *data; 55 | 56 | if(Argc != 3){ 57 | printf("ERR: %s takes 3 argument: Real tensor data, Integer layer index" 58 | "and Integer blob index\n", 59 | __FUNCTION__); 60 | libData->Message(MSG_WRONG_ARGS); 61 | return LIBRARY_FUNCTION_ERROR; 62 | } 63 | 64 | blobMT = MArgument_getMTensor(Args[0]); 65 | layerIdx = MArgument_getInteger(Args[1]); 66 | blobIdx = MArgument_getInteger(Args[2]); 67 | 68 | data = libData->MTensor_getRealData(blobMT); 69 | if(!setTopBlob_(&data, layerIdx, blobIdx)) 70 | return LIBRARY_FUNCTION_ERROR; 71 | 72 | return LIBRARY_NO_ERROR; 73 | } 74 | 75 | /** Mathematica librarylink wrapper for \c setBottomBlob_.*/ 76 | extern "C" DLLEXPORT int setBottomBlob(LIB_LINK_ARGS) 77 | { 78 | MTensor blobMT; 79 | int layerIdx; 80 | int blobIdx; 81 | double *data; 82 | 83 | if(Argc != 3){ 84 | printf("ERR: %s takes 3 argument: Real tensor data, Integer layer index" 85 | "and Integer blob index\n", 86 | __FUNCTION__); 87 | libData->Message(MSG_WRONG_ARGS); 88 | return LIBRARY_FUNCTION_ERROR; 89 | } 90 | 91 | blobMT = MArgument_getMTensor(Args[0]); 92 | layerIdx = MArgument_getInteger(Args[1]); 93 | blobIdx = MArgument_getInteger(Args[2]); 94 | 95 | data = libData->MTensor_getRealData(blobMT); 96 | if(!setBottomBlob_(&data, layerIdx, blobIdx)) 97 | return LIBRARY_FUNCTION_ERROR; 98 | 99 | return LIBRARY_NO_ERROR; 100 | } 101 | 102 | /** Mathematica librarylink wrapper for \c setParamBlob_.*/ 103 | extern "C" DLLEXPORT int setParamBlob(LIB_LINK_ARGS) 104 | { 105 | MTensor blobMT; 106 | int layerIdx; 107 | int blobIdx; 108 | double *data; 109 | int parDataIdx; 110 | 111 | if(Argc != 3){ 112 | printf("ERR: %s takes 3 argument: Real tensor data, Integer layer index" 113 | "and Integer blob index\n", 114 | __FUNCTION__); 115 | libData->Message(MSG_WRONG_ARGS); 116 | return LIBRARY_FUNCTION_ERROR; 117 | } 118 | 119 | blobMT = MArgument_getMTensor(Args[0]); 120 | layerIdx = MArgument_getInteger(Args[1]); 121 | blobIdx = MArgument_getInteger(Args[2]); 122 | 123 | data = libData->MTensor_getRealData(blobMT); 124 | if(!setParamBlob_(&data, layerIdx, blobIdx)){ 125 | libData->MTensor_free(blobMT); 126 | return LIBRARY_FUNCTION_ERROR; 127 | } 128 | 129 | if (!isUsingDouble()) 130 | /* MTensor (doubles) was converted to float, hence is not needed */ 131 | libData->MTensor_free(blobMT); 132 | else { 133 | /* free previous MTensor and store the new one */ 134 | parDataIdx = getParamDataLUT()[layerIdx] + blobIdx; 135 | if (paramDataMT[parDataIdx]) 136 | libData->MTensor_free(paramDataMT[parDataIdx]); 137 | paramDataMT[parDataIdx] = blobMT; 138 | } 139 | 140 | return LIBRARY_NO_ERROR; 141 | } 142 | 143 | /** Mathematica librarylink wrapper for \c setTopBlob_ called by layer name.*/ 144 | extern "C" DLLEXPORT int setTopBlobLName(LIB_LINK_ARGS) 145 | { 146 | MTensor blobMT; 147 | char* name; 148 | int layerIdx; 149 | int blobIdx; 150 | double *data; 151 | 152 | if(Argc != 3){ 153 | printf("ERR: %s takes 3 argument: Real tensor data, UTF8String layer" 154 | "name and Integer blob index\n", 155 | __FUNCTION__); 156 | libData->Message(MSG_WRONG_ARGS); 157 | return LIBRARY_FUNCTION_ERROR; 158 | } 159 | 160 | blobMT = MArgument_getMTensor(Args[0]); 161 | name = MArgument_getUTF8String(Args[1]); 162 | layerIdx = getLayerIdx_(name); 163 | if(layerIdx == -1) 164 | return LIBRARY_FUNCTION_ERROR; 165 | 166 | blobIdx = MArgument_getInteger(Args[2]); 167 | 168 | data = libData->MTensor_getRealData(blobMT); 169 | if(!setTopBlob_(&data, layerIdx, blobIdx)) 170 | return LIBRARY_FUNCTION_ERROR; 171 | 172 | return LIBRARY_NO_ERROR; 173 | } 174 | 175 | /** Mathematica librarylink wrapper for \c setBottomBlob_ called by layer name.*/ 176 | extern "C" DLLEXPORT int setBottomBlobLName(LIB_LINK_ARGS) 177 | { 178 | MTensor blobMT; 179 | char* name; 180 | int layerIdx; 181 | int blobIdx; 182 | double *data; 183 | 184 | if(Argc != 3){ 185 | printf("ERR: %s takes 3 argument: Real tensor data, UTF8String layer" 186 | "name and Integer blob index\n", 187 | __FUNCTION__); 188 | libData->Message(MSG_WRONG_ARGS); 189 | return LIBRARY_FUNCTION_ERROR; 190 | } 191 | 192 | blobMT = MArgument_getMTensor(Args[0]); 193 | name = MArgument_getUTF8String(Args[1]); 194 | layerIdx = getLayerIdx_(name); 195 | if(layerIdx == -1) 196 | return LIBRARY_FUNCTION_ERROR; 197 | 198 | blobIdx = MArgument_getInteger(Args[2]); 199 | 200 | data = libData->MTensor_getRealData(blobMT); 201 | if(!setBottomBlob_(&data, layerIdx, blobIdx)) 202 | return LIBRARY_FUNCTION_ERROR; 203 | 204 | return LIBRARY_NO_ERROR; 205 | } 206 | 207 | /** Mathematica librarylink wrapper for \c setParamBlob_ called by layer name.*/ 208 | extern "C" DLLEXPORT int setParamBlobLName(LIB_LINK_ARGS) 209 | { 210 | MTensor blobMT; 211 | char* name; 212 | int layerIdx; 213 | int blobIdx; 214 | double *data; 215 | int parDataIdx; 216 | 217 | if(Argc != 3){ 218 | printf("ERR: %s takes 3 argument: Real tensor data, UTF8String layer" 219 | "name and Integer blob index\n", 220 | __FUNCTION__); 221 | libData->Message(MSG_WRONG_ARGS); 222 | return LIBRARY_FUNCTION_ERROR; 223 | } 224 | 225 | blobMT = MArgument_getMTensor(Args[0]); 226 | name = MArgument_getUTF8String(Args[1]); 227 | layerIdx = getLayerIdx_(name); 228 | if(layerIdx == -1){ 229 | libData->MTensor_free(blobMT); 230 | return LIBRARY_FUNCTION_ERROR; 231 | } 232 | 233 | blobIdx = MArgument_getInteger(Args[2]); 234 | 235 | data = libData->MTensor_getRealData(blobMT); 236 | if (!setParamBlob_(&data, layerIdx, blobIdx)) { 237 | libData->MTensor_free(blobMT); 238 | return LIBRARY_FUNCTION_ERROR; 239 | } 240 | 241 | if (!isUsingDouble()) 242 | /* MTensor (doubles) was converted to float, hence is not needed */ 243 | libData->MTensor_free(blobMT); 244 | else { 245 | /* free previous MTensor and store the new one */ 246 | parDataIdx = getParamDataLUT()[layerIdx] + blobIdx; 247 | if (paramDataMT[parDataIdx]) 248 | libData->MTensor_free(paramDataMT[parDataIdx]); 249 | paramDataMT[parDataIdx] = blobMT; 250 | } 251 | 252 | return LIBRARY_NO_ERROR; 253 | } 254 | 255 | /** Mathematica librarylink wrapper for \c setInput_.*/ 256 | extern "C" DLLEXPORT int setInput(LIB_LINK_ARGS) 257 | { 258 | double *data; 259 | 260 | if(Argc != 1){ 261 | printf("ERR: %s takes 1 argument: Real tensor data, with size" 262 | "of input blob\n", 263 | __FUNCTION__); 264 | libData->Message(MSG_WRONG_ARGS); 265 | return LIBRARY_FUNCTION_ERROR; 266 | } 267 | 268 | if(inputBlobMT) 269 | libData->MTensor_free(inputBlobMT); 270 | inputBlobMT = MArgument_getMTensor(Args[0]); 271 | 272 | data = libData->MTensor_getRealData(inputBlobMT); 273 | if(!setInput_(&data)) 274 | return LIBRARY_FUNCTION_ERROR; 275 | 276 | return LIBRARY_NO_ERROR; 277 | } 278 | -------------------------------------------------------------------------------- /libLink_inputs.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef LIBLINK_INPUTS_HPP 3 | #define LIBLINK_INPUTS_HPP 4 | 5 | extern "C" { 6 | 7 | bool initParamDataMT(); 8 | void freeParamDataMT(WolframLibraryData libData); 9 | 10 | } 11 | 12 | #endif /* LIBLINK_INPUTS_HPP */ 13 | 14 | -------------------------------------------------------------------------------- /libLink_outputs.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "WolframLibrary.h" 6 | #include "caffe/caffe.hpp" 7 | 8 | #include "build_utils.hpp" 9 | #include "caffeLink.hpp" 10 | 11 | /** Mathematica librarylink wrapper for \c exportNet_.*/ 12 | extern "C" DLLEXPORT int exportNet(LIB_LINK_ARGS) 13 | { 14 | char* path; 15 | 16 | if (Argc != 1) { 17 | printf("ERR: %s takes 1 argument: UTF8String path to net data\n", 18 | __FUNCTION__); 19 | libData->Message(MSG_WRONG_ARGS); 20 | return LIBRARY_FUNCTION_ERROR; 21 | } 22 | 23 | path = MArgument_getUTF8String(Args[0]); 24 | exportNet_(path); 25 | 26 | return LIBRARY_NO_ERROR; 27 | } 28 | 29 | /** Prints root directory in stdout. */ 30 | extern "C" DLLEXPORT int printWorkingPath(LIB_LINK_ARGS) 31 | { 32 | if (Argc != 0) { 33 | printf("WRN: %s takes 0 arguments\n", __FUNCTION__); 34 | } 35 | 36 | system("pwd"); 37 | return LIBRARY_NO_ERROR; 38 | } 39 | 40 | /** Mathematica librarylink wrapper for \c printNetInfo_.*/ 41 | extern "C" DLLEXPORT int printNetInfo(LIB_LINK_ARGS) 42 | { 43 | if (Argc != 0) { 44 | printf("WRN: %s takes 0 arguments\n", __FUNCTION__); 45 | } 46 | 47 | printNetInfo_(); 48 | return LIBRARY_NO_ERROR; 49 | } 50 | 51 | /** Mathematica librarylink wrapper for \c getLayerNum_.*/ 52 | extern "C" DLLEXPORT int getLayerNum(LIB_LINK_ARGS) 53 | { 54 | if (Argc != 0) { 55 | printf("WRN: %s takes 0 arguments\n", __FUNCTION__); 56 | } 57 | 58 | MArgument_setInteger(Res, getLayerNum_()); 59 | return LIBRARY_NO_ERROR; 60 | } 61 | 62 | bool getBlobSize(WolframLibraryData libData, 63 | MTensor *dimsMT, int layerIdx, bool (*getBlSize)(int**, int*, int)) 64 | { 65 | int err, i; 66 | int *dims, dimSize; 67 | mint *data, mtSize; 68 | 69 | if(!getBlSize(&dims, &dimSize, layerIdx)) 70 | return false; 71 | 72 | mtSize = dimSize; 73 | err = libData->MTensor_new(MType_Integer, 1, &mtSize, dimsMT); 74 | if (err) { 75 | printf("ERR %d, could not create new MTensor\n", err); 76 | return false; 77 | } 78 | 79 | data = libData->MTensor_getIntegerData(*dimsMT); 80 | for(i = 0; i < dimSize; i++) 81 | data[i] = dims[i]; 82 | 83 | if(dimSize != 0) 84 | free(dims); 85 | 86 | return true; 87 | } 88 | 89 | /** Mathematica librarylink wrapper for \c getTopBlobSize_.*/ 90 | extern "C" DLLEXPORT int getTopBlobSize(LIB_LINK_ARGS) 91 | { 92 | MTensor dimsMT; 93 | int layerIdx; 94 | 95 | if(Argc != 1){ 96 | printf("ERR: %s takes 1 argument: Integer layer index\n", 97 | __FUNCTION__); 98 | libData->Message(MSG_WRONG_ARGS); 99 | return LIBRARY_FUNCTION_ERROR; 100 | } 101 | 102 | layerIdx = MArgument_getInteger(Args[0]); 103 | if (!getBlobSize(libData, &dimsMT, layerIdx, &getTopBlobSize_)) 104 | return LIBRARY_FUNCTION_ERROR; 105 | 106 | MArgument_setMTensor(Res, dimsMT); 107 | return LIBRARY_NO_ERROR; 108 | } 109 | 110 | /** Mathematica librarylink wrapper for \c getBottomBlobSize_.*/ 111 | extern "C" DLLEXPORT int getBottomBlobSize(LIB_LINK_ARGS) 112 | { 113 | MTensor dimsMT; 114 | int layerIdx; 115 | 116 | if(Argc != 1){ 117 | printf("ERR: %s takes 1 argument: Integer layer index\n", 118 | __FUNCTION__); 119 | libData->Message(MSG_WRONG_ARGS); 120 | return LIBRARY_FUNCTION_ERROR; 121 | } 122 | 123 | layerIdx = MArgument_getInteger(Args[0]); 124 | if (!getBlobSize(libData, &dimsMT, layerIdx, &getBottomBlobSize_)) 125 | return LIBRARY_FUNCTION_ERROR; 126 | 127 | MArgument_setMTensor(Res, dimsMT); 128 | return LIBRARY_NO_ERROR; 129 | } 130 | 131 | /** Mathematica librarylink wrapper for \c getParamBlobSize_.*/ 132 | extern "C" DLLEXPORT int getParamBlobSize(LIB_LINK_ARGS) 133 | { 134 | MTensor dimsMT; 135 | int layerIdx; 136 | 137 | if(Argc != 1){ 138 | printf("ERR: %s takes 1 argument: Integer layer index\n", 139 | __FUNCTION__); 140 | libData->Message(MSG_WRONG_ARGS); 141 | return LIBRARY_FUNCTION_ERROR; 142 | } 143 | 144 | layerIdx = MArgument_getInteger(Args[0]); 145 | if (!getBlobSize(libData, &dimsMT, layerIdx, &getParamBlobSize_)) 146 | return LIBRARY_FUNCTION_ERROR; 147 | 148 | MArgument_setMTensor(Res, dimsMT); 149 | return LIBRARY_NO_ERROR; 150 | } 151 | 152 | /** Mathematica librarylink wrapper for \c getTopBlobSize_ called by layer name.*/ 153 | extern "C" DLLEXPORT int getTopBlobSizeLName(LIB_LINK_ARGS) 154 | { 155 | MTensor dimsMT; 156 | char* name; 157 | int layerIdx; 158 | 159 | if(Argc != 1){ 160 | printf("ERR: %s takes 1 arguments: UTF8String layer name\n", 161 | __FUNCTION__); 162 | libData->Message(MSG_WRONG_ARGS); 163 | return LIBRARY_FUNCTION_ERROR; 164 | } 165 | 166 | name = MArgument_getUTF8String(Args[0]); 167 | layerIdx = getLayerIdx_(name); 168 | if(layerIdx == -1) 169 | return LIBRARY_FUNCTION_ERROR; 170 | 171 | if (!getBlobSize(libData, &dimsMT, layerIdx, &getTopBlobSize_)) 172 | return LIBRARY_FUNCTION_ERROR; 173 | 174 | MArgument_setMTensor(Res, dimsMT); 175 | return LIBRARY_NO_ERROR; 176 | } 177 | 178 | /** Mathematica librarylink wrapper for \c getBottomBlobSize_ called by layer name.*/ 179 | extern "C" DLLEXPORT int getBottomBlobSizeLName(LIB_LINK_ARGS) 180 | { 181 | MTensor dimsMT; 182 | char* name; 183 | int layerIdx; 184 | 185 | if(Argc != 1){ 186 | printf("ERR: %s takes 1 arguments: UTF8String layer name\n", 187 | __FUNCTION__); 188 | libData->Message(MSG_WRONG_ARGS); 189 | return LIBRARY_FUNCTION_ERROR; 190 | } 191 | 192 | name = MArgument_getUTF8String(Args[0]); 193 | layerIdx = getLayerIdx_(name); 194 | if(layerIdx == -1) 195 | return LIBRARY_FUNCTION_ERROR; 196 | 197 | if (!getBlobSize(libData, &dimsMT, layerIdx, &getBottomBlobSize_)) 198 | return LIBRARY_FUNCTION_ERROR; 199 | 200 | MArgument_setMTensor(Res, dimsMT); 201 | return LIBRARY_NO_ERROR; 202 | } 203 | 204 | /** Mathematica librarylink wrapper for \c getParamBlobSize_ called by layer name.*/ 205 | extern "C" DLLEXPORT int getParamBlobSizeLName(LIB_LINK_ARGS) 206 | { 207 | MTensor dimsMT; 208 | char* name; 209 | int layerIdx; 210 | 211 | if(Argc != 1){ 212 | printf("ERR: %s takes 1 arguments: UTF8String layer name\n", 213 | __FUNCTION__); 214 | libData->Message(MSG_WRONG_ARGS); 215 | return LIBRARY_FUNCTION_ERROR; 216 | } 217 | 218 | name = MArgument_getUTF8String(Args[0]); 219 | layerIdx = getLayerIdx_(name); 220 | if(layerIdx == -1) 221 | return LIBRARY_FUNCTION_ERROR; 222 | 223 | if (!getBlobSize(libData, &dimsMT, layerIdx, &getParamBlobSize_)) 224 | return LIBRARY_FUNCTION_ERROR; 225 | 226 | MArgument_setMTensor(Res, dimsMT); 227 | return LIBRARY_NO_ERROR; 228 | } 229 | 230 | 231 | 232 | bool getBlob(WolframLibraryData libData, MTensor *blobMT, 233 | int layerIdx, int blobIdx, 234 | bool (*getBl)(double**, int, int), bool (*getBlSize)(int**, int*, int)) 235 | { 236 | int err, i, blobSize; 237 | mint mtSize; 238 | double *data; 239 | double *dataD; 240 | float *dataF; 241 | 242 | if (isUsingDouble()) { 243 | if (!getBl(&dataD, layerIdx, blobIdx)) 244 | return false; 245 | } else { 246 | if (!getBl((double**) &dataF, layerIdx, blobIdx)) 247 | return false; 248 | } 249 | 250 | if(!getBlobSize_(&blobSize, getBlSize, layerIdx, blobIdx)) 251 | return false; 252 | 253 | mtSize = blobSize; 254 | err = libData->MTensor_new(MType_Real, 1, &mtSize, blobMT); 255 | if (err){ 256 | printf("ERR %d, could not create new MTensor\n", err); 257 | return false; 258 | } 259 | 260 | data = libData->MTensor_getRealData(*blobMT); 261 | 262 | if (isUsingDouble()) { 263 | for(i = 0; i < mtSize; i++) 264 | data[i] = dataD[i]; 265 | } else { 266 | for(i = 0; i < mtSize; i++) 267 | data[i] = dataF[i]; 268 | } 269 | 270 | return true; 271 | } 272 | 273 | /** Mathematica librarylink wrapper for \c getTopBlob_.*/ 274 | extern "C" DLLEXPORT int getTopBlob(LIB_LINK_ARGS) 275 | { 276 | MTensor blobMT; 277 | int layerIdx; 278 | int blobIdx; 279 | 280 | if(Argc != 2){ 281 | printf("ERR: %s takes 2 arguments: Integer layer index " 282 | "and Integer blob index\n", 283 | __FUNCTION__); 284 | libData->Message(MSG_WRONG_ARGS); 285 | return LIBRARY_FUNCTION_ERROR; 286 | } 287 | 288 | layerIdx = MArgument_getInteger(Args[0]); 289 | blobIdx = MArgument_getInteger(Args[1]); 290 | if (!getBlob(libData, &blobMT, layerIdx, blobIdx, &getTopBlob_, &getTopBlobSize_)) 291 | return LIBRARY_FUNCTION_ERROR; 292 | 293 | MArgument_setMTensor(Res, blobMT); 294 | return LIBRARY_NO_ERROR; 295 | } 296 | 297 | /** Mathematica librarylink wrapper for \c getBottomBlob_.*/ 298 | extern "C" DLLEXPORT int getBottomBlob(LIB_LINK_ARGS) 299 | { 300 | MTensor blobMT; 301 | int layerIdx; 302 | int blobIdx; 303 | 304 | if(Argc != 2){ 305 | printf("ERR: %s takes 2 arguments: Integer layer index " 306 | "and Integer blob index\n", 307 | __FUNCTION__); 308 | libData->Message(MSG_WRONG_ARGS); 309 | return LIBRARY_FUNCTION_ERROR; 310 | } 311 | 312 | layerIdx = MArgument_getInteger(Args[0]); 313 | blobIdx = MArgument_getInteger(Args[1]); 314 | if (!getBlob(libData, &blobMT, layerIdx, blobIdx, &getBottomBlob_, &getBottomBlobSize_)) 315 | return LIBRARY_FUNCTION_ERROR; 316 | 317 | MArgument_setMTensor(Res, blobMT); 318 | return LIBRARY_NO_ERROR; 319 | } 320 | 321 | /** Mathematica librarylink wrapper for \c getParamBlob_.*/ 322 | extern "C" DLLEXPORT int getParamBlob(LIB_LINK_ARGS) 323 | { 324 | MTensor blobMT; 325 | int layerIdx; 326 | int blobIdx; 327 | 328 | if(Argc != 2){ 329 | printf("ERR: %s takes 2 arguments: Integer layer index " 330 | "and Integer blob index\n", 331 | __FUNCTION__); 332 | libData->Message(MSG_WRONG_ARGS); 333 | return LIBRARY_FUNCTION_ERROR; 334 | } 335 | 336 | layerIdx = MArgument_getInteger(Args[0]); 337 | blobIdx = MArgument_getInteger(Args[1]); 338 | if (!getBlob(libData, &blobMT, layerIdx, blobIdx, &getParamBlob_, &getParamBlobSize_)) 339 | return LIBRARY_FUNCTION_ERROR; 340 | 341 | MArgument_setMTensor(Res, blobMT); 342 | return LIBRARY_NO_ERROR; 343 | } 344 | 345 | 346 | /** Mathematica librarylink wrapper for \c getTopBlob_ called by layer name.*/ 347 | extern "C" DLLEXPORT int getTopBlobLName(LIB_LINK_ARGS) 348 | { 349 | MTensor blobMT; 350 | char* name; 351 | int layerIdx; 352 | int blobIdx; 353 | 354 | if(Argc != 2){ 355 | printf("ERR: %s takes 2 arguments: UTF8String layer name " 356 | "and Integer blob index\n", 357 | __FUNCTION__); 358 | libData->Message(MSG_WRONG_ARGS); 359 | return LIBRARY_FUNCTION_ERROR; 360 | } 361 | 362 | name = MArgument_getUTF8String(Args[0]); 363 | layerIdx = getLayerIdx_(name); 364 | if(layerIdx == -1) 365 | return LIBRARY_FUNCTION_ERROR; 366 | 367 | blobIdx = MArgument_getInteger(Args[1]); 368 | if (!getBlob(libData, &blobMT, layerIdx, blobIdx, &getTopBlob_, &getTopBlobSize_)) 369 | return LIBRARY_FUNCTION_ERROR; 370 | 371 | MArgument_setMTensor(Res, blobMT); 372 | return LIBRARY_NO_ERROR; 373 | } 374 | 375 | /** Mathematica librarylink wrapper for \c getBottomBlob_ called by layer name.*/ 376 | extern "C" DLLEXPORT int getBottomBlobLName(LIB_LINK_ARGS) 377 | { 378 | MTensor blobMT; 379 | char* name; 380 | int layerIdx; 381 | int blobIdx; 382 | 383 | if(Argc != 2){ 384 | printf("ERR: %s takes 2 arguments: UTF8String layer name " 385 | "and Integer blob index\n", 386 | __FUNCTION__); 387 | libData->Message(MSG_WRONG_ARGS); 388 | return LIBRARY_FUNCTION_ERROR; 389 | } 390 | 391 | name = MArgument_getUTF8String(Args[0]); 392 | layerIdx = getLayerIdx_(name); 393 | if(layerIdx == -1) 394 | return LIBRARY_FUNCTION_ERROR; 395 | 396 | blobIdx = MArgument_getInteger(Args[1]); 397 | if (!getBlob(libData, &blobMT, layerIdx, blobIdx, &getBottomBlob_, &getBottomBlobSize_)) 398 | return LIBRARY_FUNCTION_ERROR; 399 | 400 | MArgument_setMTensor(Res, blobMT); 401 | return LIBRARY_NO_ERROR; 402 | } 403 | 404 | /** Mathematica librarylink wrapper for \c getParamBlob_ called by layer name.*/ 405 | extern "C" DLLEXPORT int getParamBlobLName(LIB_LINK_ARGS) 406 | { 407 | MTensor blobMT; 408 | char* name; 409 | int layerIdx; 410 | int blobIdx; 411 | 412 | if(Argc != 2){ 413 | printf("ERR: %s takes 2 arguments: UTF8String layer name " 414 | "and Integer blob index\n", 415 | __FUNCTION__); 416 | libData->Message(MSG_WRONG_ARGS); 417 | return LIBRARY_FUNCTION_ERROR; 418 | } 419 | 420 | name = MArgument_getUTF8String(Args[0]); 421 | layerIdx = getLayerIdx_(name); 422 | if(layerIdx == -1) 423 | return LIBRARY_FUNCTION_ERROR; 424 | 425 | blobIdx = MArgument_getInteger(Args[1]); 426 | if (!getBlob(libData, &blobMT, layerIdx, blobIdx, &getParamBlob_, &getParamBlobSize_)) 427 | return LIBRARY_FUNCTION_ERROR; 428 | 429 | MArgument_setMTensor(Res, blobMT); 430 | return LIBRARY_NO_ERROR; 431 | } 432 | -------------------------------------------------------------------------------- /libLink_start.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "caffe/caffe.hpp" 5 | #include "WolframLibrary.h" 6 | 7 | #include "build_utils.hpp" 8 | #include "caffeLink.hpp" 9 | #include "libLink_inputs.hpp" 10 | 11 | 12 | /* Necessary for Mathematica library link. */ 13 | extern "C" DLLEXPORT mint WolframLibrary_getVersion() 14 | { 15 | return WolframLibraryVersion; 16 | } 17 | 18 | /* Necessary for Mathematica library link. */ 19 | extern "C" DLLEXPORT int WolframLibrary_initialize(WolframLibraryData libData) 20 | { 21 | return 0; 22 | } 23 | 24 | /* Necessary for Mathematica library link. */ 25 | extern "C" DLLEXPORT void WolframLibrary_uninitialize(WolframLibraryData libData) 26 | { 27 | } 28 | 29 | /** Mathematica librarylink wrapper for \c initCaffeLink_.*/ 30 | extern "C" DLLEXPORT int initCaffeLink(LIB_LINK_ARGS) 31 | { 32 | mbool useDoubles; 33 | mbool useGPU; 34 | mint devID; 35 | 36 | if(Argc != 3){ 37 | printf("ERR: %s takes 3 arguments: \"Boolean\" (True for net using " 38 | "doubles, false for floats), \"Boolean\" (True for GPU, false " 39 | "for CPU mode) and Integer (device ID in case of GPU mode)\n", 40 | __FUNCTION__); 41 | libData->Message(MSG_WRONG_ARGS); 42 | return LIBRARY_FUNCTION_ERROR; 43 | } 44 | 45 | useDoubles = MArgument_getBoolean(Args[0]); 46 | useGPU = MArgument_getBoolean(Args[1]); 47 | devID = MArgument_getInteger(Args[2]); 48 | 49 | initCaffeLink_(useDoubles, useGPU, devID); 50 | 51 | return LIBRARY_NO_ERROR; 52 | } 53 | 54 | /** Mathematica librarylink wrapper for \c prepareNetFile_.*/ 55 | extern "C" DLLEXPORT int prepareNetFile(LIB_LINK_ARGS) 56 | { 57 | char* path; 58 | 59 | if (Argc != 1) { 60 | printf("ERR: %s takes 1 argument: UTF8String path to net protobuffer file\n", 61 | __FUNCTION__); 62 | libData->Message(MSG_WRONG_ARGS); 63 | return LIBRARY_FUNCTION_ERROR; 64 | } 65 | 66 | path = MArgument_getUTF8String(Args[0]); 67 | 68 | if(isUsingDouble()) 69 | freeParamDataMT(libData); 70 | 71 | if(!prepareNetFile_(path)) 72 | return LIBRARY_FUNCTION_ERROR; 73 | 74 | if(isUsingDouble()) 75 | initParamDataMT(); 76 | 77 | return LIBRARY_NO_ERROR; 78 | } 79 | 80 | /** Mathematica librarylink wrapper for \c prepareNetString_.*/ 81 | extern "C" DLLEXPORT int prepareNetString(LIB_LINK_ARGS) 82 | { 83 | char* str; 84 | 85 | if (Argc != 1) { 86 | printf("ERR: %s takes 1 argument: UTF8String net protobuffer parameters\n", 87 | __FUNCTION__); 88 | libData->Message(MSG_WRONG_ARGS); 89 | return LIBRARY_FUNCTION_ERROR; 90 | } 91 | 92 | str = MArgument_getUTF8String(Args[0]); 93 | 94 | if (isUsingDouble()) 95 | freeParamDataMT(libData); 96 | 97 | if (!prepareNetString_(str)) 98 | return LIBRARY_FUNCTION_ERROR; 99 | 100 | if (isUsingDouble()) 101 | initParamDataMT(); 102 | 103 | return LIBRARY_NO_ERROR; 104 | } 105 | 106 | /** Mathematica librarylink wrapper for \c loadNet_.*/ 107 | extern "C" DLLEXPORT int loadNet(LIB_LINK_ARGS) 108 | { 109 | char* path; 110 | 111 | if (Argc != 1) { 112 | printf("ERR: %s takes 1 arguments: UTF8String path to net data\n", 113 | __FUNCTION__); 114 | libData->Message(MSG_WRONG_ARGS); 115 | return LIBRARY_FUNCTION_ERROR; 116 | } 117 | 118 | path = MArgument_getUTF8String(Args[0]); 119 | loadNet_(path); 120 | 121 | return LIBRARY_NO_ERROR; 122 | } 123 | 124 | /** Mathematica librarylink wrapper for \c testNet_.*/ 125 | extern "C" DLLEXPORT int testNet(LIB_LINK_ARGS) 126 | { 127 | if (Argc != 0) { 128 | printf("WRN: %s takes 0 arguments\n", __FUNCTION__); 129 | } 130 | 131 | testNet_(); 132 | 133 | return LIBRARY_NO_ERROR; 134 | } 135 | 136 | /** Mathematica librarylink wrapper for \c trainNet_, TRAIN_NEW version.*/ 137 | extern "C" DLLEXPORT int trainNetString(LIB_LINK_ARGS) 138 | { 139 | char* str; 140 | 141 | if (Argc != 1) { 142 | printf("WRN: %s takes 1 argument: UTF8String solver " 143 | "protobuffer parameters\n", __FUNCTION__); 144 | libData->Message(MSG_WRONG_ARGS); 145 | return LIBRARY_FUNCTION_ERROR; 146 | } 147 | 148 | str = MArgument_getUTF8String(Args[0]); 149 | if(!trainNet_(str, false, TRAIN_NEW, (char*) "")) 150 | return LIBRARY_FUNCTION_ERROR; 151 | 152 | return LIBRARY_NO_ERROR; 153 | } 154 | 155 | /** Mathematica librarylink wrapper for \c trainNet_, TRAIN_NEW version.*/ 156 | extern "C" DLLEXPORT int trainNetFile(LIB_LINK_ARGS) 157 | { 158 | char* str; 159 | 160 | if (Argc != 1) { 161 | printf("WRN: %s takes 1 argument: UTF8String path to solver " 162 | "protobuffer file\n", __FUNCTION__); 163 | libData->Message(MSG_WRONG_ARGS); 164 | return LIBRARY_FUNCTION_ERROR; 165 | } 166 | 167 | str = MArgument_getUTF8String(Args[0]); 168 | if(!trainNet_(str, true, TRAIN_NEW, (char*) "")) 169 | return LIBRARY_FUNCTION_ERROR; 170 | 171 | return LIBRARY_NO_ERROR; 172 | } 173 | 174 | /** Mathematica librarylink wrapper for \c trainNet_, TRAIN_SNAPSHOT version.*/ 175 | extern "C" DLLEXPORT int trainNetSnapshotString(LIB_LINK_ARGS) 176 | { 177 | char* str; 178 | char* path; 179 | 180 | if (Argc != 2) { 181 | printf("WRN: %s takes 2 argument: UTF8String solver " 182 | "protobuffer parameters and UTF8String path to snapshot\n", 183 | __FUNCTION__); 184 | libData->Message(MSG_WRONG_ARGS); 185 | return LIBRARY_FUNCTION_ERROR; 186 | } 187 | 188 | str = MArgument_getUTF8String(Args[0]); 189 | path = MArgument_getUTF8String(Args[1]); 190 | if(!trainNet_(str, false, TRAIN_SNAPSHOT, path)) 191 | return LIBRARY_FUNCTION_ERROR; 192 | 193 | return LIBRARY_NO_ERROR; 194 | } 195 | 196 | /** Mathematica librarylink wrapper for \c trainNet_, TRAIN_SNAPSHOT version.*/ 197 | extern "C" DLLEXPORT int trainNetSnapshotFile(LIB_LINK_ARGS) 198 | { 199 | char* str; 200 | char* path; 201 | 202 | if (Argc != 2) { 203 | printf("WRN: %s takes 2 argument: UTF8String path to solver " 204 | "protobuffer file and UTF8String path to snapshot\n", 205 | __FUNCTION__); 206 | libData->Message(MSG_WRONG_ARGS); 207 | return LIBRARY_FUNCTION_ERROR; 208 | } 209 | 210 | str = MArgument_getUTF8String(Args[0]); 211 | path = MArgument_getUTF8String(Args[1]); 212 | if(!trainNet_(str, true, TRAIN_SNAPSHOT, path)) 213 | return LIBRARY_FUNCTION_ERROR; 214 | 215 | return LIBRARY_NO_ERROR; 216 | } 217 | 218 | /** Mathematica librarylink wrapper for \c trainNet_, TRAIN_WEIGHTS version.*/ 219 | extern "C" DLLEXPORT int trainNetWeightsString(LIB_LINK_ARGS) 220 | { 221 | char* str; 222 | char* path; 223 | 224 | if (Argc != 2) { 225 | printf("WRN: %s takes 2 argument: UTF8String solver " 226 | "protobuffer parameters and UTF8String path to weights\n", 227 | __FUNCTION__); 228 | libData->Message(MSG_WRONG_ARGS); 229 | return LIBRARY_FUNCTION_ERROR; 230 | } 231 | 232 | str = MArgument_getUTF8String(Args[0]); 233 | path = MArgument_getUTF8String(Args[1]); 234 | if(!trainNet_(str, false, TRAIN_WEIGHTS, path)) 235 | return LIBRARY_FUNCTION_ERROR; 236 | 237 | return LIBRARY_NO_ERROR; 238 | } 239 | 240 | /** Mathematica librarylink wrapper for \c trainNet_, TRAIN_WEIGHTS version.*/ 241 | extern "C" DLLEXPORT int trainNetWeightsFile(LIB_LINK_ARGS) 242 | { 243 | char* str; 244 | char* path; 245 | 246 | if (Argc != 2) { 247 | printf("WRN: %s takes 2 argument: UTF8String to solver " 248 | "protobuffer file and UTF8String path to weights\n", 249 | __FUNCTION__); 250 | libData->Message(MSG_WRONG_ARGS); 251 | return LIBRARY_FUNCTION_ERROR; 252 | } 253 | 254 | str = MArgument_getUTF8String(Args[0]); 255 | path = MArgument_getUTF8String(Args[1]); 256 | if(!trainNet_(str, true, TRAIN_WEIGHTS, path)) 257 | return LIBRARY_FUNCTION_ERROR; 258 | 259 | return LIBRARY_NO_ERROR; 260 | } 261 | -------------------------------------------------------------------------------- /module/caffeLink.m: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | BeginPackage["CaffeLink`"] 4 | 5 | initCaffeLinkModule::usage = "initCaffeLinkModule[path] initializes module from caffe.proto file." 6 | newNet::usage = "newNetOld[netParameters] returns net internal reprezentation, 'netParameters' format is {NetParam01, NetParam02, {{Layer01Param01, Layer01Param02, ..}, {Layer02Param01, Layer02Param02, ..}}}." 7 | newSolver::usage = "newSolver[solverParameters] return solver internal reprezentation, 'solverParameters' format is {SolverParam01, SolverParam02, ...}." 8 | solverAddNetParam::usage = "solverAddNetParam[solverParametersString, netParametersString] adds net definition to solver as net_param field. Returns resulting solver parameter string." 9 | getParamString::usage = "getParamString[net] generates protobuffer string from net compatible with Caffe." 10 | 11 | cblasTest::usage = "Test cblas stability." 12 | 13 | initCaffeLink::usage = "initCaffeLink[UseDouble, UseGPU, GPUDevice] initializes CaffeLink library with data type double or float, sets GPU or CPU mode and device ID for GPU mode, default is 0." 14 | prepareNetFile::usage = "prepareNetFile[path] sets up a new net from protobuffer file on given path." 15 | prepareNetString::usage = "prepareNetString[string] sets up a new net from given protobuffer string." 16 | loadNet::usage = "loadNet[path] loads previously exported or snapshoted net after the same net was prepared using prepareNet*." 17 | exportNet::usage = "exportNet[path] exports previously loaded net to selected path." 18 | 19 | evaluateNet::usage = "evaluateNet[] runs loaded net in test phase." 20 | trainNetString::usage = "trainNetString[solverParam] trains new net with solver protobuffer parameters given in string." 21 | trainNetFile::usage = "trainNetFile[path] trains new net with solver protobuffer parameters taken from file." 22 | trainNetSnapshotString::usage = "trainNetSnapshotString[solverParam, pathState] continues training from solver state file on 'pathState'. Solver parameters given as string." 23 | trainNetSnapshotFile::usage = "trainNetSnapshotFile[path, pathState] continues training from solver state file on 'pathState'. Solver parameters given as 'path' to file." 24 | trainNetWeightsString::usage = "trainNetWeightsString[solverParam, pathWeights] finetunes net on path 'pathWeights'. Solver parameters given as string." 25 | trainNetWeightsFile::usage = "trainNetWeightsFile[path, pathWeights] finetunes net on path 'pathWeights'. Solver parameters given as 'path' to file." 26 | 27 | printNetInfo::usage = "printNetInfo[] prints (in console, stdout) info about prepared net: structure, layers, sizes and counts of blobs, etc." 28 | printWorkingPath::usage = "printWorkingPath[] prints (in console, stdout) root directory, executes 'pwd' command." 29 | 30 | getLayerNum::usage = "getLayerNum[] returns number of layers in prepared net." 31 | getTopBlobSize::usage = "getTopBlobSize[layer] returns array of top blob dimensions: {num, channels, height, width}, 4 elements for each blob, 'layer' can be layer index or name." 32 | getBottomBlobSize::usage = "getBottomBlobSize[layer] returns array of bottom blob dimensions: {num, channels, height, width}, 4 elements for each blob, 'layer' can be layer index or name.." 33 | getParamBlobSize::usage = "getParamBlobSize[layer] returns array of parameter blob dimensions: {num, channels, height, width}, 4 elements for each blob, 'layer' can be layer index or name.." 34 | 35 | getTopBlob::usage = "getTopBlob[layer, blobIdx] returns data stored in top 'blobIdx'-th blob of layer with index or name 'layer'. Default 'blobIdx' is 0." 36 | getBottomBlob::usage = "getBottomBlob[layer, blobIdx] returns data stored in bottom 'blobIdx'-th blob of layer with index or name 'layer'. Default 'blobIdx' is 0." 37 | getParamBlob::usage = "getParamBlob[layer, blobIdx] returns data stored in parameter 'blobIdx'-th blob of layer with index or name 'layer'. Default 'blobIdx' is 0." 38 | 39 | setTopBlob::usage = "setTopBlob[data, layer, blobIdx] inserts 'data' to top 'blobIdx'-th blob of layer with index or name 'layer'. Default 'blobIdx' is 0." 40 | setBottomBlob::usage = "setBottomBlob[data, layer, blobIdx] inserts 'data' to bottom 'blobIdx'-th blob of layer with index or name 'layer'. Default 'blobIdx' is 0." 41 | setParamBlob::usage = "setParamBlob[data, layer, blobIdx] inserts 'data' to parameter 'blobIdx'-th blob of layer with index or name 'layer'. Default 'blobIdx' is 0." 42 | setInput::usage = "setInput[data] inserts 'data' to input blob. Work only with net without input (data) layer." 43 | (* -------------------------------------------------------------------------- *) 44 | 45 | 46 | Begin["`Private`"] 47 | 48 | regexSelectMsg = RegularExpression["message .*\{(([^\{\}]\n*)*(\{([^\}]\n*)*\})?)*\}"]; 49 | regexSelectEnum = RegularExpression["enum .* \{([^\}]*\n*)*\}"]; 50 | (* basic types and enums *) 51 | basicTypes = {"bool", "bytes", "int", "long", "int32", "int64", "uint32", 52 | "uint64", "float", "double", "string"}; 53 | strBasicType = "*b*"; 54 | strMsgType = "*s*"; 55 | globalEnums = {}; 56 | enumsInMsg = {}; 57 | paramLists = {}; 58 | 59 | layerParTypeBased = {}; (* list of parameters based on layer type *) 60 | (* which par. types belong to which layer type *) 61 | layerParTypeLUT = { 62 | {"ABSVAL","",{""}}, 63 | {"ACCURACY","",{"AccuracyParameter"}}, 64 | {"ARGMAX","",{"ArgMaxParameter"}}, 65 | {"BNLL","",{""}}, 66 | {"CONCAT","",{"ConcatParameter"}}, 67 | {"CONTRASTIVE_LOSS","",{"ContrastiveLossParameter"}}, 68 | {"CONVOLUTION","",{"ConvolutionParameter"}}, 69 | {"DATA","",{"DataParameter","TransformationParameter"}}, 70 | {"DROPOUT","",{"DropoutParameter"}}, 71 | {"DUMMY_DATA","",{"DummyDataParameter","TransformationParameter"}}, 72 | {"EUCLIDEAN_LOSS","",{""}}, 73 | {"ELTWISE","",{"EltwiseParameter"}}, 74 | {"FLATTEN","",{""}}, 75 | {"HDF5_DATA","",{"HDF5DataParameter","TransformationParameter"}}, 76 | {"HDF5_OUTPUT","",{"HDF5OutputParameter"}}, 77 | {"HINGE_LOSS","",{"HingeLossParameter"}}, 78 | {"IM2COL","",{""}}, 79 | {"IMAGE_DATA","",{"ImageDataParameter","TransformationParameter"}}, 80 | {"INFOGAIN_LOSS","",{"InfogainLossParameter"}}, 81 | {"INNER_PRODUCT","",{"InnerProductParameter"}}, 82 | {"LRN","",{"LRNParameter"}}, 83 | {"MEMORY_DATA","",{"MemoryDataParameter","TransformationParameter"}}, 84 | {"MULTINOMIAL_LOGISTIC_LOSS","",{""}}, 85 | {"MVN","",{"MVNParameter"}}, 86 | {"POOLING","",{"PoolingParameter"}}, 87 | {"POWER","",{"PowerParameter"}}, 88 | {"RELU","",{"ReLUParameter"}}, 89 | {"SIGMOID","",{"SigmoidParameter"}}, 90 | {"SIGMOID_CROSS_ENTROPY_LOSS","",{""}}, 91 | {"SILENCE","",{""}}, 92 | {"SOFTMAX","",{"SoftmaxParameter"}}, 93 | {"SOFTMAX_LOSS","",{"SoftmaxParameter"}}, 94 | {"SPLIT","",{""}}, 95 | {"SLICE","",{"SliceParameter"}}, 96 | {"TANH","",{"TanHParameter"}}, 97 | {"WINDOW_DATA","",{"WindowDataParameter","TransformationParameter"}}, 98 | {"THRESHOLD","",{"ThresholdParameter"}}} 99 | (* -------------------------------------------------------------------------- *) 100 | 101 | 102 | initCaffeLinkModule[caffeProto_] := Module[{clProto, msgWithEnums, globalEnums}, 103 | If[!FileExistsQ[caffeProto], 104 | Throw[StringJoin["file not found: ", caffeProto]]; 105 | ]; 106 | 107 | clProto = CleanProto[caffeProto]; 108 | msgWithEnums = ParseEnumsInMsg[clProto]; 109 | globalEnums = ParseGlobalEnums[clProto]; 110 | ParseParamList[globalEnums, msgWithEnums, clProto]; 111 | Return[True]; 112 | ]; 113 | (* -------------------------------------------------------------------------- *) 114 | 115 | 116 | newNet[netp_] := Module[{nn, netname, i, j, lap, lname, ltype}, 117 | np = netp[[1;;-2]]; 118 | netname = Replace["name",np]; 119 | 120 | nn = NewNetOld[netname]; 121 | For[i = 1, i <= Length[np], i++, 122 | nn = SetNetParam[nn, np[[i, 1]], np[[i, 2]]]; 123 | ]; 124 | 125 | For[i = 1, i <= Length[netp[[-1]]], i++, 126 | lap = netp[[-1, i]]; 127 | lname = Replace["name",lap]; 128 | ltype = Replace["type",lap]; 129 | nn = AddLayer[nn,ltype,lname]; 130 | For[j = 1, j <= Length[lap], j++, 131 | nn = SetLayerParam[nn,i, lap[[j,1]],lap[[j,2]]]; 132 | ]; 133 | ]; 134 | nn 135 | ]; 136 | (* -------------------------------------------------------------------------- *) 137 | 138 | 139 | NewNetOld[name_] := Module[{nn}, 140 | nn = Select[paramLists, StringMatchQ[#[[1]], "NetParameter"] &][[1]]; 141 | nn = SetOneParam[nn, "name", name]; 142 | {nn, {}} 143 | ]; 144 | (* -------------------------------------------------------------------------- *) 145 | 146 | 147 | newSolver[solp_] := Module[{type, i}, 148 | type = Replace["solver_type",solp]; 149 | 150 | sol = NewSolverOld[type]; 151 | For[i = 1, i <= Length[solp], i++, 152 | sol = SetSolverParam[sol, solp[[i, 1]], solp[[i, 2]]]; 153 | ]; 154 | sol 155 | ]; 156 | (* -------------------------------------------------------------------------- *) 157 | 158 | 159 | solverAddNetParam[solver_, net_] := Module[{netp}, 160 | 161 | StringJoin["net_param: {\n", net, "}\n", solver] 162 | ]; 163 | 164 | 165 | NewSolverOld[type_] := Module[{ns}, 166 | ns = Select[paramLists, StringMatchQ[#[[1]], "SolverParameter"] &][[1]]; 167 | ns = SetOneParam[ns, "solver_type", type]; 168 | {ns,{}} 169 | ]; 170 | (* -------------------------------------------------------------------------- *) 171 | 172 | 173 | AddLayer[net_, type_, name_] := Module[{cnet, netLayers, lp, laPars, laTypPar, i}, 174 | (* create new layer parameter list and set name and type *) 175 | layer = Select[paramLists, StringMatchQ[#[[1]], "LayerParameter"] &][[1]]; 176 | layer = SetOneParam[layer, "name", name]; 177 | layer = SetOneParam[layer, "type", type]; 178 | laPars = layer[[-1]]; 179 | 180 | (* find specific parameter for this layer type *) 181 | laTypPar = Select[layerParTypeLUT, StringMatchQ[#[[1]], type] &]; 182 | If[Length[laTypPar] < 1, Throw["Error: unknown layer type"];]; 183 | (* select its parameter list *) 184 | laTypPar = laTypPar[[;;,3]]; 185 | laTypPar = Select[paramLists, StringMatchQ[#[[1]], laTypPar] &]; 186 | 187 | For[i=1,i<=Length[laTypPar],i++, 188 | laTypPar[[i,1]] = layerParTypeBased[[ 189 | Position[layerParTypeBased,laTypPar[[i,1]]][[1,1]], 190 | 1]]; 191 | ]; 192 | 193 | (* and add it to layer *) 194 | laPars = Join[laPars, laTypPar]; 195 | 196 | layer[[-1]] = laPars; 197 | netLayers = net[[2]]; 198 | AppendTo[netLayers, layer]; 199 | cnet = net; 200 | cnet[[2]] = netLayers; 201 | cnet 202 | ]; 203 | (* -------------------------------------------------------------------------- *) 204 | 205 | 206 | SetNetParam[net_, name_, value_] := Module[{newParList}, 207 | newParList = SetParamRekur[net[[1]], name, value]; 208 | If[newParList != {False}, 209 | Return[{newParList, net[[2]]}]; 210 | ]; 211 | 212 | Throw[StringJoin["unknown parameter name: ", name]]; 213 | ]; 214 | (* -------------------------------------------------------------------------- *) 215 | 216 | 217 | SetLayerParam[net_, layerInx_, name_, value_] := Module[{newParList, newLayers}, 218 | newParList = SetParamRekur[net[[2,layerInx]], name, value]; 219 | If[newParList != {False}, 220 | newLayers = net[[2]]; 221 | newLayers[[layerInx]] = newParList; 222 | Return[{net[[1]], newLayers}]; 223 | ]; 224 | 225 | Throw[StringJoin["unknown parameter name: ", name]]; 226 | ]; 227 | (* -------------------------------------------------------------------------- *) 228 | 229 | 230 | SetSolverParam[solver_, name_, value_] := Module[{newParList}, 231 | newParList = SetParamRekur[solver[[1]], name, value]; 232 | If[newParList != {False}, 233 | Return[{newParList, solver[[2]]}]; 234 | ]; 235 | 236 | Throw[StringJoin["unknown parameter name: ", name]]; 237 | ]; 238 | (* -------------------------------------------------------------------------- *) 239 | 240 | 241 | (* Messages can be nested in protobuffers. This goes through them recursively 242 | until proper parameter name is found. Some timmes it is good idea provide 243 | name as 'msgname.paramname' *) 244 | SetParamRekur[pList_, name_, value_] := Module[{newParList, list, i}, 245 | (* try set basic params - params with value *) 246 | newParList = SetOneParam[pList, name, value]; 247 | If[newParList != {False}, 248 | Return[newParList]; 249 | ]; 250 | 251 | (* recursively go throught all special parameters - msg *) 252 | list = pList[[-1]]; 253 | For[i = 1, i <= Length[list], i++, 254 | newParList = SetParamRekur[list[[i]], name, value]; 255 | If[newParList != {False}, 256 | list[[i]] = newParList; 257 | newParList = pList; 258 | newParList[[-1]] = list; 259 | Return[newParList]; 260 | ]; 261 | ]; 262 | 263 | Return[{False}]; 264 | ]; 265 | (* -------------------------------------------------------------------------- *) 266 | 267 | 268 | SetOneParam[pList_, name_, value_] := Module[{list, splName, preName, ppos}, 269 | splName = name; 270 | (* handle compound param. names *) 271 | If[Length[StringSplit[name,"."]] > 1, 272 | preName = StringSplit[name,"."][[1]]; 273 | splName = StringSplit[name,"."][[2]]; 274 | If[preName != pList[[1]], 275 | Return[{False}]; 276 | ]; 277 | ]; 278 | 279 | (* list[[2;;-2]]: 1. element is msg name or type, last element is list 280 | of unfolded parameters with special type *) 281 | ppos = Select[pList[[2;;-2]], StringMatchQ[#[[1]], splName] &]; 282 | If[Length[ppos] == 0, 283 | Return[{False}]; 284 | ]; 285 | ppos = Position[pList, ppos[[1]]]; 286 | 287 | list = pList; 288 | list[[ppos[[1]], 3]] = value; 289 | list 290 | ]; 291 | (* -------------------------------------------------------------------------- *) 292 | 293 | 294 | getParamString[net_] := Module[{str, li}, 295 | 296 | (* start with net params *) 297 | str = GetParamStrRekur[net[[1]], ""]; 298 | 299 | (* layers *) 300 | For[li = 1, li <= Length[net[[2]]], li++, 301 | str = StringJoin[str, "layers {\n"]; 302 | str = StringJoin[str, GetParamStrRekur[net[[2, li]], " "]]; 303 | str = StringJoin[str, "}\n"]; 304 | ]; 305 | 306 | Return[str] 307 | ]; 308 | (* -------------------------------------------------------------------------- *) 309 | 310 | 311 | (* Recursively generrates protobuffer string from pList - msg. 312 | Space is size of indentation. *) 313 | GetParamStrRekur[pList_, space_] := Module[{str, subStr, i}, 314 | (* basic params *) 315 | str = GetParamString[pList, space]; 316 | 317 | (* parameters with special type *) 318 | For[i = 1, i <= Length[pList[[-1]]], i++, 319 | subStr = GetParamStrRekur[pList[[-1, i]], StringJoin[space, " "]]; 320 | If[StringLength[subStr] > 0, 321 | str = StringJoin[str, space, pList[[-1, i, 1]], " {\n"]; 322 | str = StringJoin[str, subStr]; 323 | str = StringJoin[str, space, "}\n"]; 324 | ]; 325 | ]; 326 | str 327 | ]; 328 | (* -------------------------------------------------------------------------- *) 329 | 330 | 331 | (* Generrates protobuffer string from pList - msg. 332 | Space is size of indentation. *) 333 | GetParamString[pList_, space_] := Module[{str, value, orig, i}, 334 | str = ""; 335 | For[i = 2, i <= Length[pList] - 1, i++, 336 | value = Flatten[{pList[[i, 3]]}]; 337 | orig = ToString[pList[[i, 3]]]; 338 | If[IsBasicOrEnum[pList[[i, 2]]] && orig != strBasicType && orig != "", 339 | For[j = 1, j <= Length[value], j++, 340 | str = StringJoin[str, space, pList[[i, 1]], ": "]; 341 | If[pList[[i, 2]] == "string", 342 | str = StringJoin[str, "\"", value[[j]], "\"\n"] 343 | , 344 | str = StringJoin[str, ToString[value[[j]]], "\n"]; 345 | ]; 346 | ]; 347 | ]; 348 | ]; 349 | str 350 | ]; 351 | (* -------------------------------------------------------------------------- *) 352 | 353 | 354 | (* Checks if parametr's type can hold a value directly. 355 | Checks if parameter's type is basic or global enum or enum in msg.*) 356 | IsBasicOrEnum[type_] := Block[{}, 357 | res = Select[basicTypes, StringMatchQ[#, type] &]; 358 | If[Length[res] > 0, Return[True];]; 359 | res = Select[globalEnums, StringMatchQ[#, type] &]; 360 | If[Length[res] > 0, Return[True]]; 361 | res = Select[enumsInMsg, StringMatchQ[#, type] &]; 362 | If[Length[res] > 0, Return[True]]; 363 | Return[False]; 364 | ]; 365 | (* -------------------------------------------------------------------------- *) 366 | 367 | 368 | (* Cleans protobuffer file in given path. CleanProto[filePath] returns 369 | content of file without comments, empty lines, etc. *) 370 | CleanProto[caffeProtoPath_] := Module[{str}, 371 | str = ReadString[caffeProtoPath]; 372 | (*Remove comments, empty lines and trim spaces.*) 373 | str = StringTrim[StringSplit[str, "\n"]]; 374 | str = StringTrim[str, RegularExpression["//.*"]]; 375 | str = Select[str, #!=""&]; 376 | StringJoin[Riffle[str, ConstantArray["\n", Length[str]]]] 377 | ]; 378 | (* -------------------------------------------------------------------------- *) 379 | 380 | 381 | (* Parses and returns enum names from cleaned proto. *) 382 | ParseEnumNames[clProto_] := Module[{enms}, 383 | enms = StringCases[clProto, regexSelectEnum]; 384 | enms = StringSplit[enms, " "]; 385 | (* names only *) 386 | enms[[;;, 2]] 387 | ]; 388 | (* -------------------------------------------------------------------------- *) 389 | 390 | 391 | (* For every msg parses its enums. Returns list: 392 | {{msg_01, {enum01, enum02, ...}}, ...} *) 393 | ParseEnumsInMsg[clProto_] := Module[{i, msgs, msgNames, msgWithEnums}, 394 | msgs = StringCases[clProto,regexSelectMsg]; 395 | msgNames = StringSplit[msgs, " "][[;;, 2]]; 396 | 397 | (*For every msg parse its enums.*) 398 | enumsInMsg = {}; (* only enum names *) 399 | msgWithEnums = ConstantArray[{},Length[msgs]]; (* msg and its own enums *) 400 | For[i = 1, i <= Length[msgs], i++, 401 | AppendTo[enumsInMsg, ParseEnumNames[msgs[[i]]]]; 402 | msgWithEnums[[i]] = {msgNames[[i]], ParseEnumNames[msgs[[i]]]}; 403 | ]; 404 | enumsInMsg = Flatten[enumsInMsg]; 405 | msgWithEnums 406 | ]; 407 | (* -------------------------------------------------------------------------- *) 408 | 409 | 410 | (* Parses and returns list of enums outside messages 411 | in clProto - cleaned proto file. *) 412 | ParseGlobalEnums[clProto_] := Module[{str}, 413 | str = StringReplace[clProto,regexSelectMsg -> ""]; 414 | globalEnums = ParseEnumNames[str] 415 | ]; 416 | (* -------------------------------------------------------------------------- *) 417 | 418 | 419 | (* Creates list of lists of parameters from messages in 'clProto' 420 | and enum lists. *) 421 | ParseParamList[globalEnum_, msgEnum_, clProto_] := Module[ 422 | {i, msgs, parList, pars, parListUnfold, parListUnfold2}, 423 | msgs = StringReplace[clProto, regexSelectEnum-> ""]; 424 | msgs = StringCases[msgs,regexSelectMsg]; 425 | msgs = StringReplace[msgs, RegularExpression["(.*\{\n?)|(\}.*\n?)|( *\n *)"]-> ""]; 426 | msgs = StringSplit[msgs, ";"]; (* list of messages *) 427 | 428 | parList = {}; 429 | (* convert each msg to list of parameters *) 430 | For[i = 1, i <= Length[msgs], i++, 431 | pars = StringSplit[msgs[[i]], " "][[;;, {3, 2}]]; 432 | pars = Append[#,"*"]&/@pars; 433 | pars = Join[{msgEnum[[i,1]]}, pars]; 434 | AppendTo[parList, pars]; 435 | ]; 436 | 437 | (* net and layer par. lists are extra -> remove them before unfolding*) 438 | (*parList = RemoveLayerParTypes[parList]; 439 | parList = RemoveNetParLayers[parList];*) 440 | parList = RemoveParpattInMsg[parList, "LayerParameter", "*_param"]; 441 | parList = RemoveParpattInMsg[parList, "NetParameter", "layers"]; 442 | parList = RemoveParpattInMsg[parList, "SolverParameter", "*net_param"]; 443 | 444 | (* unfolding parameters with special type (message - group of pars.) *) 445 | parListUnfold = {}; 446 | For[i = 1, i <= Length[msgs], i++, (*Length[msgs]*) 447 | AppendTo[parListUnfold, UnfoldMsgTypePars[parList[[i]], parList]]; 448 | ]; 449 | (* currently there is no need to unfold more nested msg than 2 levels, 450 | so this kinda nasty repeating is enough, since layers (LayerParameter) in 451 | NetParameter is handled separately *) 452 | parListUnfold2 = {}; 453 | For[i = 1, i <= Length[msgs], i++, (*Length[msgs]*) 454 | AppendTo[parListUnfold2, UnfoldMsgTypePars[parList[[i]], parListUnfold]]; 455 | ]; 456 | 457 | paramLists = parListUnfold2 458 | ]; 459 | (* -------------------------------------------------------------------------- *) 460 | 461 | 462 | RemoveParpattInMsg[pList_, msgName_, patt_] := Module[ 463 | {i, parList, msgPar, msgParPos, msgParPatt, p, depParP}, 464 | (* remove all layer type based parameters *) 465 | parList = pList; 466 | msgPar = Select[parList, StringMatchQ[#[[1]], msgName] &][[1]]; 467 | msgParPos = Flatten[Position[parList, msgPar]]; 468 | msgParPatt = Select[msgPar[[2;;]], StringMatchQ[#[[1]], patt] &]; 469 | (* if layer msg, save layer type based msgs *) 470 | If[msgName == "LayerParameter", 471 | layerParTypeBased = msgParPatt; 472 | ]; 473 | p = {}; 474 | For[i=1, i <= Length[msgParPatt], i++, 475 | p = Join[p,Position[msgPar, msgParPatt[[i]]]]; 476 | ]; 477 | (* also remove deprecated parameter V0LayerParameter, if present *) 478 | depParP = Position[msgPar, "V0LayerParameter"]; 479 | If[Length[depParP] > 0, 480 | depParP = {{depParP[[1,1]]}}; (* and only 1. coord. is needed *) 481 | p = Join[p, depParP]; 482 | ]; 483 | (* actual removal *) 484 | msgPar = Delete[parList[[msgParPos]][[1]], p]; 485 | 486 | parList = ReplacePart[parList, msgParPos -> msgPar]; 487 | parList 488 | ]; 489 | (* -------------------------------------------------------------------------- *) 490 | 491 | 492 | (* Unfold parameters referencing whole messages: 493 | parList = {msgName, {p1Name, ""}, {p2Name, ""}}, 494 | msgPars = {p2Name, {p2TypeP1, ""}, p2TypeP2, ""}} 495 | --\[Rule] 496 | {msgName, {p1Name, "*b*"}, {p2Name, "*s*"}, {p2Name, {p2TypeP1, ""}, p2TypeP2, ""}}}. 497 | There is actualy also type: {pName, pType, pValue} *) 498 | UnfoldMsgTypePars[msgPars_, parList_] := Module[{pars, unfPars, unfp, j}, 499 | pars = msgPars; 500 | 501 | unfPars = {}; 502 | For[j = 2, j <= Length[pars], j++, 503 | If[!IsBasicOrEnum[pars[[j, 2]]], 504 | pars[[j,3]] = strMsgType; (* set default value ... "*s*" *) 505 | unfp = Select[parList, StringMatchQ[#[[1]], pars[[j, 2]]] &][[1]]; 506 | (* associate the new list with name of parameter rather then the type *) 507 | unfp[[1]] = pars[[j,1]]; 508 | AppendTo[unfPars, unfp]; 509 | , 510 | pars[[j,3]] = strBasicType; (* set default value ... "*b*" *) 511 | ]; 512 | ]; 513 | 514 | AppendTo[pars, unfPars] 515 | ]; 516 | (* -------------------------------------------------------------------------- *) 517 | 518 | 519 | (* Returns names and messages included in layer based on its type. *) 520 | GetLayerParTypeBased[] := layerParTypeBased; 521 | (* -------------------------------------------------------------------------- *) 522 | 523 | 524 | cblasTest=LibraryFunctionLoad["libcaffeLink", "cblasTest", {}, "Void"]; 525 | 526 | initCaffeLinkLL=LibraryFunctionLoad["libcaffeLink", "initCaffeLink", {"Boolean", "Boolean", Integer}, "Void"]; 527 | prepareNetFileLL = LibraryFunctionLoad["libcaffeLink", "prepareNetFile", {"UTF8String"}, "Void"]; 528 | prepareNetString = LibraryFunctionLoad["libcaffeLink", "prepareNetString", {"UTF8String"}, "Void"]; 529 | loadNetLL = LibraryFunctionLoad["libcaffeLink", "loadNet", {"UTF8String"}, "Void"]; 530 | exportNet = LibraryFunctionLoad["libcaffeLink", "exportNet", {"UTF8String"}, "Void"]; 531 | 532 | evaluateNet=LibraryFunctionLoad["libcaffeLink", "testNet", {}, "Void"]; 533 | trainNetString=LibraryFunctionLoad["libcaffeLink", "trainNetString",{"UTF8String"},"Void"]; 534 | trainNetFileLL=LibraryFunctionLoad["libcaffeLink", "trainNetFile",{"UTF8String"},"Void"]; 535 | trainNetSnapshotStringLL=LibraryFunctionLoad["libcaffeLink", "trainNetSnapshotString",{"UTF8String","UTF8String"},"Void"]; 536 | trainNetSnapshotFileLL=LibraryFunctionLoad["libcaffeLink", "trainNetSnapshotFile",{"UTF8String","UTF8String"},"Void"]; 537 | trainNetWeightsStringLL=LibraryFunctionLoad["libcaffeLink", "trainNetWeightsString",{"UTF8String","UTF8String"},"Void"]; 538 | trainNetWeightsFileLL=LibraryFunctionLoad["libcaffeLink", "trainNetWeightsFile",{"UTF8String","UTF8String"},"Void"]; 539 | 540 | printNetInfo = LibraryFunctionLoad["libcaffeLink","printNetInfo",{},"Void"]; 541 | printWorkingPath = LibraryFunctionLoad["libcaffeLink","printWorkingPath",{},"Void"]; 542 | 543 | getLayerNum = LibraryFunctionLoad["libcaffeLink","getLayerNum",{},Integer]; 544 | getTopBlobSizeLL = LibraryFunctionLoad["libcaffeLink","getTopBlobSize",{Integer},{Integer,1}]; 545 | getBottomBlobSizeLL = LibraryFunctionLoad["libcaffeLink","getBottomBlobSize",{Integer},{Integer,1}]; 546 | getParamBlobSizeLL = LibraryFunctionLoad["libcaffeLink","getParamBlobSize",{Integer},{Integer,1}]; 547 | getTopBlobSizeLNameLL = LibraryFunctionLoad["libcaffeLink","getTopBlobSizeLName",{"UTF8String"},{Integer,1}]; 548 | getBottomBlobSizeLNameLL = LibraryFunctionLoad["libcaffeLink","getBottomBlobSizeLName",{"UTF8String"},{Integer,1}]; 549 | getParamBlobSizeLNameLL = LibraryFunctionLoad["libcaffeLink","getParamBlobSizeLName",{"UTF8String"},{Integer,1}]; 550 | 551 | getTopBlobLL = LibraryFunctionLoad["libcaffeLink","getTopBlob",{Integer,Integer},{Real,1}]; 552 | getBottomBlobLL = LibraryFunctionLoad["libcaffeLink","getBottomBlob",{Integer,Integer},{Real,1}]; 553 | getParamBlobLL = LibraryFunctionLoad["libcaffeLink","getParamBlob",{Integer,Integer},{Real,1}]; 554 | getTopBlobLNameLL = LibraryFunctionLoad["libcaffeLink","getTopBlobLName",{"UTF8String",Integer},{Real,1}]; 555 | getBottomBlobLNameLL = LibraryFunctionLoad["libcaffeLink","getBottomBlobLName",{"UTF8String",Integer},{Real,1}]; 556 | getParamBlobLNameLL = LibraryFunctionLoad["libcaffeLink","getParamBlobLName",{"UTF8String",Integer},{Real,1}]; 557 | 558 | setTopBlobLL = LibraryFunctionLoad["libcaffeLink","setTopBlob",{{Real,1,"Manual"},Integer,Integer},"Void"]; 559 | setBottomBlobLL = LibraryFunctionLoad["libcaffeLink","setBottomBlob",{{Real,1,"Manual"},Integer,Integer},"Void"]; 560 | setParamBlobLL = LibraryFunctionLoad["libcaffeLink","setParamBlob",{{Real,1,"Manual"},Integer,Integer},"Void"]; 561 | 562 | setTopBlobLNameLL = LibraryFunctionLoad["libcaffeLink","setTopBlobLName",{{Real,1,"Manual"},"UTF8String",Integer},"Void"]; 563 | setBottomBlobLNameLL = LibraryFunctionLoad["libcaffeLink","setBottomBlobLName",{{Real,1,"Manual"},"UTF8String",Integer},"Void"]; 564 | setParamBlobLNameLL = LibraryFunctionLoad["libcaffeLink","setParamBlobLName",{{Real,1,"Manual"},"UTF8String",Integer},"Void"]; 565 | setInput = LibraryFunctionLoad["libcaffeLink","setInput",{{Real,1,"Manual"}},"Void"]; 566 | (* -------------------------------------------------------------------------- *) 567 | 568 | 569 | initCaffeLink[ra_, rb_, rc_:{"GPUDevice"->0}] := Module[{useDouble, useGpu, devid, res}, 570 | args = Flatten[{ra, rb, rc}]; 571 | useDouble = Replace["UseDouble", args]; 572 | useGpu = Replace["UseGPU", args]; 573 | devid = Replace["GPUDevice", args]; 574 | 575 | res = initCaffeLinkLL[useDouble, useGpu, devid]; 576 | If[res == Null, Return[True], 577 | Print[res]; 578 | Return[False]; 579 | ]; 580 | ]; 581 | (* -------------------------------------------------------------------------- *) 582 | 583 | 584 | prepareNetFile[path_] := Module[{}, 585 | If[!FileExistsQ[path], 586 | Throw[StringJoin["file not found: ", path]]; 587 | ]; 588 | prepareNetFileLL[path] 589 | ]; 590 | (* -------------------------------------------------------------------------- *) 591 | 592 | 593 | loadNet[path_] := Module[{}, 594 | If[!FileExistsQ[path], 595 | Throw[StringJoin["file not found: ", path]]; 596 | ]; 597 | loadNetLL[path] 598 | ]; 599 | (* -------------------------------------------------------------------------- *) 600 | 601 | 602 | trainNetFile[path_] := Module[{}, 603 | If[!FileExistsQ[path], 604 | Throw[StringJoin["file not found: ", path]]; 605 | ]; 606 | trainNetFileLL[path] 607 | ]; 608 | (* -------------------------------------------------------------------------- *) 609 | 610 | 611 | trainNetSnapshotString 612 | 613 | trainNetSnapshotString[solver_, path_] := Module[{}, 614 | If[!FileExistsQ[path], 615 | Throw[StringJoin["file not found: ", path]]; 616 | ]; 617 | trainNetSnapshotStringLL[solver, path] 618 | ]; 619 | (* -------------------------------------------------------------------------- *) 620 | 621 | 622 | trainNetSnapshotFile[solverPath_, path_] := Module[{}, 623 | If[!FileExistsQ[path], 624 | Throw[StringJoin["file not found: ", path]]; 625 | ]; 626 | If[!FileExistsQ[solverPath], 627 | Throw[StringJoin["file not found: ", solverPath]]; 628 | ]; 629 | trainNetSnapshotFileLL[solverPath, path] 630 | ]; 631 | (* -------------------------------------------------------------------------- *) 632 | 633 | 634 | trainNetWeightsString[solver_, path_] := Module[{}, 635 | If[!FileExistsQ[path], 636 | Throw[StringJoin["file not found: ", path]]; 637 | ]; 638 | trainNetWeightsStringLL[solver, path] 639 | ]; 640 | (* -------------------------------------------------------------------------- *) 641 | 642 | 643 | trainNetWeightsFile[solverPath_, path_] := Module[{}, 644 | If[!FileExistsQ[path], 645 | Throw[StringJoin["file not found: ", path]]; 646 | ]; 647 | If[!FileExistsQ[solverPath], 648 | Throw[StringJoin["file not found: ", solverPath]]; 649 | ]; 650 | trainNetWeightsFileLL[solverPath, path] 651 | ]; 652 | (* -------------------------------------------------------------------------- *) 653 | 654 | 655 | getTopBlob[layer_, blob_:0] := Module[{}, 656 | If[StringQ[layer], 657 | Return[getTopBlobLNameLL[layer,blob]]; 658 | , 659 | Return[getTopBlobLL[layer,blob]]; 660 | ]; 661 | ]; 662 | (* -------------------------------------------------------------------------- *) 663 | 664 | 665 | getParamBlob[layer_, blob_:0] := Module[{}, 666 | If[StringQ[layer], 667 | Return[getParamBlobLNameLL[layer,blob]]; 668 | , 669 | Return[getParamBlobLL[layer,blob]]; 670 | ]; 671 | ]; 672 | (* -------------------------------------------------------------------------- *) 673 | 674 | 675 | getBottomBlob[layer_, blob_:0] := Module[{}, 676 | If[StringQ[layer], 677 | Return[getBottomBlobLNameLL[layer,blob]]; 678 | , 679 | Return[getBottomBlobLL[layer,blob]]; 680 | ]; 681 | ]; 682 | (* -------------------------------------------------------------------------- *) 683 | 684 | 685 | getTopBlobSize[layer_] := Module[{}, 686 | If[StringQ[layer], 687 | Return[getTopBlobSizeLNameLL[layer]]; 688 | , 689 | Return[getTopBlobSizeLL[layer]]; 690 | ]; 691 | ]; 692 | (* -------------------------------------------------------------------------- *) 693 | 694 | 695 | getBottomBlobSize[layer_] := Module[{}, 696 | If[StringQ[layer], 697 | Return[getBottomBlobSizeLNameLL[layer]]; 698 | , 699 | Return[getBottomBlobSizeLL[layer]]; 700 | ]; 701 | ]; 702 | (* -------------------------------------------------------------------------- *) 703 | 704 | 705 | getParamBlobSize[layer_] := Module[{}, 706 | If[StringQ[layer], 707 | Return[getParamBlobSizeLNameLL[layer]]; 708 | , 709 | Return[getParamBlobSizeLL[layer]]; 710 | ]; 711 | ]; 712 | (* -------------------------------------------------------------------------- *) 713 | 714 | 715 | setTopBlob[data_, layer_, blob_:0] := Module[{}, 716 | If[StringQ[layer], 717 | Return[setTopBlobLNameLL[data, layer, blob]]; 718 | , 719 | Return[setTopBlobLL[data, layer, blob]]; 720 | ]; 721 | ]; 722 | (* -------------------------------------------------------------------------- *) 723 | 724 | 725 | setBottomBlob[data_, layer_, blob_:0] := Module[{}, 726 | If[StringQ[layer], 727 | Return[setBottomBlobLNameLL[data, layer, blob]]; 728 | , 729 | Return[setBottomBlobLL[data, layer, blob]]; 730 | ]; 731 | ]; 732 | (* -------------------------------------------------------------------------- *) 733 | 734 | 735 | setParamBlob[data_, layer_, blob_:0] := Module[{}, 736 | If[StringQ[layer], 737 | Return[setParamBlobLNameLL[data, layer, blob]]; 738 | , 739 | Return[setParamBlobLL[data, layer, blob]]; 740 | ]; 741 | ]; 742 | (* -------------------------------------------------------------------------- *) 743 | 744 | 745 | End[] 746 | EndPackage[] 747 | -------------------------------------------------------------------------------- /module/demo/imageNet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seilim/CaffeLink/efd16474e3a19e73ed091fffa98aa062259cd33f/module/demo/imageNet.pdf -------------------------------------------------------------------------------- /module/demo/liblink-test.nb: -------------------------------------------------------------------------------- 1 | (* Content-type: application/vnd.wolfram.mathematica *) 2 | 3 | (*** Wolfram Notebook File ***) 4 | (* http://www.wolfram.com/nb *) 5 | 6 | (* CreatedBy='Mathematica 10.0' *) 7 | 8 | (*CacheID: 234*) 9 | (* Internal cache information: 10 | NotebookFileLineBreakTest 11 | NotebookFileLineBreakTest 12 | NotebookDataPosition[ 158, 7] 13 | NotebookDataLength[ 19767, 503] 14 | NotebookOptionsPosition[ 18434, 452] 15 | NotebookOutlinePosition[ 18849, 469] 16 | CellTagsIndexPosition[ 18806, 466] 17 | WindowFrame->Normal*) 18 | 19 | (* Beginning of Notebook Content *) 20 | Notebook[{ 21 | 22 | Cell[CellGroupData[{ 23 | Cell["CaffeLink", "Title", 24 | CellChangeTimes->{{3.6286167009686193`*^9, 3.628616705353096*^9}, { 25 | 3.628626423243058*^9, 3.628626438634693*^9}}], 26 | 27 | Cell[CellGroupData[{ 28 | 29 | Cell["LibLink test", "Chapter", 30 | CellChangeTimes->{{3.62861672363321*^9, 3.628616729321156*^9}, { 31 | 3.6286182071179743`*^9, 3.628618207989902*^9}, {3.6287118654359283`*^9, 32 | 3.628711866035581*^9}, {3.628717312225256*^9, 3.628717317457108*^9}}], 33 | 34 | Cell[CellGroupData[{ 35 | 36 | Cell["Requirements and Initialization", "Section", 37 | CellChangeTimes->{{3.628619311403955*^9, 3.628619333729766*^9}, { 38 | 3.628626296938178*^9, 3.628626307578312*^9}, {3.62862634230451*^9, 39 | 3.628626346353798*^9}, 3.628706794516664*^9}], 40 | 41 | Cell[TextData[{ 42 | "Compiled CaffeLink library must be somewhere in ", 43 | StyleBox["$LibraryPath", "Input"], 44 | " so that ", 45 | StyleBox["Mathematica", 46 | FontSlant->"Italic"], 47 | " can find it." 48 | }], "Text", 49 | CellChangeTimes->{{3.628718425306878*^9, 3.628718511187456*^9}, { 50 | 3.6287185996524*^9, 3.6287186000852633`*^9}}], 51 | 52 | Cell[BoxData["$LibraryPath"], "Input"], 53 | 54 | Cell[TextData[{ 55 | "Check it with ", 56 | StyleBox["FindLibrary", "Input"], 57 | "." 58 | }], "Text", 59 | CellChangeTimes->{3.628718608829934*^9}], 60 | 61 | Cell[CellGroupData[{ 62 | 63 | Cell[BoxData[ 64 | RowBox[{"FindLibrary", "[", "\"\\"", "]"}]], "Input", 65 | CellChangeTimes->{{3.628718497514213*^9, 3.628718526830739*^9}}], 66 | 67 | Cell[BoxData["\<\"/home/kerhy/.Mathematica/SystemFiles/LibraryResources/Linux-\ 68 | x86-64/libcaffeLink.so\"\>"], "Output", 69 | CellChangeTimes->{{3.628718545140374*^9, 3.628718573656456*^9}}] 70 | }, Open ]], 71 | 72 | Cell[TextData[{ 73 | "CaffeLink depends on numerous other libraries, especially on libcaffe. \ 74 | These libraries do not have to be in ", 75 | StyleBox["$LibraryPath", "Input"], 76 | " but should be loadable in your system." 77 | }], "Text", 78 | CellChangeTimes->{{3.628718647289814*^9, 3.628718722437767*^9}, { 79 | 3.6287187631260977`*^9, 3.628718805670025*^9}, {3.6287193282930613`*^9, 80 | 3.628719340405506*^9}, {3.628745719341028*^9, 3.628745719492669*^9}}], 81 | 82 | Cell[CellGroupData[{ 83 | 84 | Cell["Initialize CaffeLink", "Subsection", 85 | CellChangeTimes->{{3.628619135237503*^9, 3.628619142452895*^9}}], 86 | 87 | Cell[TextData[{ 88 | "Normally you would initialize module and load all functions at once with\n \ 89 | ", 90 | StyleBox["Needs[\[OpenCurlyDoubleQuote]CaffeLink`\[CloseCurlyDoubleQuote],\ 91 | FileNameJoin[{NotebookDirectory[],\[CloseCurlyDoubleQuote]..\ 92 | \[CloseCurlyDoubleQuote],\[CloseCurlyDoubleQuote]caffeLink.m\ 93 | \[CloseCurlyDoubleQuote]}]];", "Input"], 94 | StyleBox["\n \ 95 | InitCaffeLinkModule[\[OpenCurlyDoubleQuote]caffe-root/src/caffe/proto/caffe.\ 96 | proto\[CloseCurlyDoubleQuote]];\n", "Input"], 97 | "but for test one function is enough." 98 | }], "Text", 99 | CellChangeTimes->{{3.628619167569005*^9, 3.628619223869854*^9}, { 100 | 3.628708795306896*^9, 3.6287088134024067`*^9}, {3.628719056344322*^9, 101 | 3.628719181569992*^9}, 3.6287193431731863`*^9, 3.62872167114476*^9}], 102 | 103 | Cell[CellGroupData[{ 104 | 105 | Cell[BoxData[ 106 | RowBox[{"testLibLink", "=", 107 | RowBox[{"LibraryFunctionLoad", "[", 108 | RowBox[{"\"\\"", ",", "\"\\"", ",", 109 | RowBox[{"{", 110 | RowBox[{"{", 111 | RowBox[{"Real", ",", "2"}], "}"}], "}"}], ",", 112 | RowBox[{"{", 113 | RowBox[{"Real", ",", "2"}], "}"}]}], "]"}]}]], "Input"], 114 | 115 | Cell[BoxData[ 116 | InterpretationBox[ 117 | RowBox[{ 118 | TagBox["LibraryFunction", 119 | "SummaryHead"], "[", 120 | DynamicModuleBox[{Typeset`open$$ = False}, 121 | PanelBox[ 122 | PaneSelectorBox[{False->GridBox[{ 123 | { 124 | PaneBox[ 125 | ButtonBox[ 126 | 127 | DynamicBox[FEPrivate`FrontEndResource[ 128 | "FEBitmaps", "SquarePlusIconMedium"], 129 | ImageSizeCache->{12., {0., 12.}}], 130 | Appearance->None, 131 | ButtonFunction:>(Typeset`open$$ = True), 132 | Evaluator->Automatic, 133 | Method->"Preemptive"], 134 | Alignment->{Center, Center}, 135 | 136 | ImageSize-> 137 | Dynamic[{ 138 | Automatic, 3.5 CurrentValue["FontCapHeight"]/AbsoluteCurrentValue[ 139 | Magnification]}]], 140 | GraphicsBox[ 141 | {Thickness[0.038461538461538464`], 142 | {FaceForm[{GrayLevel[0.93], Opacity[1.]}], 143 | FilledCurveBox[{{{1, 4, 3}, {0, 1, 0}, {1, 3, 3}, {0, 1, 0}, {1, 144 | 3, 3}, {0, 1, 0}, {1, 3, 3}, {0, 1, 0}}}, {{{25.499999999999996`, 145 | 2.5}, {25.499999999999996`, 1.3953100000000003`}, { 146 | 24.604699999999998`, 0.49999999999999994`}, {23.5, 147 | 0.49999999999999994`}, {2.5, 0.49999999999999994`}, { 148 | 1.3953100000000003`, 0.49999999999999994`}, { 149 | 0.49999999999999994`, 1.3953100000000003`}, { 150 | 0.49999999999999994`, 2.5}, {0.49999999999999994`, 23.5}, { 151 | 0.49999999999999994`, 24.604699999999998`}, {1.3953100000000003`, 152 | 25.499999999999996`}, {2.5, 25.499999999999996`}, {23.5, 153 | 25.499999999999996`}, {24.604699999999998`, 154 | 25.499999999999996`}, {25.499999999999996`, 155 | 24.604699999999998`}, {25.499999999999996`, 23.5}, { 156 | 25.499999999999996`, 2.5}}}]}, 157 | {FaceForm[{RGBColor[0.5, 0.5, 0.5], Opacity[1.]}], 158 | FilledCurveBox[{{{0, 2, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 159 | 1, 0}, {0, 1, 0}, {0, 1, 0}}}, {{{20.5426, 160 | 19.116799999999998`}, {16.3832, 21.876199999999997`}, {16.3832, 161 | 20.021499999999996`}, {6.930469999999999, 20.021499999999996`}, { 162 | 6.930469999999999, 18.212100000000003`}, {16.3832, 163 | 18.212100000000003`}, {16.3832, 16.357399999999995`}, {20.5426, 164 | 19.116799999999998`}}}], 165 | FilledCurveBox[{{{0, 2, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 166 | 1, 0}, {0, 1, 0}, {0, 1, 0}}}, {{{5.30508, 13.8391}, {9.46445, 167 | 11.079700000000003`}, {9.46445, 12.933999999999997`}, { 168 | 18.917199999999998`, 12.933999999999997`}, {18.917199999999998`, 169 | 14.7438}, {9.46445, 14.7438}, {9.46445, 16.598}, {5.30508, 170 | 13.8391}}}], 171 | FilledCurveBox[{{{1, 4, 3}, {1, 3, 3}, {1, 3, 3}, {1, 3, 3}, {0, 172 | 1, 0}, {1, 3, 3}, {1, 3, 3}, {1, 3, 3}, {1, 3, 3}, {1, 3, 3}, {1, 173 | 3, 3}, {1, 3, 3}, {1, 3, 3}, {0, 1, 0}, {1, 3, 3}, {1, 3, 3}, { 174 | 1, 3, 3}, {1, 3, 3}}}, CompressedData[" 175 | 1:eJxTTMoPSmVmYGBgBGJjIGYCYq4u+eR3VWoOxhkv5j+Qk3SA8fu/r5rKOVHM 176 | Yd2vmNyjZ9Qcrt9QXbW9S8RBg59767IidQcPMYPGaHFhB+Nn61SfiGo4xBwp 177 | T7o2V9Dh0Iolfg6/NBxEHv/x1Xgi4FB1XNNqkqQWnP+axUTQjFkbzn+oO2HB 178 | j/3aDg/Xn5T6WiDoYLs3aJpiqI7DrHN+bO6tQg4zfUR7vF7pQOyfIQyRd9d1 179 | WLL0U1kLoyjEPdm6DpoTXfQX7xBzKPjQejLkhDacL5J4+LL2Gm2HtNnSl4Vf 180 | iELM69J2qPZM3tLnJerQX1oozZugDfHPUxGI/4W1HQ5p3TthoyDi4D7lG1v8 181 | Mi2HgqL9zoEHhB1inOxe3BRG8MH62zXhfIbN1Z82CGhC/GMg4nCm3mN/7TYN 182 | iHlCUPdGakDc/1kUEr46GhD/eYnD+d9iBf/kC0vC+TuCrSL+X5Vy2HGR4R5T 183 | uAbEfxYykPBYp+HQqsCuesZAFhL+rJoQ/2rIQdxTpwkJ7yVycPfD+GDzVqLx 184 | xbUd5HYt2JfqBdWfpA2Jj9mykPDpgYaXtaxDy7yzq86vg8qvlIHE5ylY/Eo7 185 | XAt7o787TxfOB5ufqesQ9PbyxxkPZSDhZ6nrMAGU3u7LQtLLEx2HN2253UbX 186 | ofZ76jis/Pay4oynAsT8QG0HKZMv/gW7FCDyTFpwPsz/MD44/Mw1HFjb1uR9 187 | TVVwWKy880/7FHWIexrkIfLX1RzOMDZZVjHIOaDkh3vSDuj5AwCF11Vc 188 | "]]}}, 189 | AspectRatio->1, 190 | Axes->False, 191 | Background->GrayLevel[0.93], 192 | Frame->True, 193 | FrameStyle->Directive[ 194 | Thickness[Tiny], 195 | GrayLevel[0.7]], 196 | FrameTicks->None, 197 | ImageSize->{Automatic, 198 | Dynamic[ 199 | 3.5 (CurrentValue["FontCapHeight"]/AbsoluteCurrentValue[ 200 | Magnification]), ImageSizeCache -> {45., {0., 9.}}]}], 201 | GridBox[{ 202 | { 203 | RowBox[{ 204 | TagBox["\<\"Function name: \"\>", 205 | "SummaryItemAnnotation"], "\[InvisibleSpace]", 206 | TagBox["\<\"testLibLink\"\>", 207 | "SummaryItem"]}]}, 208 | { 209 | RowBox[{ 210 | TagBox["\<\"Argument count: \"\>", 211 | "SummaryItemAnnotation"], "\[InvisibleSpace]", 212 | TagBox["1", 213 | "SummaryItem"]}]} 214 | }, 215 | AutoDelete->False, 216 | 217 | BaseStyle->{ 218 | ShowStringCharacters -> False, NumberMarks -> False, 219 | PrintPrecision -> 3, ShowSyntaxStyles -> False}, 220 | GridBoxAlignment->{"Columns" -> {{Left}}, "Rows" -> {{Automatic}}}, 221 | 222 | GridBoxItemSize->{ 223 | "Columns" -> {{Automatic}}, "Rows" -> {{Automatic}}}, 224 | GridBoxSpacings->{"Columns" -> {{2}}, "Rows" -> {{Automatic}}}]} 225 | }, 226 | AutoDelete->False, 227 | BaselinePosition->{1, 1}, 228 | GridBoxAlignment->{"Rows" -> {{Top}}}, 229 | GridBoxItemSize->{ 230 | "Columns" -> {{Automatic}}, "Rows" -> {{Automatic}}}], True-> 231 | GridBox[{ 232 | { 233 | PaneBox[ 234 | ButtonBox[ 235 | 236 | DynamicBox[FEPrivate`FrontEndResource[ 237 | "FEBitmaps", "SquareMinusIconMedium"]], 238 | Appearance->None, 239 | ButtonFunction:>(Typeset`open$$ = False), 240 | Evaluator->Automatic, 241 | Method->"Preemptive"], 242 | Alignment->{Center, Center}, 243 | 244 | ImageSize-> 245 | Dynamic[{ 246 | Automatic, 3.5 CurrentValue["FontCapHeight"]/AbsoluteCurrentValue[ 247 | Magnification]}]], 248 | GraphicsBox[ 249 | {Thickness[0.038461538461538464`], 250 | {FaceForm[{GrayLevel[0.93], Opacity[1.]}], 251 | FilledCurveBox[{{{1, 4, 3}, {0, 1, 0}, {1, 3, 3}, {0, 1, 0}, {1, 252 | 3, 3}, {0, 1, 0}, {1, 3, 3}, {0, 1, 0}}}, {{{25.499999999999996`, 253 | 2.5}, {25.499999999999996`, 1.3953100000000003`}, { 254 | 24.604699999999998`, 0.49999999999999994`}, {23.5, 255 | 0.49999999999999994`}, {2.5, 0.49999999999999994`}, { 256 | 1.3953100000000003`, 0.49999999999999994`}, { 257 | 0.49999999999999994`, 1.3953100000000003`}, { 258 | 0.49999999999999994`, 2.5}, {0.49999999999999994`, 23.5}, { 259 | 0.49999999999999994`, 24.604699999999998`}, {1.3953100000000003`, 260 | 25.499999999999996`}, {2.5, 25.499999999999996`}, {23.5, 261 | 25.499999999999996`}, {24.604699999999998`, 262 | 25.499999999999996`}, {25.499999999999996`, 263 | 24.604699999999998`}, {25.499999999999996`, 23.5}, { 264 | 25.499999999999996`, 2.5}}}]}, 265 | {FaceForm[{RGBColor[0.5, 0.5, 0.5], Opacity[1.]}], 266 | FilledCurveBox[{{{0, 2, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 267 | 1, 0}, {0, 1, 0}, {0, 1, 0}}}, {{{20.5426, 268 | 19.116799999999998`}, {16.3832, 21.876199999999997`}, {16.3832, 269 | 20.021499999999996`}, {6.930469999999999, 20.021499999999996`}, { 270 | 6.930469999999999, 18.212100000000003`}, {16.3832, 271 | 18.212100000000003`}, {16.3832, 16.357399999999995`}, {20.5426, 272 | 19.116799999999998`}}}], 273 | FilledCurveBox[{{{0, 2, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 274 | 1, 0}, {0, 1, 0}, {0, 1, 0}}}, {{{5.30508, 13.8391}, {9.46445, 275 | 11.079700000000003`}, {9.46445, 12.933999999999997`}, { 276 | 18.917199999999998`, 12.933999999999997`}, {18.917199999999998`, 277 | 14.7438}, {9.46445, 14.7438}, {9.46445, 16.598}, {5.30508, 278 | 13.8391}}}], 279 | FilledCurveBox[{{{1, 4, 3}, {1, 3, 3}, {1, 3, 3}, {1, 3, 3}, {0, 280 | 1, 0}, {1, 3, 3}, {1, 3, 3}, {1, 3, 3}, {1, 3, 3}, {1, 3, 3}, {1, 281 | 3, 3}, {1, 3, 3}, {1, 3, 3}, {0, 1, 0}, {1, 3, 3}, {1, 3, 3}, { 282 | 1, 3, 3}, {1, 3, 3}}}, CompressedData[" 283 | 1:eJxTTMoPSmVmYGBgBGJjIGYCYq4u+eR3VWoOxhkv5j+Qk3SA8fu/r5rKOVHM 284 | Yd2vmNyjZ9Qcrt9QXbW9S8RBg59767IidQcPMYPGaHFhB+Nn61SfiGo4xBwp 285 | T7o2V9Dh0Iolfg6/NBxEHv/x1Xgi4FB1XNNqkqQWnP+axUTQjFkbzn+oO2HB 286 | j/3aDg/Xn5T6WiDoYLs3aJpiqI7DrHN+bO6tQg4zfUR7vF7pQOyfIQyRd9d1 287 | WLL0U1kLoyjEPdm6DpoTXfQX7xBzKPjQejLkhDacL5J4+LL2Gm2HtNnSl4Vf 288 | iELM69J2qPZM3tLnJerQX1oozZugDfHPUxGI/4W1HQ5p3TthoyDi4D7lG1v8 289 | Mi2HgqL9zoEHhB1inOxe3BRG8MH62zXhfIbN1Z82CGhC/GMg4nCm3mN/7TYN 290 | iHlCUPdGakDc/1kUEr46GhD/eYnD+d9iBf/kC0vC+TuCrSL+X5Vy2HGR4R5T 291 | uAbEfxYykPBYp+HQqsCuesZAFhL+rJoQ/2rIQdxTpwkJ7yVycPfD+GDzVqLx 292 | xbUd5HYt2JfqBdWfpA2Jj9mykPDpgYaXtaxDy7yzq86vg8qvlIHE5ylY/Eo7 293 | XAt7o787TxfOB5ufqesQ9PbyxxkPZSDhZ6nrMAGU3u7LQtLLEx2HN2253UbX 294 | ofZ76jis/Pay4oynAsT8QG0HKZMv/gW7FCDyTFpwPsz/MD44/Mw1HFjb1uR9 295 | TVVwWKy880/7FHWIexrkIfLX1RzOMDZZVjHIOaDkh3vSDuj5AwCF11Vc 296 | "]]}}, 297 | AspectRatio->1, 298 | Axes->False, 299 | Background->GrayLevel[0.93], 300 | Frame->True, 301 | FrameStyle->Directive[ 302 | Thickness[Tiny], 303 | GrayLevel[0.7]], 304 | FrameTicks->None, 305 | ImageSize->{Automatic, 306 | Dynamic[ 307 | 3.5 (CurrentValue["FontCapHeight"]/AbsoluteCurrentValue[ 308 | Magnification]), ImageSizeCache -> {45., {0., 9.}}]}], 309 | GridBox[{ 310 | { 311 | RowBox[{ 312 | TagBox["\<\"Function name: \"\>", 313 | "SummaryItemAnnotation"], "\[InvisibleSpace]", 314 | TagBox["\<\"testLibLink\"\>", 315 | "SummaryItem"]}]}, 316 | { 317 | RowBox[{ 318 | TagBox["\<\"Argument count: \"\>", 319 | "SummaryItemAnnotation"], "\[InvisibleSpace]", 320 | TagBox["1", 321 | "SummaryItem"]}]}, 322 | { 323 | RowBox[{ 324 | TagBox["\<\"Argument types: \"\>", 325 | "SummaryItemAnnotation"], "\[InvisibleSpace]", 326 | TagBox[ 327 | PaneBox[ 328 | RowBox[{"{", 329 | RowBox[{"{", 330 | RowBox[{"Real", ",", "2"}], "}"}], "}"}], 331 | BaselinePosition->Baseline, 332 | ContentPadding->False, 333 | FrameMargins->0, 334 | ImageSize->{{1, 500}, Automatic}, 335 | StripOnInput->True], 336 | "SummaryItem"]}]}, 337 | { 338 | RowBox[{ 339 | TagBox["\<\"Return type: \"\>", 340 | "SummaryItemAnnotation"], "\[InvisibleSpace]", 341 | TagBox[ 342 | PaneBox[ 343 | RowBox[{"{", 344 | RowBox[{"Real", ",", "2"}], "}"}], 345 | BaselinePosition->Baseline, 346 | ContentPadding->False, 347 | FrameMargins->0, 348 | ImageSize->{{1, 500}, Automatic}, 349 | StripOnInput->True], 350 | "SummaryItem"]}]}, 351 | { 352 | RowBox[{ 353 | TagBox["\<\"Library: \"\>", 354 | "SummaryItemAnnotation"], "\[InvisibleSpace]", 355 | TagBox["\<\"libcaffeLink.so\"\>", 356 | "SummaryItem"]}]} 357 | }, 358 | AutoDelete->False, 359 | 360 | BaseStyle->{ 361 | ShowStringCharacters -> False, NumberMarks -> False, 362 | PrintPrecision -> 3, ShowSyntaxStyles -> False}, 363 | GridBoxAlignment->{"Columns" -> {{Left}}, "Rows" -> {{Automatic}}}, 364 | 365 | GridBoxItemSize->{ 366 | "Columns" -> {{Automatic}}, "Rows" -> {{Automatic}}}, 367 | GridBoxSpacings->{"Columns" -> {{2}}, "Rows" -> {{Automatic}}}]} 368 | }, 369 | AutoDelete->False, 370 | BaselinePosition->{1, 1}, 371 | GridBoxAlignment->{"Rows" -> {{Top}}}, 372 | GridBoxItemSize->{ 373 | "Columns" -> {{Automatic}}, "Rows" -> {{Automatic}}}]}, Dynamic[ 374 | Typeset`open$$], 375 | ImageSize->Automatic], 376 | BaselinePosition->Baseline], 377 | DynamicModuleValues:>{}], "]"}], 378 | LibraryFunction[ 379 | "/home/kerhy/.Mathematica/SystemFiles/LibraryResources/Linux-x86-64/\ 380 | libcaffeLink.so", "testLibLink", {{Real, 2}}, {Real, 2}], 381 | Editable->False, 382 | SelectWithContents->True, 383 | Selectable->False]], "Output", 384 | CellChangeTimes->{3.6287191952634563`*^9, 3.6287193788139277`*^9}] 385 | }, Open ]], 386 | 387 | Cell["Should anything go wrong, check", "Text", 388 | CellChangeTimes->{{3.6286343923985662`*^9, 3.6286345244155293`*^9}, { 389 | 3.628634570911632*^9, 3.628634578951747*^9}, 3.628708887859647*^9, { 390 | 3.6287089337321033`*^9, 3.628708946020158*^9}, {3.6287192014496813`*^9, 391 | 3.628719210497307*^9}, {3.62871986275898*^9, 3.628719864518695*^9}, { 392 | 3.6287200978492527`*^9, 3.628720098097443*^9}}], 393 | 394 | Cell[CellGroupData[{ 395 | 396 | Cell[BoxData["LibraryLink`$LibraryError"], "Input", 397 | InitializationCell->True, 398 | CellChangeTimes->{{3.628634527533304*^9, 3.62863456453273*^9}, { 399 | 3.628695965176127*^9, 3.628695965998765*^9}, {3.628704926285433*^9, 400 | 3.628704926812426*^9}, 3.628706884246049*^9, 3.628719230158777*^9}], 401 | 402 | Cell[BoxData["\<\"/home/kerhy/.Mathematica/SystemFiles/LibraryResources/Linux-\ 403 | x86-64/libcaffeLink.so: undefined symbol: \ 404 | WolframCompileLibrary_wrapper\"\>"], "Output", 405 | CellChangeTimes->{3.6287192316300087`*^9, 3.6287193822414722`*^9}] 406 | }, Open ]], 407 | 408 | Cell[TextData[{ 409 | "and try resolving the error. Message ", 410 | StyleBox["undefined symbol: WolframCompileLibrary_wrapper", "Output"], 411 | " seems to be fine." 412 | }], "Text", 413 | CellChangeTimes->{{3.628634589377369*^9, 3.628634638432069*^9}, { 414 | 3.628708955947938*^9, 3.628708956907789*^9}, 3.628711991150771*^9, { 415 | 3.628719237905466*^9, 3.628719247417884*^9}, 3.6287193502691593`*^9, 416 | 3.628719404347487*^9, {3.628719434779977*^9, 3.6287194416595383`*^9}, { 417 | 3.628719590221293*^9, 3.628719590476479*^9}, {3.6287203170188303`*^9, 418 | 3.628720322867412*^9}}], 419 | 420 | Cell["Else you should be ready to go.", "Text", 421 | CellChangeTimes->{{3.6287192635291653`*^9, 3.628719280842279*^9}}], 422 | 423 | Cell[CellGroupData[{ 424 | 425 | Cell[BoxData[ 426 | RowBox[{ 427 | RowBox[{"testLibLink", "[", 428 | RowBox[{"{", 429 | RowBox[{ 430 | RowBox[{"{", 431 | RowBox[{"3", ",", "4", ",", "5"}], "}"}], ",", 432 | RowBox[{"{", 433 | RowBox[{"1", ",", "2", ",", "6"}], "}"}]}], "}"}], "]"}], " ", 434 | RowBox[{"(*", " ", 435 | RowBox[{"returns", " ", "each", " ", "element", " ", "squared"}], " ", 436 | "*)"}]}]], "Input", 437 | CellChangeTimes->{{3.6287194870017548`*^9, 3.6287195121544237`*^9}}], 438 | 439 | Cell[BoxData[ 440 | RowBox[{"{", 441 | RowBox[{ 442 | RowBox[{"{", 443 | RowBox[{"9.`", ",", "16.`", ",", "25.`"}], "}"}], ",", 444 | RowBox[{"{", 445 | RowBox[{"1.`", ",", "4.`", ",", "36.`"}], "}"}]}], "}"}]], "Output", 446 | CellChangeTimes->{3.6287192959671593`*^9, 3.6287194760003843`*^9}] 447 | }, Open ]] 448 | }, Open ]] 449 | }, Open ]] 450 | }, Open ]] 451 | }, Open ]] 452 | }, 453 | WindowToolbars->"EditBar", 454 | WindowSize->{1049, 990}, 455 | WindowMargins->{{Automatic, 0}, {Automatic, 0}}, 456 | SpellingDictionaries->{"CorrectWords"->{"Caffe"}}, 457 | FrontEndVersion->"10.0 for Linux x86 (64-bit) (September 9, 2014)", 458 | StyleDefinitions->"Default.nb" 459 | ] 460 | (* End of Notebook Content *) 461 | 462 | (* Internal cache information *) 463 | (*CellTagsOutline 464 | CellTagsIndex->{} 465 | *) 466 | (*CellTagsIndex 467 | CellTagsIndex->{} 468 | *) 469 | (*NotebookFileOutline 470 | Notebook[{ 471 | Cell[CellGroupData[{ 472 | Cell[580, 22, 143, 2, 117, "Title"], 473 | Cell[CellGroupData[{ 474 | Cell[748, 28, 244, 3, 85, "Chapter"], 475 | Cell[CellGroupData[{ 476 | Cell[1017, 35, 237, 3, 81, "Section"], 477 | Cell[1257, 40, 311, 9, 68, "Text"], 478 | Cell[1571, 51, 38, 0, 37, "Input"], 479 | Cell[1612, 53, 128, 5, 39, "Text"], 480 | Cell[CellGroupData[{ 481 | Cell[1765, 62, 149, 2, 37, "Input"], 482 | Cell[1917, 66, 185, 2, 66, "Output"] 483 | }, Open ]], 484 | Cell[2117, 71, 435, 8, 67, "Text"], 485 | Cell[CellGroupData[{ 486 | Cell[2577, 83, 108, 1, 56, "Subsection"], 487 | Cell[2688, 86, 745, 14, 120, "Text"], 488 | Cell[CellGroupData[{ 489 | Cell[3458, 104, 321, 8, 66, "Input"], 490 | Cell[3782, 114, 12208, 269, 76, "Output"] 491 | }, Open ]], 492 | Cell[16005, 386, 390, 5, 39, "Text"], 493 | Cell[CellGroupData[{ 494 | Cell[16420, 395, 287, 4, 37, "Input", 495 | InitializationCell->True], 496 | Cell[16710, 401, 238, 3, 66, "Output"] 497 | }, Open ]], 498 | Cell[16963, 407, 551, 10, 67, "Text"], 499 | Cell[17517, 419, 115, 1, 39, "Text"], 500 | Cell[CellGroupData[{ 501 | Cell[17657, 424, 436, 12, 66, "Input"], 502 | Cell[18096, 438, 274, 7, 37, "Output"] 503 | }, Open ]] 504 | }, Open ]] 505 | }, Open ]] 506 | }, Open ]] 507 | }, Open ]] 508 | } 509 | ] 510 | *) 511 | 512 | (* End of internal cache information *) 513 | -------------------------------------------------------------------------------- /module/demo/mnist.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seilim/CaffeLink/efd16474e3a19e73ed091fffa98aa062259cd33f/module/demo/mnist.pdf -------------------------------------------------------------------------------- /utils.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | extern "C" { 6 | #include 7 | } 8 | 9 | #include "caffe/caffe.hpp" 10 | #include "WolframLibrary.h" 11 | 12 | #include "build_utils.hpp" 13 | 14 | extern "C" bool doublesToFloats(double* in, float** out, long size) 15 | { 16 | int i; 17 | float *tmp; 18 | tmp = (float*) realloc(*out, sizeof(float) * size); 19 | 20 | if (!tmp) { 21 | printf("%s: allocation failed\n", __FUNCTION__); 22 | return false; 23 | } 24 | *out = tmp; 25 | 26 | for (i = 0; i < size; i++) 27 | (*out)[i] = in[i]; 28 | return true; 29 | } 30 | 31 | /** Simple function to test library link. Gets one tensor on input and returns 32 | * second with squared elements of the first. 33 | * In Mathematica: tll = LibraryLoadFunction["libcaffeLink", "testLibLink", 34 | * {{Real, 2}}, {Real, 2}] 35 | * tll[{{3, 4, 5}, {1, 2, 6}}] --> {{9., 16., 25.}, {1., 4., 36.}} */ 36 | extern "C" DLLEXPORT int testLibLink(LIB_LINK_ARGS) 37 | { 38 | int err; // error code 39 | MTensor m1; // input tensor 40 | MTensor m2; // output tensor 41 | 42 | mint const* dims; // dimensions of the tensor 43 | 44 | double* data1; // actual data of the input tensor 45 | double* data2; // data for the output tensor 46 | 47 | mint i; // bean counters 48 | mint j; 49 | 50 | m1 = MArgument_getMTensor(Args[0]); 51 | dims = libData->MTensor_getDimensions(m1); 52 | err = libData->MTensor_new(MType_Real, 2, dims, &m2); 53 | if(err) 54 | return LIBRARY_MEMORY_ERROR; 55 | data1 = libData->MTensor_getRealData(m1); 56 | data2 = libData->MTensor_getRealData(m2); 57 | 58 | for (i = 0; i < dims[0]; i++) { 59 | for (j = 0; j < dims[1]; j++) { 60 | data2[i * dims[1] + j] = data1[i * dims[1] + j] * data1[i * dims[1] + j]; 61 | } 62 | } 63 | 64 | MArgument_setMTensor(Res, m2); 65 | return LIBRARY_NO_ERROR; 66 | } 67 | 68 | extern "C" void cblasTest_() 69 | { 70 | #ifdef CBLAS_TEST 71 | 72 | // check Atlas version, only if you build this with atlas (-latlas) 73 | // void ATL_buildinfo(void); 74 | // ATL_buildinfo(); 75 | 76 | printf("Testing cblast_sgemm...\n"); 77 | 78 | const int M = 10; 79 | const int N = 8; 80 | const int K = 5; 81 | 82 | float *A = new float[M * K]; 83 | float *B = new float[K * N]; 84 | float *C = new float[M * N]; 85 | 86 | cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 87 | M, N, K, 1.0, A, K, B, N, 0.0, C, N); 88 | 89 | delete A; 90 | delete B; 91 | delete C; 92 | 93 | printf("Success, cblast_sgemm is ok.\n"); 94 | #else 95 | printf("To test cblast_sgemm, rebuild caffeLink with CBLAS_TEST defined.\n"); 96 | #endif 97 | } 98 | 99 | /** In case your kernel crushes in CPU mode, you can try calling this 100 | * function cblas_test and if it crushes again, then it is the mentioned 101 | * cblas_sggem. */ 102 | extern "C" DLLEXPORT int cblasTest(LIB_LINK_ARGS) 103 | { 104 | cblasTest_(); 105 | return LIBRARY_NO_ERROR; 106 | } 107 | 108 | 109 | -------------------------------------------------------------------------------- /utils.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef UTILS_HPP 3 | #define UTILS_HPP 4 | 5 | extern "C" { 6 | 7 | /** 8 | * Converts doubles: \c in to floats: \c out. 9 | * @param in input 10 | * @param out output 11 | * @param size size of arrays 12 | * @return \c true on success, \c false otherwise 13 | */ 14 | bool doublesToFloats(double* in, float** out, long size); 15 | 16 | /** 17 | * Test of BLAS function cblas_sgemm. 18 | * 19 | * I have trouble running Caffe (caffeLink) from Mathematica in CPU mode. There 20 | * is some problem with cblas_sggem() - Mathematica kernel just crashes after 21 | * calling this function. However in GPU mode everything is fine (no need for 22 | * cblas_sggem()). 23 | * 24 | * I tried it with Atlas 3.8.4 (Debian version), Mathematica 10.0.1.0, 25 | * release candidate for Caffe 1.0 (Sep. 19 2014) and os: Debian 7.1 64x. 26 | * 27 | * In case your kernel crushes in CPU mode, you can try calling this 28 | * function cblas_test and if it crushes again, then it is the mentioned 29 | * cblas_sggem. 30 | */ 31 | void cblasTest_(); 32 | 33 | } 34 | 35 | #endif /* UTILS_HPP */ 36 | 37 | --------------------------------------------------------------------------------