Created
May 9, 2013 17:11
-
-
Save anonymous/5548936 to your computer and use it in GitHub Desktop.
output of python -c "import theano".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
00001 #include <Python.h> | |
00002 #include "structmember.h" | |
00003 #include <sys/time.h> | |
00004 | |
00005 // Old Python compatibility from here: | |
00006 // http://www.python.org/dev/peps/pep-0353/ | |
00007 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) | |
00008 typedef int Py_ssize_t; | |
00009 #define PY_SSIZE_T_MAX INT_MAX | |
00010 #define PY_SSIZE_T_MIN INT_MIN | |
00011 // This one was taken from: | |
00012 // http://svn.python.org/projects/python/trunk/Modules/_ctypes/ctypes.h | |
00013 #define PyNumber_AsSsize_t(ob, exc) PyInt_AsLong(ob) | |
00014 #endif | |
00015 | |
00016 /** | |
00017 | |
00018 TODO: | |
00019 - Check max supported depth of recursion | |
00020 - CLazyLinker should add context information to errors caught during evaluation. Say what node we were on, add the traceback attached to the node. | |
00021 - Clear containers of fully-useed intermediate results if allow_gc is 1 | |
00022 - Add timers for profiling | |
00023 - Add support for profiling space used. | |
00024 | |
00025 | |
00026 */ | |
00027 static double pytime(const struct timeval * tv) | |
00028 { | |
00029 struct timeval t; | |
00030 if (!tv) | |
00031 { | |
00032 tv = &t; | |
00033 gettimeofday(&t, NULL); | |
00034 } | |
00035 return (double) tv->tv_sec + (double) tv->tv_usec / 1000000.0; | |
00036 } | |
00037 | |
00038 /** | |
00039 Helper routine to convert a PyList of integers to a c array of integers. | |
00040 */ | |
00041 static int unpack_list_of_ssize_t(PyObject * pylist, Py_ssize_t **dst, Py_ssize_t *len, | |
00042 const char* kwname) | |
00043 { | |
00044 Py_ssize_t buflen, *buf; | |
00045 if (!PyList_Check(pylist)) | |
00046 { | |
00047 PyErr_Format(PyExc_TypeError, "%s must be list", kwname); | |
00048 return -1; | |
00049 } | |
00050 assert (NULL == *dst); | |
00051 *len = buflen = PyList_Size(pylist); | |
00052 *dst = buf = (Py_ssize_t*)calloc(buflen, sizeof(Py_ssize_t)); | |
00053 assert(buf); | |
00054 for (int ii = 0; ii < buflen; ++ii) | |
00055 { | |
00056 PyObject * el_i = PyList_GetItem(pylist, ii); | |
00057 Py_ssize_t n_i = PyNumber_AsSsize_t(el_i, PyExc_IndexError); | |
00058 if (PyErr_Occurred()) | |
00059 { | |
00060 free(buf); | |
00061 *dst = NULL; | |
00062 return -1; | |
00063 } | |
00064 buf[ii] = n_i; | |
00065 } | |
00066 return 0; | |
00067 } | |
00068 | |
00069 /** | |
00070 | |
00071 CLazyLinker | |
00072 | |
00073 | |
00074 */ | |
00075 typedef struct { | |
00076 PyObject_HEAD | |
00077 /* Type-specific fields go here. */ | |
00078 PyObject * nodes; // the python list of nodes | |
00079 PyObject * thunks; // python list of thunks | |
00080 PyObject * pre_call_clear; //list of cells to clear on call. | |
00081 int allow_gc; | |
00082 Py_ssize_t n_applies; | |
00083 int n_vars; // number of variables in the graph | |
00084 int * var_computed; // 1 or 0 for every variable | |
00085 PyObject ** var_computed_cells; | |
00086 PyObject ** var_value_cells; | |
00087 Py_ssize_t **dependencies; // list of vars dependencies for GC | |
00088 Py_ssize_t *n_dependencies; | |
00089 | |
00090 Py_ssize_t n_output_vars; | |
00091 Py_ssize_t * output_vars; // variables that *must* be evaluated by call | |
00092 | |
00093 int * is_lazy; // 1 or 0 for every thunk | |
00094 | |
00095 Py_ssize_t * var_owner; // nodes[[var_owner[var_idx]]] is var[var_idx]->owner | |
00096 int * var_has_owner; // 1 or 0 | |
00097 | |
00098 Py_ssize_t * node_n_inputs; | |
00099 Py_ssize_t * node_n_outputs; | |
00100 Py_ssize_t ** node_inputs; | |
00101 Py_ssize_t ** node_outputs; | |
00102 Py_ssize_t * node_inputs_outputs_base; // node_inputs and node_outputs point into this | |
00103 Py_ssize_t * node_n_prereqs; | |
00104 Py_ssize_t ** node_prereqs; | |
00105 | |
00106 Py_ssize_t * update_storage; // input cells to update with the last outputs in output_vars | |
00107 Py_ssize_t n_updates; | |
00108 | |
00109 void ** thunk_cptr_fn; | |
00110 void ** thunk_cptr_data; | |
00111 PyObject * call_times; | |
00112 PyObject * call_counts; | |
00113 int do_timing; | |
00114 int need_update_inputs; | |
00115 int position_of_error; // -1 for no error, otw the index into `thunks` that failed. | |
00116 } CLazyLinker; | |
00117 | |
00118 | |
00119 static void | |
00120 CLazyLinker_dealloc(PyObject* _self) | |
00121 { | |
00122 CLazyLinker* self = (CLazyLinker *) _self; | |
00123 free(self->thunk_cptr_fn); | |
00124 free(self->thunk_cptr_data); | |
00125 | |
00126 free(self->is_lazy); | |
00127 | |
00128 free(self->update_storage); | |
00129 | |
00130 if (self->node_n_prereqs) | |
00131 { | |
00132 for (int i = 0; i < self->n_applies; ++i) | |
00133 { | |
00134 free(self->node_prereqs[i]); | |
00135 } | |
00136 } | |
00137 free(self->node_n_prereqs); | |
00138 free(self->node_prereqs); | |
00139 free(self->node_inputs_outputs_base); | |
00140 free(self->node_n_inputs); | |
00141 free(self->node_n_outputs); | |
00142 free(self->node_inputs); | |
00143 free(self->node_outputs); | |
00144 | |
00145 if (self->dependencies) | |
00146 { | |
00147 for (int i = 0; i < self->n_vars; ++i) | |
00148 { | |
00149 free(self->dependencies[i]); | |
00150 } | |
00151 free(self->dependencies); | |
00152 free(self->n_dependencies); | |
00153 } | |
00154 | |
00155 free(self->var_owner); | |
00156 free(self->var_has_owner); | |
00157 free(self->var_computed); | |
00158 if (self->var_computed_cells) | |
00159 { | |
00160 for (int i = 0; i < self->n_vars; ++i) | |
00161 { | |
00162 Py_DECREF(self->var_computed_cells[i]); | |
00163 Py_DECREF(self->var_value_cells[i]); | |
00164 } | |
00165 } | |
00166 free(self->var_computed_cells); | |
00167 free(self->var_value_cells); | |
00168 free(self->output_vars); | |
00169 | |
00170 Py_XDECREF(self->nodes); | |
00171 Py_XDECREF(self->thunks); | |
00172 Py_XDECREF(self->call_times); | |
00173 Py_XDECREF(self->call_counts); | |
00174 Py_XDECREF(self->pre_call_clear); | |
00175 self->ob_type->tp_free((PyObject*)self); | |
00176 } | |
00177 static PyObject * | |
00178 CLazyLinker_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |
00179 { | |
00180 CLazyLinker *self; | |
00181 | |
00182 self = (CLazyLinker *)type->tp_alloc(type, 0); | |
00183 if (self != NULL) { | |
00184 self->nodes = NULL; | |
00185 self->thunks = NULL; | |
00186 self->pre_call_clear = NULL; | |
00187 | |
00188 self->allow_gc = 1; | |
00189 self->n_applies = 0; | |
00190 self->n_vars = 0; | |
00191 self->var_computed = NULL; | |
00192 self->var_computed_cells = NULL; | |
00193 self->var_value_cells = NULL; | |
00194 self->dependencies = NULL; | |
00195 self->n_dependencies = NULL; | |
00196 | |
00197 self->n_output_vars = 0; | |
00198 self->output_vars = NULL; | |
00199 | |
00200 self->is_lazy = NULL; | |
00201 | |
00202 self->var_owner = NULL; | |
00203 self->var_has_owner = NULL; | |
00204 | |
00205 self->node_n_inputs = NULL; | |
00206 self->node_n_outputs = NULL; | |
00207 self->node_inputs = NULL; | |
00208 self->node_outputs = NULL; | |
00209 self->node_inputs_outputs_base = NULL; | |
00210 self->node_prereqs = NULL; | |
00211 self->node_n_prereqs = NULL; | |
00212 | |
00213 self->update_storage = NULL; | |
00214 self->n_updates = 0; | |
00215 | |
00216 self->thunk_cptr_data = NULL; | |
00217 self->thunk_cptr_fn = NULL; | |
00218 self->call_times = NULL; | |
00219 self->call_counts = NULL; | |
00220 self->do_timing = 0; | |
00221 | |
00222 self->need_update_inputs = 0; | |
00223 self->position_of_error = -1; | |
00224 } | |
00225 return (PyObject *)self; | |
00226 } | |
00227 | |
00228 static int | |
00229 CLazyLinker_init(CLazyLinker *self, PyObject *args, PyObject *kwds) | |
00230 { | |
00231 static char *kwlist[] = { | |
00232 (char*)"nodes", | |
00233 (char*)"thunks", | |
00234 (char*)"pre_call_clear", | |
00235 (char*)"allow_gc", | |
00236 (char*)"call_counts", | |
00237 (char*)"call_times", | |
00238 (char*)"compute_map_list", | |
00239 (char*)"storage_map_list", | |
00240 (char*)"base_input_output_list", | |
00241 (char*)"node_n_inputs", | |
00242 (char*)"node_n_outputs", | |
00243 (char*)"node_input_offset", | |
00244 (char*)"node_output_offset", | |
00245 (char*)"var_owner", | |
00246 (char*)"is_lazy_list", | |
00247 (char*)"output_vars", | |
00248 (char*)"node_prereqs", | |
00249 (char*)"node_output_size", | |
00250 (char*)"update_storage", | |
00251 (char*)"dependencies", | |
00252 NULL}; | |
00253 | |
00254 PyObject *compute_map_list=NULL, | |
00255 *storage_map_list=NULL, | |
00256 *base_input_output_list=NULL, | |
00257 *node_n_inputs=NULL, | |
00258 *node_n_outputs=NULL, | |
00259 *node_input_offset=NULL, | |
00260 *node_output_offset=NULL, | |
00261 *var_owner=NULL, | |
00262 *is_lazy=NULL, | |
00263 *output_vars=NULL, | |
00264 *node_prereqs=NULL, | |
00265 *node_output_size=NULL, | |
00266 *update_storage=NULL, | |
00267 *dependencies=NULL; | |
00268 | |
00269 assert(!self->nodes); | |
00270 if (! PyArg_ParseTupleAndKeywords(args, kwds, "OOOiOOOOOOOOOOOOOOOO", kwlist, | |
00271 &self->nodes, | |
00272 &self->thunks, | |
00273 &self->pre_call_clear, | |
00274 &self->allow_gc, | |
00275 &self->call_counts, | |
00276 &self->call_times, | |
00277 &compute_map_list, | |
00278 &storage_map_list, | |
00279 &base_input_output_list, | |
00280 &node_n_inputs, | |
00281 &node_n_outputs, | |
00282 &node_input_offset, | |
00283 &node_output_offset, | |
00284 &var_owner, | |
00285 &is_lazy, | |
00286 &output_vars, | |
00287 &node_prereqs, | |
00288 &node_output_size, | |
00289 &update_storage, | |
00290 &dependencies | |
00291 )) | |
00292 return -1; | |
00293 Py_INCREF(self->nodes); | |
00294 Py_INCREF(self->thunks); | |
00295 Py_INCREF(self->pre_call_clear); | |
00296 Py_INCREF(self->call_counts); | |
00297 Py_INCREF(self->call_times); | |
00298 | |
00299 Py_ssize_t n_applies = PyList_Size(self->nodes); | |
00300 | |
00301 self->n_applies = n_applies; | |
00302 self->n_vars = PyList_Size(var_owner); | |
00303 | |
00304 if (PyList_Size(self->thunks) != n_applies) return -1; | |
00305 if (PyList_Size(self->call_counts) != n_applies) return -1; | |
00306 if (PyList_Size(self->call_times) != n_applies) return -1; | |
00307 | |
00308 // allocated and initialize thunk_cptr_data and thunk_cptr_fn | |
00309 if (n_applies) | |
00310 { | |
00311 self->thunk_cptr_data = (void**)calloc(n_applies, sizeof(void*)); | |
00312 self->thunk_cptr_fn = (void**)calloc(n_applies, sizeof(void*)); | |
00313 self->is_lazy = (int*)calloc(n_applies, sizeof(int)); | |
00314 self->node_prereqs = (Py_ssize_t**)calloc(n_applies, sizeof(Py_ssize_t*)); | |
00315 self->node_n_prereqs = (Py_ssize_t*)calloc(n_applies, sizeof(Py_ssize_t)); | |
00316 assert(self->node_prereqs); | |
00317 assert(self->node_n_prereqs); | |
00318 assert(self->is_lazy); | |
00319 assert(self->thunk_cptr_fn); | |
00320 assert(self->thunk_cptr_data); | |
00321 | |
00322 for (int i = 0; i < n_applies; ++i) | |
00323 { | |
00324 PyObject * thunk = PyList_GetItem(self->thunks, i); | |
00325 //thunk is borrowed | |
00326 if (PyObject_HasAttrString(thunk, "cthunk")) | |
00327 { | |
00328 PyObject * cthunk = PyObject_GetAttrString(thunk, "cthunk"); | |
00329 //new reference | |
00330 assert (cthunk && PyCObject_Check(cthunk)); | |
00331 self->thunk_cptr_fn[i] = PyCObject_AsVoidPtr(cthunk); | |
00332 self->thunk_cptr_data[i] = PyCObject_GetDesc(cthunk); | |
00333 Py_DECREF(cthunk); | |
00334 // cthunk is kept alive by membership in self->thunks | |
00335 } | |
00336 | |
00337 PyObject * el_i = PyList_GetItem(is_lazy, i); | |
00338 self->is_lazy[i] = PyNumber_AsSsize_t(el_i, NULL); | |
00339 | |
00340 /* now get the prereqs */ | |
00341 el_i = PyList_GetItem(node_prereqs, i); | |
00342 assert (PyList_Check(el_i)); | |
00343 self->node_n_prereqs[i] = PyList_Size(el_i); | |
00344 if (self->node_n_prereqs[i]) | |
00345 { | |
00346 self->node_prereqs[i] = (Py_ssize_t*)malloc( | |
00347 PyList_Size(el_i)*sizeof(Py_ssize_t)); | |
00348 for (int j = 0; j < PyList_Size(el_i); ++j) | |
00349 { | |
00350 PyObject * el_ij = PyList_GetItem(el_i, j); | |
00351 Py_ssize_t N = PyNumber_AsSsize_t(el_ij, PyExc_IndexError); | |
00352 if (PyErr_Occurred()) | |
00353 return -1; | |
00354 // N < n. variables | |
00355 assert(N < PyList_Size(var_owner)); | |
00356 self->node_prereqs[i][j] = N; | |
00357 } | |
00358 } | |
00359 } | |
00360 } | |
00361 if (PyList_Check(base_input_output_list)) | |
00362 { | |
00363 Py_ssize_t n_inputs_outputs_base = PyList_Size(base_input_output_list); | |
00364 self->node_inputs_outputs_base = (Py_ssize_t*)calloc(n_inputs_outputs_base,sizeof(Py_ssize_t)); | |
00365 assert(self->node_inputs_outputs_base); | |
00366 for (int i = 0; i < n_inputs_outputs_base; ++i) | |
00367 { | |
00368 PyObject *el_i = PyList_GetItem(base_input_output_list, i); | |
00369 Py_ssize_t idx = PyNumber_AsSsize_t(el_i, PyExc_IndexError); | |
00370 if (PyErr_Occurred()) return -1; | |
00371 self->node_inputs_outputs_base[i] = idx; | |
00372 } | |
00373 self->node_n_inputs = (Py_ssize_t*)calloc(n_applies,sizeof(Py_ssize_t)); | |
00374 assert(self->node_n_inputs); | |
00375 self->node_n_outputs = (Py_ssize_t*)calloc(n_applies,sizeof(Py_ssize_t)); | |
00376 assert(self->node_n_outputs); | |
00377 self->node_inputs = (Py_ssize_t**)calloc(n_applies,sizeof(Py_ssize_t*)); | |
00378 assert(self->node_inputs); | |
00379 self->node_outputs = (Py_ssize_t**)calloc(n_applies,sizeof(Py_ssize_t*)); | |
00380 assert(self->node_outputs); | |
00381 for (int i = 0; i < n_applies; ++i) | |
00382 { | |
00383 Py_ssize_t N; | |
00384 N = PyNumber_AsSsize_t(PyList_GetItem(node_n_inputs, i),PyExc_IndexError); | |
00385 if (PyErr_Occurred()) return -1; | |
00386 assert (N <= n_inputs_outputs_base); | |
00387 self->node_n_inputs[i] = N; | |
00388 N = PyNumber_AsSsize_t(PyList_GetItem(node_n_outputs, i),PyExc_IndexError); | |
00389 if (PyErr_Occurred()) return -1; | |
00390 assert (N <= n_inputs_outputs_base); | |
00391 self->node_n_outputs[i] = N; | |
00392 N = PyNumber_AsSsize_t(PyList_GetItem(node_input_offset, i),PyExc_IndexError); | |
00393 if (PyErr_Occurred()) return -1; | |
00394 assert (N <= n_inputs_outputs_base); | |
00395 self->node_inputs[i] = &self->node_inputs_outputs_base[N]; | |
00396 N = PyNumber_AsSsize_t(PyList_GetItem(node_output_offset, i),PyExc_IndexError); | |
00397 if (PyErr_Occurred()) return -1; | |
00398 assert (N <= n_inputs_outputs_base); | |
00399 self->node_outputs[i] = &self->node_inputs_outputs_base[N]; | |
00400 } | |
00401 } | |
00402 else | |
00403 { | |
00404 PyErr_SetString(PyExc_TypeError, "base_input_output_list must be list"); | |
00405 return -1; | |
00406 } | |
00407 | |
00408 // allocation for var_owner | |
00409 if (PyList_Check(var_owner)) | |
00410 { | |
00411 self->var_owner = (Py_ssize_t*)calloc(self->n_vars,sizeof(Py_ssize_t)); | |
00412 self->var_has_owner = (int*)calloc(self->n_vars,sizeof(int)); | |
00413 self->var_computed = (int*)calloc(self->n_vars,sizeof(int)); | |
00414 self->var_computed_cells = (PyObject**)calloc(self->n_vars,sizeof(PyObject*)); | |
00415 self->var_value_cells = (PyObject**)calloc(self->n_vars,sizeof(PyObject*)); | |
00416 for (int i = 0; i < self->n_vars; ++i) | |
00417 { | |
00418 PyObject * el_i = PyList_GetItem(var_owner, i); | |
00419 if (el_i == Py_None) | |
00420 { | |
00421 self->var_has_owner[i] = 0; | |
00422 } | |
00423 else | |
00424 { | |
00425 Py_ssize_t N = PyNumber_AsSsize_t(el_i, PyExc_IndexError); | |
00426 if (PyErr_Occurred()) return -1; | |
00427 assert (N <= n_applies); | |
00428 self->var_owner[i] = N; | |
00429 self->var_has_owner[i] = 1; | |
00430 } | |
00431 self->var_computed_cells[i] = PyList_GetItem(compute_map_list, i); | |
00432 Py_INCREF(self->var_computed_cells[i]); | |
00433 self->var_value_cells[i] = PyList_GetItem(storage_map_list, i); | |
00434 Py_INCREF(self->var_value_cells[i]); | |
00435 } | |
00436 } | |
00437 else | |
00438 { | |
00439 PyErr_SetString(PyExc_TypeError, "var_owner must be list"); | |
00440 return -1; | |
00441 } | |
00442 | |
00443 if (dependencies != Py_None) | |
00444 { | |
00445 self->dependencies = (Py_ssize_t**)calloc(self->n_vars, sizeof(Py_ssize_t *)); | |
00446 self->n_dependencies = (Py_ssize_t*)calloc(self->n_vars, sizeof(Py_ssize_t)); | |
00447 assert(self->dependencies); | |
00448 assert(self->n_dependencies); | |
00449 | |
00450 for (int i = 0; i < self->n_vars; ++i) | |
00451 { | |
00452 PyObject *tmp = PyList_GetItem(dependencies, i); | |
00453 // refcounting - tmp is borrowed | |
00454 if (unpack_list_of_ssize_t(tmp, &self->dependencies[i], &self->n_dependencies[i], | |
00455 "dependencies")) | |
00456 return -1; | |
00457 } | |
00458 } | |
00459 | |
00460 if (unpack_list_of_ssize_t(output_vars, &self->output_vars, &self->n_output_vars, | |
00461 "output_vars")) | |
00462 return -1; | |
00463 for (int i = 0; i < self->n_output_vars; ++i) | |
00464 { | |
00465 assert(self->output_vars[i] < self->n_vars); | |
00466 } | |
00467 if (unpack_list_of_ssize_t(update_storage, &self->update_storage, &self->n_updates, | |
00468 "updates_storage")) | |
00469 return -1; | |
00470 return 0; | |
00471 } | |
00472 static void set_position_of_error(CLazyLinker * self, int owner_idx) | |
00473 { | |
00474 if (self->position_of_error == -1) | |
00475 { | |
00476 self->position_of_error = owner_idx; | |
00477 } | |
00478 } | |
00479 static PyObject * pycall(CLazyLinker * self, Py_ssize_t node_idx, int verbose) | |
00480 { | |
00481 // call thunk to see which inputs it wants | |
00482 PyObject * thunk = PyList_GetItem(self->thunks, node_idx); | |
00483 // refcounting - thunk is borrowed | |
00484 PyObject * rval = NULL; | |
00485 if (self->do_timing) | |
00486 { | |
00487 double t0 = pytime(NULL); | |
00488 if (verbose) fprintf(stderr, "calling via Python (node %i)\n", (int)node_idx); | |
00489 rval = PyObject_CallObject(thunk, NULL); | |
00490 if (rval) | |
00491 { | |
00492 double t1 = pytime(NULL); | |
00493 double ti = PyFloat_AsDouble( | |
00494 PyList_GetItem(self->call_times, node_idx)); | |
00495 PyList_SetItem(self->call_times, node_idx, | |
00496 PyFloat_FromDouble(t1 - t0 + ti)); | |
00497 PyObject * count = PyList_GetItem(self->call_counts, node_idx); | |
00498 long icount = PyInt_AsLong(count); | |
00499 PyList_SetItem(self->call_counts, node_idx, | |
00500 PyInt_FromLong(icount + 1)); | |
00501 } | |
00502 } | |
00503 else | |
00504 { | |
00505 if (verbose) | |
00506 { | |
00507 fprintf(stderr, "calling via Python (node %i)\n", (int)node_idx); | |
00508 } | |
00509 rval = PyObject_CallObject(thunk, NULL); | |
00510 } | |
00511 return rval; | |
00512 } | |
00513 static int c_call(CLazyLinker * self, Py_ssize_t node_idx, int verbose) | |
00514 { | |
00515 void * ptr_addr = self->thunk_cptr_fn[node_idx]; | |
00516 int (*fn)(void*) = (int (*)(void*))(ptr_addr); | |
00517 if (verbose) fprintf(stderr, "calling non-lazy shortcut (node %i)\n", (int)node_idx); | |
00518 int err = 0; | |
00519 if (self->do_timing) | |
00520 { | |
00521 double t0 = pytime(NULL); | |
00522 err = fn(self->thunk_cptr_data[node_idx]); | |
00523 double t1 = pytime(NULL); | |
00524 double ti = PyFloat_AsDouble(PyList_GetItem(self->call_times, node_idx)); | |
00525 PyList_SetItem(self->call_times, node_idx, PyFloat_FromDouble(t1 - t0 + ti)); | |
00526 PyObject * count = PyList_GetItem(self->call_counts, node_idx); | |
00527 long icount = PyInt_AsLong(count); | |
00528 PyList_SetItem(self->call_counts, node_idx, PyInt_FromLong(icount+1)); | |
00529 } | |
00530 else | |
00531 { | |
00532 err = fn(self->thunk_cptr_data[node_idx]); | |
00533 } | |
00534 | |
00535 if (err) | |
00536 { | |
00537 // cast the argument to a PyList (as described near line 226 of cc.py) | |
00538 PyObject * __ERROR = ((PyObject**)self->thunk_cptr_data[node_idx])[0]; | |
00539 assert (PyList_Check(__ERROR)); | |
00540 assert (PyList_Size(__ERROR) == 3); | |
00541 PyObject * err_type = PyList_GetItem(__ERROR, 0); //stolen ref | |
00542 PyObject * err_msg = PyList_GetItem(__ERROR, 1); //stolen ref | |
00543 PyObject * err_trace = PyList_GetItem(__ERROR, 2); //stolen ref | |
00544 PyList_SET_ITEM(__ERROR, 0, Py_None); Py_INCREF(Py_None); //clobbers old ref | |
00545 PyList_SET_ITEM(__ERROR, 1, Py_None); Py_INCREF(Py_None); //clobbers old ref | |
00546 PyList_SET_ITEM(__ERROR, 2, Py_None); Py_INCREF(Py_None); //clobbers old ref | |
00547 | |
00548 assert(!PyErr_Occurred()); // because CLinker hid the exception in __ERROR aka data | |
00549 PyErr_Restore(err_type, err_msg, err_trace); //steals refs to args | |
00550 } | |
00551 if (err) set_position_of_error(self, node_idx); | |
00552 return err; | |
00553 } | |
00554 static | |
00555 int lazy_rec_eval(CLazyLinker * self, Py_ssize_t var_idx, PyObject*one, PyObject*zero) | |
00556 { | |
00557 PyObject *rval = NULL; | |
00558 int verbose = 0; | |
00559 int err = 0; | |
00560 | |
00561 if (verbose) fprintf(stderr, "lazy_rec computing %i\n", (int)var_idx); | |
00562 | |
00563 if (self->var_computed[var_idx] || !self->var_has_owner[var_idx]) | |
00564 return 0; | |
00565 | |
00566 Py_ssize_t owner_idx = self->var_owner[var_idx]; | |
00567 | |
00568 // STEP 1: compute the pre-requirements of the node | |
00569 // Includes input nodes for non-lazy ops. | |
00570 for (int i = 0; i < self->node_n_prereqs[owner_idx]; ++i) | |
00571 { | |
00572 Py_ssize_t prereq_idx = self->node_prereqs[owner_idx][i]; | |
00573 if (!self->var_computed[prereq_idx]) | |
00574 { | |
00575 err = lazy_rec_eval(self, prereq_idx, one, zero); | |
00576 if (err) return err; | |
00577 } | |
00578 assert (self->var_computed[prereq_idx]); | |
00579 } | |
00580 | |
00581 // STEP 2: compute the node itself | |
00582 if (self->is_lazy[owner_idx]) | |
00583 { | |
00584 // update the compute_map cells corresponding to the inputs of this thunk | |
00585 for (int i = 0; i < self->node_n_inputs[owner_idx]; ++i) | |
00586 { | |
00587 int in_idx = self->node_inputs[owner_idx][i]; | |
00588 if (self->var_computed[in_idx]) | |
00589 { | |
00590 Py_INCREF(one); | |
00591 err = PyList_SetItem(self->var_computed_cells[in_idx], 0, one); | |
00592 } | |
00593 else | |
00594 { | |
00595 Py_INCREF(zero); | |
00596 err = PyList_SetItem(self->var_computed_cells[in_idx], 0, zero); | |
00597 } | |
00598 if (err) goto fail; | |
00599 } | |
00600 | |
00601 rval = pycall(self, owner_idx, verbose); | |
00602 // refcounting - rval is new ref | |
00603 //TODO: to prevent infinite loops | |
00604 // - consider check that a thunk does not ask for an input that is already computed | |
00605 if (rval == NULL) | |
00606 { | |
00607 assert (PyErr_Occurred()); | |
00608 err = 1; | |
00609 goto fail; | |
00610 } | |
00611 | |
00612 //update the computed-ness of any output cells | |
00613 for (int i = 0; i < self->node_n_outputs[owner_idx]; ++i) | |
00614 { | |
00615 int out_idx = self->node_outputs[owner_idx][i]; | |
00616 PyObject * el_i = PyList_GetItem(self->var_computed_cells[out_idx], 0); | |
00617 Py_ssize_t N = PyNumber_AsSsize_t(el_i, PyExc_IndexError); | |
00618 if (PyErr_Occurred()) | |
00619 { | |
00620 err = -1; | |
00621 goto pyfail; | |
00622 } | |
00623 assert (N==0 || N==1); | |
00624 self->var_computed[out_idx] = N; | |
00625 } | |
00626 if (!self->var_computed[var_idx]) | |
00627 { | |
00628 /* | |
00629 * If self is not computed after the call, this means that some | |
00630 * inputs are needed. Compute the ones on the returned list | |
00631 * and try to compute the current node again (with recursive call). | |
00632 * This allows a node to request more nodes more than once before | |
00633 * finally yielding a result. | |
00634 */ | |
00635 if (!PyList_Check(rval)) | |
00636 { | |
00637 //TODO: More helpful error to help find *which node* made this | |
00638 // bad thunk | |
00639 PyErr_SetString(PyExc_TypeError, | |
00640 "lazy thunk should return a list"); | |
00641 err = 1; | |
00642 goto pyfail; | |
00643 } | |
00644 | |
00645 if (!PyList_Size(rval)) | |
00646 { | |
00647 PyErr_SetString(PyExc_ValueError, | |
00648 "lazy thunk returned empty list without computing output"); | |
00649 err = 1; | |
00650 goto pyfail; | |
00651 } | |
00652 | |
00653 for (int i = 0; i < PyList_Size(rval); ++i) | |
00654 { | |
00655 PyObject * el_i = PyList_GetItem(rval, i); | |
00656 Py_ssize_t N = PyNumber_AsSsize_t(el_i, PyExc_IndexError); | |
00657 if (PyErr_Occurred()) | |
00658 { | |
00659 err = 1; | |
00660 goto pyfail; | |
00661 } | |
00662 assert (N <= self->node_n_inputs[owner_idx]); | |
00663 Py_ssize_t input_idx = self->node_inputs[owner_idx][N]; | |
00664 err = lazy_rec_eval(self, input_idx, one, zero); | |
00665 if (err) goto pyfail; | |
00666 } | |
00667 | |
00668 Py_DECREF(rval); | |
00669 /* | |
00670 * We intentionally skip all the end-of-function processing | |
00671 * (mark outputs, GC) as it will be performed by the call | |
00672 * that actually manages to compute the result. | |
00673 */ | |
00674 return lazy_rec_eval(self, var_idx, one, zero); | |
00675 } | |
00676 | |
00677 Py_DECREF(rval); | |
00678 } | |
00679 else //owner is not a lazy op. Ensure all intputs are evaluated. | |
00680 { | |
00681 // loop over inputs to owner | |
00682 // call lazy_rec_eval on each one that is not computed. | |
00683 // if there's an error, pass it up the stack | |
00684 for (int i = 0; i < self->node_n_inputs[owner_idx]; ++i) | |
00685 { | |
00686 Py_ssize_t input_idx = self->node_inputs[owner_idx][i]; | |
00687 if (!self->var_computed[input_idx]) | |
00688 { | |
00689 err = lazy_rec_eval(self, input_idx, one, zero); | |
00690 if (err) return err; | |
00691 } | |
00692 assert (self->var_computed[input_idx]); | |
00693 } | |
00694 | |
00695 // call the thunk for this owner. | |
00696 if (self->thunk_cptr_fn[owner_idx]) | |
00697 { | |
00698 err = c_call(self, owner_idx, verbose); | |
00699 if (err) goto fail; | |
00700 } | |
00701 else | |
00702 { | |
00703 rval = pycall(self, owner_idx, verbose); | |
00704 //rval is new ref | |
00705 if (rval) //pycall returned normally (no exception) | |
00706 { | |
00707 if (rval == Py_None) | |
00708 { | |
00709 Py_DECREF(rval); //ignore a return of None | |
00710 } | |
00711 else if (PyList_Check(rval)) | |
00712 { | |
00713 PyErr_SetString(PyExc_TypeError, | |
00714 "non-lazy thunk should return None, not list"); | |
00715 err = 1; | |
00716 goto pyfail; | |
00717 } | |
00718 else // don't know what it returned, but it wasn't right. | |
00719 { | |
00720 PyErr_SetObject(PyExc_TypeError, rval); | |
00721 err = 1; | |
00722 // We don't release rval since we put it in the error above | |
00723 goto fail; | |
00724 } | |
00725 } | |
00726 else // pycall returned NULL (internal error) | |
00727 { | |
00728 err = 1; | |
00729 goto fail; | |
00730 } | |
00731 } | |
00732 } | |
00733 | |
00734 // loop over all outputs and mark them as computed | |
00735 for (int i = 0; i < self->node_n_outputs[owner_idx]; ++i) | |
00736 { | |
00737 self->var_computed[self->node_outputs[owner_idx][i]] = 1; | |
00738 } | |
00739 | |
00740 // Free vars that are not needed anymore | |
00741 if (self->allow_gc) | |
00742 { | |
00743 for (int i = 0; i < self->node_n_inputs[owner_idx]; ++i) | |
00744 { | |
00745 int cleanup = 1; | |
00746 Py_ssize_t i_idx = self->node_inputs[owner_idx][i]; | |
00747 if (!self->var_has_owner[i_idx]) | |
00748 continue; | |
00749 | |
00750 for (int j = 0; j < self->n_output_vars; ++j) | |
00751 { | |
00752 if (i_idx == self->output_vars[j]) | |
00753 { | |
00754 cleanup = 0; | |
00755 break; | |
00756 } | |
00757 } | |
00758 if (!cleanup) continue; | |
00759 | |
00760 for (int j = 0; j < self->n_dependencies[i_idx]; ++j) | |
00761 { | |
00762 if (!self->var_computed[self->dependencies[i_idx][j]]) | |
00763 { | |
00764 cleanup = 0; | |
00765 break; | |
00766 } | |
00767 } | |
00768 if (!cleanup) continue; | |
00769 | |
00770 Py_INCREF(Py_None); | |
00771 err = PyList_SetItem(self->var_value_cells[i_idx], 0, Py_None); | |
00772 //See the Stack gc implementation for why we change it to 2 and not 0. | |
00773 self->var_computed[i_idx] = 2; | |
00774 if (err) goto fail; | |
00775 } | |
00776 } | |
00777 | |
00778 return 0; | |
00779 pyfail: | |
00780 Py_DECREF(rval); | |
00781 fail: | |
00782 set_position_of_error(self, owner_idx); | |
00783 return err; | |
00784 } | |
00785 PyObject * | |
00786 CLazyLinker_call(PyObject *_self, PyObject *args, PyObject *kwds) | |
00787 { | |
00788 CLazyLinker * self = (CLazyLinker*)_self; | |
00789 static char *kwlist[] = { | |
00790 (char*)"time_thunks", | |
00791 (char *)"n_calls", | |
00792 NULL}; | |
00793 int n_calls=1; | |
00794 if (! PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwlist, | |
00795 &self->do_timing, | |
00796 &n_calls)) | |
00797 return NULL; | |
00798 int err = 0; | |
00799 self->position_of_error = -1; | |
00800 // create constants used to fill the var_compute_cells | |
00801 PyObject * one = PyInt_FromLong(1); | |
00802 PyObject * zero = PyInt_FromLong(0); | |
00803 | |
00804 // pre-allocate our return value | |
00805 Py_INCREF(Py_None); | |
00806 PyObject * rval = Py_None; | |
00807 //clear storage of pre_call_clear elements | |
00808 for (int call_i = 0; call_i < n_calls && (!err); ++call_i) | |
00809 { | |
00810 Py_ssize_t n_pre_call_clear = PyList_Size(self->pre_call_clear); | |
00811 assert(PyList_Check(self->pre_call_clear)); | |
00812 for (int i = 0; i < n_pre_call_clear; ++i) | |
00813 { | |
00814 PyObject * el_i = PyList_GetItem(self->pre_call_clear, i); | |
00815 Py_INCREF(Py_None); | |
00816 PyList_SetItem(el_i, 0, Py_None); | |
00817 } | |
00818 //clear the computed flag out of all non-input vars | |
00819 for (int i = 0; i < self->n_vars; ++i) | |
00820 { | |
00821 self->var_computed[i] = !self->var_has_owner[i]; | |
00822 if (self->var_computed[i]) | |
00823 { | |
00824 Py_INCREF(one); | |
00825 PyList_SetItem(self->var_computed_cells[i], 0, one); | |
00826 } | |
00827 else | |
00828 { | |
00829 Py_INCREF(zero); | |
00830 PyList_SetItem(self->var_computed_cells[i], 0, zero); | |
00831 } | |
00832 } | |
00833 | |
00834 for (int i = 0; i < self->n_output_vars && (!err); ++i) | |
00835 { | |
00836 err = lazy_rec_eval(self, self->output_vars[i], one, zero); | |
00837 } | |
00838 | |
00839 if (!err) | |
00840 { | |
00841 // save references to outputs prior to updating storage containers | |
00842 assert (self->n_output_vars >= self->n_updates); | |
00843 Py_DECREF(rval); | |
00844 rval = PyList_New(self->n_output_vars); | |
00845 for (int i = 0; i < (self->n_output_vars); ++i) | |
00846 { | |
00847 Py_ssize_t src = self->output_vars[i]; | |
00848 PyObject * item = PyList_GetItem(self->var_value_cells[src], 0); | |
00849 if (self->var_computed[src] != 1) | |
00850 { | |
00851 err = 1; | |
00852 PyErr_Format(PyExc_AssertionError, | |
00853 "The compute map of output %d should contain " | |
00854 "1 at the end of execution, not %d.", | |
00855 i, self->var_computed[src]); | |
00856 break; | |
00857 } | |
00858 Py_INCREF(item); | |
00859 PyList_SetItem(rval, i, item); | |
00860 } | |
00861 } | |
00862 | |
00863 if (!err) | |
00864 { | |
00865 // Update the inputs that have an update rule | |
00866 for (int i = 0; i < self->n_updates; ++i) | |
00867 { | |
00868 PyObject* tmp = PyList_GetItem(rval, self->n_output_vars - self->n_updates + i); | |
00869 Py_INCREF(tmp); | |
00870 Py_ssize_t dst = self->update_storage[i]; | |
00871 PyList_SetItem(self->var_value_cells[dst], 0, tmp); | |
00872 } | |
00873 } | |
00874 } | |
00875 | |
00876 /* | |
00877 Clear everything that is left and not an output. This is needed | |
00878 for lazy evaluation since the current GC algo is too conservative | |
00879 with lazy graphs. | |
00880 */ | |
00881 if (self->allow_gc && !err) | |
00882 { | |
00883 for (Py_ssize_t i = 0; i < self->n_vars; ++i) | |
00884 { | |
00885 int do_cleanup = 1; | |
00886 if (!self->var_has_owner[i] || !self->var_computed[i]) | |
00887 continue; | |
00888 for (int j = 0; j < self->n_output_vars; ++j) | |
00889 { | |
00890 if (i == self->output_vars[j]) | |
00891 { | |
00892 do_cleanup = 0; | |
00893 break; | |
00894 } | |
00895 } | |
00896 if (!do_cleanup) | |
00897 continue; | |
00898 Py_INCREF(Py_None); | |
00899 PyList_SetItem(self->var_value_cells[i], 0, Py_None); | |
00900 } | |
00901 } | |
00902 Py_DECREF(one); | |
00903 Py_DECREF(zero); | |
00904 if (err) | |
00905 { | |
00906 Py_DECREF(rval); | |
00907 return NULL; | |
00908 } | |
00909 return rval; | |
00910 } | |
00911 | |
00912 #if 0 | |
00913 static PyMethodDef CLazyLinker_methods[] = { | |
00914 { | |
00915 //"name", (PyCFunction)CLazyLinker_accept, METH_VARARGS, "Return the name, combining the first and last name" | |
00916 }, | |
00917 {NULL} /* Sentinel */ | |
00918 }; | |
00919 #endif | |
00920 | |
00921 static PyMemberDef CLazyLinker_members[] = { | |
00922 {(char*)"nodes", T_OBJECT_EX, offsetof(CLazyLinker, nodes), 0, | |
00923 (char*)"list of nodes"}, | |
00924 {(char*)"thunks", T_OBJECT_EX, offsetof(CLazyLinker, thunks), 0, | |
00925 (char*)"list of thunks in program"}, | |
00926 {(char*)"call_counts", T_OBJECT_EX, offsetof(CLazyLinker, call_counts), 0, | |
00927 (char*)"number of calls of each thunk"}, | |
00928 {(char*)"call_times", T_OBJECT_EX, offsetof(CLazyLinker, call_times), 0, | |
00929 (char*)"total runtime in each thunk"}, | |
00930 {(char*)"position_of_error", T_INT, offsetof(CLazyLinker, position_of_error), 0, | |
00931 (char*)"position of failed thunk"}, | |
00932 {(char*)"time_thunks", T_INT, offsetof(CLazyLinker, do_timing), 0, | |
00933 (char*)"bool: nonzero means call will time thunks"}, | |
00934 {(char*)"need_update_inputs", T_INT, offsetof(CLazyLinker, need_update_inputs), 0, | |
00935 (char*)"bool: nonzero means Function.__call__ must implement update mechanism"}, | |
00936 {NULL} /* Sentinel */ | |
00937 }; | |
00938 | |
00939 static PyTypeObject lazylinker_ext_CLazyLinkerType = { | |
00940 PyObject_HEAD_INIT(NULL) | |
00941 0, /*ob_size*/ | |
00942 "lazylinker_ext.CLazyLinker", /*tp_name*/ | |
00943 sizeof(CLazyLinker), /*tp_basicsize*/ | |
00944 0, /*tp_itemsize*/ | |
00945 CLazyLinker_dealloc, /*tp_dealloc*/ | |
00946 0, /*tp_print*/ | |
00947 0, /*tp_getattr*/ | |
00948 0, /*tp_setattr*/ | |
00949 0, /*tp_compare*/ | |
00950 0, /*tp_repr*/ | |
00951 0, /*tp_as_number*/ | |
00952 0, /*tp_as_sequence*/ | |
00953 0, /*tp_as_mapping*/ | |
00954 0, /*tp_hash */ | |
00955 CLazyLinker_call, /*tp_call*/ | |
00956 0, /*tp_str*/ | |
00957 0, /*tp_getattro*/ | |
00958 0, /*tp_setattro*/ | |
00959 0, /*tp_as_buffer*/ | |
00960 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ | |
00961 "CLazyLinker object", /* tp_doc */ | |
00962 0, /* tp_traverse */ | |
00963 0, /* tp_clear */ | |
00964 0, /* tp_richcompare */ | |
00965 0, /* tp_weaklistoffset */ | |
00966 0, /* tp_iter */ | |
00967 0, /* tp_iternext */ | |
00968 0,//CLazyLinker_methods, /* tp_methods */ | |
00969 CLazyLinker_members, /* tp_members */ | |
00970 0, /* tp_getset */ | |
00971 0, /* tp_base */ | |
00972 0, /* tp_dict */ | |
00973 0, /* tp_descr_get */ | |
00974 0, /* tp_descr_set */ | |
00975 0, /* tp_dictoffset */ | |
00976 (initproc)CLazyLinker_init,/* tp_init */ | |
00977 0, /* tp_alloc */ | |
00978 CLazyLinker_new, /* tp_new */ | |
00979 }; | |
00980 | |
00981 static PyObject * get_version(PyObject *dummy, PyObject *args) | |
00982 { | |
00983 PyObject *result = PyFloat_FromDouble(0.20); | |
00984 return result; | |
00985 } | |
00986 | |
00987 static PyMethodDef lazylinker_ext_methods[] = { | |
00988 {"get_version", get_version, METH_VARARGS, "Get extension version."}, | |
00989 {NULL, NULL, 0, NULL} /* Sentinel */ | |
00990 }; | |
00991 | |
00992 #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ | |
00993 #define PyMODINIT_FUNC void | |
00994 #endif | |
00995 PyMODINIT_FUNC | |
00996 initlazylinker_ext(void) | |
00997 { | |
00998 PyObject* m; | |
00999 | |
01000 lazylinker_ext_CLazyLinkerType.tp_new = PyType_GenericNew; | |
01001 if (PyType_Ready(&lazylinker_ext_CLazyLinkerType) < 0) | |
01002 return; | |
01003 | |
01004 m = Py_InitModule3("lazylinker_ext", lazylinker_ext_methods, | |
01005 "Example module that creates an extension type."); | |
01006 | |
01007 Py_INCREF(&lazylinker_ext_CLazyLinkerType); | |
01008 PyModule_AddObject(m, "CLazyLinker", (PyObject *)&lazylinker_ext_CLazyLinkerType); | |
01009 } | |
01010 | |
01011 | |
Problem occurred during compilation with the command line below: | |
g++ -dynamiclib -g -D NPY_ARRAY_ENSURECOPY=NPY_ENSURECOPY -D NPY_ARRAY_ALIGNED=NPY_ALIGNED -D NPY_ARRAY_WRITEABLE=NPY_WRITEABLE -D NPY_ARRAY_UPDATE_ALL=NPY_UPDATE_ALL -D NPY_ARRAY_C_CONTIGUOUS=NPY_C_CONTIGUOUS -D NPY_ARRAY_F_CONTIGUOUS=NPY_F_CONTIGUOUS -fPIC -undefined dynamic_lookup -m64 -I/Users/rkeisler/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include -I/Applications/Canopy.app/appdata/canopy-1.0.0.1160.macosx-x86_64/Canopy.app/Contents/include/python2.7 -o /Users/rkeisler/.theano/compiledir_Darwin-10.8.0-x86_64-i386-64bit-i386-2.7.3/lazylinker_ext/lazylinker_ext.so /Users/rkeisler/.theano/compiledir_Darwin-10.8.0-x86_64-i386-64bit-i386-2.7.3/lazylinker_ext/mod.cpp -L/Applications/Canopy.app/appdata/canopy-1.0.0.1160.macosx-x86_64/Canopy.app/Contents/lib -lpython2.7 | |
=============================== | |
=============================== | |
ld: library not found for -lpython2.7 | |
collect2: ld returned 1 exit status | |
Traceback (most recent call last): | |
File "<string>", line 1, in <module> | |
File "/Users/rkeisler/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/theano/__init__.py", line 61, in <module> | |
import compile | |
File "/Users/rkeisler/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/theano/compile/__init__.py", line 6, in <module> | |
import function_module | |
File "/Users/rkeisler/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/theano/compile/function_module.py", line 18, in <module> | |
import mode as mode_module | |
File "/Users/rkeisler/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/theano/compile/mode.py", line 11, in <module> | |
import theano.gof.vm | |
File "/Users/rkeisler/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/theano/gof/vm.py", line 487, in <module> | |
import lazylinker_c | |
File "/Users/rkeisler/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/theano/gof/lazylinker_c.py", line 88, in <module> | |
preargs=args) | |
File "/Users/rkeisler/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/theano/gof/cmodule.py", line 1633, in compile_str | |
(status, compile_stderr.replace('\n', '. '))) | |
Exception: Compilation failed (return status=1): ld: library not found for -lpython2.7. collect2: ld returned 1 exit status. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment