4 #include "flame/base.h"
8 #define TRY PyMachine *machine = reinterpret_cast<PyMachine*>(raw); try
20 int PyMachine_init(PyObject *raw, PyObject *args, PyObject *kws)
23 assert(!machine->weak);
25 std::unique_ptr<Config> C(PyGLPSParse2Config(raw, args, kws));
27 machine->machine =
new Machine(*C);
30 } CATCH3(key_error, KeyError, -1)
31 CATCH3(std::invalid_argument, ValueError, -1)
32 CATCH3(std::exception, RuntimeError, -1)
36 void PyMachine_free(PyObject *raw)
39 std::unique_ptr<Machine> S(machine->machine);
40 machine->machine = NULL;
43 PyObject_ClearWeakRefs(raw);
45 Py_TYPE(raw)->tp_free(raw);
46 } CATCH2V(std::exception, RuntimeError)
50 PyObject *PyMachine_str(PyObject *raw)
53 std::ostringstream strm;
54 strm << *(machine->machine);
55 return PyString_FromString(strm.str().c_str());
60 PyObject *PyMachine_conf(PyObject *raw, PyObject *args, PyObject *kws)
63 PyObject *pyindex = Py_None;
64 const char *pnames[] = {
"index", NULL};
65 if(!PyArg_ParseTupleAndKeywords(args, kws,
"|O", (
char**)pnames, &pyindex))
69 if(pyindex==Py_None) {
70 C = machine->machine->conf();
71 }
else if(PyNumber_Check(pyindex)) {
72 PyRef<> pylong(PyNumber_Long(pyindex));
73 long index = PyLong_AsLong(pylong.py());
74 if(index<0 || (
unsigned long)index>=machine->machine->size())
75 return PyErr_Format(PyExc_IndexError,
"Element index out of range");
76 C = (*machine->machine)[index]->conf();
78 return PyErr_Format(PyExc_ValueError,
"'index' must be an integer or None");
87 PyObject *PyMachine_allocState(PyObject *raw, PyObject *args, PyObject *kws)
90 PyObject *d = Py_None, *W = Py_False;
91 const char *pnames[] = {
"config",
"inherit", NULL};
92 if(!PyArg_ParseTupleAndKeywords(args, kws,
"|OO", (
char**)pnames, &d, &W))
97 C = machine->machine->conf();
98 }
else if(PyDict_Check(d)) {
99 if(PyObject_IsTrue(W)) {
100 C = machine->machine->conf();
103 PyRef<> list(PyMapping_Items(d));
104 List2Config(C, list.py());
106 return PyErr_Format(PyExc_ValueError,
"allocState() needs config=None or {}");
108 std::unique_ptr<StateBase> state(machine->machine->allocState(C));
109 PyObject *ret = wrapstate(state.get());
115 struct PyStoreObserver :
public Observer
121 virtual ~PyStoreObserver() {}
124 PyRef<> tuple(PyTuple_New(2));
125 std::unique_ptr<StateBase> tmpstate(state->
clone());
126 PyRef<> statecopy(wrapstate(tmpstate.get()));
129 PyTuple_SET_ITEM(tuple.py(), 0, PyInt_FromSize_t(elem->
index));
130 PyTuple_SET_ITEM(tuple.py(), 1, statecopy.release());
131 if(PyList_Append(list.py(), tuple.py()))
132 throw std::runtime_error(
"");
136 struct PyScopedObserver
139 std::vector<size_t> observed;
140 PyScopedObserver(
Machine *m) : machine(m) {}
141 ~PyScopedObserver() {
142 for(
size_t i=0; i<machine->size(); i++) {
143 (*machine)[i]->set_observer(NULL);
148 if(i>=machine->size())
149 throw std::runtime_error(
"element index out of range");
150 observed.push_back(i);
151 (*machine)[i]->set_observer(o);
156 PyObject *PyMachine_propagate(PyObject *raw, PyObject *args, PyObject *kws)
160 PyObject *state, *toobserv = Py_None, *pymax = Py_None;
161 unsigned long start = 0;
163 const char *pnames[] = {
"state",
"start",
"max",
"observe", NULL};
164 if(!PyArg_ParseTupleAndKeywords(args, kws,
"O|kOO", (
char**)pnames, &state, &start, &pymax, &toobserv))
167 if (pymax!=Py_None) max = (int) PyLong_AsLong(pymax);
169 PyStoreObserver observer;
170 PyScopedObserver observing(machine->machine);
172 if(toobserv!=Py_None) {
173 PyRef<> iter(PyObject_GetIter(toobserv)), item;
175 while(item.reset(PyIter_Next(iter.py()), PyRef<>::allow_null())) {
176 Py_ssize_t num = PyNumber_AsSsize_t(item.py(), PyExc_ValueError);
178 throw std::runtime_error(
"");
179 observing.observe(num, &observer);
184 machine->machine->propagate(unwrapstate(state), start, max);
186 return observer.list.release();
190 } CATCH2(std::invalid_argument, ValueError)
195 PyObject *PyMachine_reconfigure(PyObject *raw, PyObject *args, PyObject *kws)
199 PyObject *conf, *replace = Py_False;
200 const char *pnames[] = {
"index",
"config",
"replace", NULL};
201 if(!PyArg_ParseTupleAndKeywords(args, kws,
"kO!|O", (
char**)pnames, &idx, &PyDict_Type, &conf, &replace))
204 if(idx>=machine->machine->size())
205 return PyErr_Format(PyExc_ValueError,
"invalid element index %lu", idx);
208 if(!PyObject_IsTrue(replace))
209 newconf = (*machine->machine)[idx]->conf();
211 PyRef<> list(PyMapping_Items(conf));
212 List2Config(newconf, list.py(), 3);
214 machine->machine->reconfigure(idx, newconf);
217 } CATCH2(std::invalid_argument, ValueError)
222 PyObject *PyMachine_find(PyObject *raw, PyObject *args, PyObject *kws)
225 const char *ename = NULL, *etype = NULL;
226 const char *pnames[] = {
"name",
"type", NULL};
227 if(!PyArg_ParseTupleAndKeywords(args, kws,
"|zz", (
char**)pnames, &ename, &etype))
230 PyRef<> ret(PyList_New(0));
232 std::pair<Machine::lookup_iterator, Machine::lookup_iterator> range;
235 return PyErr_Format(PyExc_ValueError,
"only one of 'name' or 'type' may be given");
237 range = machine->machine->equal_range(ename);
239 range = machine->machine->equal_range_type(etype);
241 range = machine->machine->all_range();
244 for(; range.first!=range.second; ++range.first) {
247 PyRef<> pyidx(PyInt_FromLong(elem->
index));
249 if(PyList_Append(ret.py(), pyidx.py()))
253 return ret.release();
258 Py_ssize_t PyMachine_len(PyObject *raw)
261 return machine->machine->size();
265 static PyMethodDef PyMachine_methods[] = {
266 {
"conf", (PyCFunction)&PyMachine_conf, METH_VARARGS|METH_KEYWORDS,
267 "conf() -> {} Machine config\n"
268 "conf(index) -> {} Element config"},
269 {
"allocState", (PyCFunction)&PyMachine_allocState, METH_VARARGS|METH_KEYWORDS,
270 "allocState() -> State\n"
271 "allocState({'variable':int|str}) -> State\n"
272 "Allocate a new State based on this Machine's configuration."
273 " Optionally provide additional configuration"},
274 {
"propagate", (PyCFunction)&PyMachine_propagate, METH_VARARGS|METH_KEYWORDS,
275 "propagate(State, start=0, max=INT_MAX, observe=None)\n"
276 "propagate(State, start=0, max=INT_MAX, observe=[1,4,...]) -> [(index,State), ...]\n"
277 "Propagate the provided State through the simulation.\n"
279 "start and max selects through which element the State will be passed.\n"
281 "observe may be None or an iterable yielding element indicies.\n"
282 "In the second form propagate() returns a list of tuples with the output State of the selected elements."
284 {
"reconfigure", (PyCFunction)&PyMachine_reconfigure, METH_VARARGS|METH_KEYWORDS,
285 "reconfigure(index, {'variable':int|str})\n"
286 "Change the configuration of an element."},
287 {
"find", (PyCFunction)&PyMachine_find, METH_VARARGS|METH_KEYWORDS,
288 "find(name=None, type=None) -> [int]\n"
289 "Return a list of element indices for element name or type matching the given string."},
290 {NULL, NULL, 0, NULL}
293 static PySequenceMethods PyMachine_seq = {
297 static PyTypeObject PyMachineType = {
298 #if PY_MAJOR_VERSION >= 3
299 PyVarObject_HEAD_INIT(NULL, 0)
301 PyObject_HEAD_INIT(NULL)
304 "flame._internal.Machine",
310 static const char pymdoc[] =
311 "Machine(config, path=None, extra=None)\n"
312 "Machine(config, path='/directry/', extra={'variable':float|str}})\n"
314 "A Machine() the primary interface to the FLAME simulation engine.\n"
316 "The 'config' argument may be a file-like object (with read())"
317 " or a buffer which will be parsed with the GLPS parser (see GLPSParser::parse).\n"
318 " Or it may be a dictionary.\n"
320 ">>> with open('some.lat', 'rb') as F:\n"
325 int registerModMachine(PyObject *mod)
327 PyMachineType.tp_doc = pymdoc;
328 PyMachineType.tp_str = &PyMachine_str;
330 PyMachineType.tp_new = &PyType_GenericNew;
331 PyMachineType.tp_init = &PyMachine_init;
332 PyMachineType.tp_dealloc = &PyMachine_free;
334 PyMachineType.tp_weaklistoffset = offsetof(PyMachine, weak);
336 PyMachineType.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE;
337 PyMachineType.tp_methods = PyMachine_methods;
338 PyMachineType.tp_as_sequence = &PyMachine_seq;
340 if(PyType_Ready(&PyMachineType))
343 Py_INCREF((PyObject*)&PyMachineType);
344 if(PyModule_AddObject(mod,
"Machine", (PyObject*)&PyMachineType)) {
345 Py_DECREF((PyObject*)&PyMachineType);
Base class for all simulated elements.
virtual void view(const ElementVoid *elem, const StateBase *state)=0
Called from within Machine::propagate()
The core simulate Machine engine.
virtual StateBase * clone() const =0
The abstract base class for all simulation state objects.
Associative configuration container.
size_t index
Index of this element (unique in its Machine)
Allow inspection of intermediate State.