compiled-knowledge 4.0.0a17__cp312-cp312-win_amd64.whl → 4.0.0a19__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of compiled-knowledge might be problematic. Click here for more details.

Files changed (27) hide show
  1. ck/circuit/__init__.py +4 -0
  2. ck/circuit/_circuit_cy.cp312-win_amd64.pyd +0 -0
  3. ck/circuit/_circuit_cy.pxd +32 -0
  4. ck/circuit/_circuit_cy.pyx +157 -182
  5. ck/circuit/_circuit_py.py +2 -2
  6. ck/circuit_compiler/cython_vm_compiler/_compiler.cp312-win_amd64.pyd +0 -0
  7. ck/circuit_compiler/cython_vm_compiler/_compiler.pyx +193 -79
  8. ck/circuit_compiler/cython_vm_compiler/cython_vm_compiler.py +29 -4
  9. ck/circuit_compiler/support/circuit_analyser/__init__.py +13 -0
  10. ck/circuit_compiler/support/circuit_analyser/_circuit_analyser_cy.cp312-win_amd64.pyd +0 -0
  11. ck/circuit_compiler/support/circuit_analyser/_circuit_analyser_cy.pyx +98 -0
  12. ck/circuit_compiler/support/{circuit_analyser.py → circuit_analyser/_circuit_analyser_py.py} +14 -2
  13. ck/pgm_compiler/ace/__init__.py +1 -1
  14. ck/pgm_compiler/support/circuit_table/__init__.py +8 -0
  15. ck/pgm_compiler/support/circuit_table/_circuit_table_cy.cp312-win_amd64.pyd +0 -0
  16. ck/pgm_compiler/support/circuit_table/_circuit_table_cy.pyx +44 -37
  17. ck/pgm_compiler/support/circuit_table/_circuit_table_py.py +76 -41
  18. ck/pgm_compiler/support/named_compiler_maker.py +12 -2
  19. ck/utils/iter_extras.py +8 -1
  20. ck_demos/ace/demo_ace.py +5 -0
  21. ck_demos/utils/compare.py +5 -1
  22. {compiled_knowledge-4.0.0a17.dist-info → compiled_knowledge-4.0.0a19.dist-info}/METADATA +1 -1
  23. {compiled_knowledge-4.0.0a17.dist-info → compiled_knowledge-4.0.0a19.dist-info}/RECORD +26 -23
  24. {compiled_knowledge-4.0.0a17.dist-info → compiled_knowledge-4.0.0a19.dist-info}/WHEEL +1 -1
  25. ck/circuit_compiler/cython_vm_compiler/_compiler.c +0 -16946
  26. {compiled_knowledge-4.0.0a17.dist-info → compiled_knowledge-4.0.0a19.dist-info}/licenses/LICENSE.txt +0 -0
  27. {compiled_knowledge-4.0.0a17.dist-info → compiled_knowledge-4.0.0a19.dist-info}/top_level.txt +0 -0
@@ -1,20 +1,20 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Dict, Tuple, Sequence
3
+ from typing import Dict, Tuple
4
4
 
5
5
  import numpy as np
6
6
  import ctypes as ct
7
7
 
8
8
  from ck import circuit
9
- from ck.circuit import OpNode, VarNode, CircuitNode
10
- from ck.circuit_compiler.support.circuit_analyser import CircuitAnalysis, analyze_circuit
9
+ from ck.circuit import OpNode
10
+ from ck.circuit_compiler.support.circuit_analyser import CircuitAnalysis
11
11
  from ck.program.raw_program import RawProgramFunction
12
12
  from ck.utils.np_extras import NDArrayNumeric, DTypeNumeric
13
13
 
14
14
  from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
15
+ from cython.operator cimport dereference as deref, preincrement as incr, postdecrement
15
16
 
16
17
  cimport numpy as cnp
17
- cimport cython
18
18
 
19
19
  cnp.import_array()
20
20
 
@@ -22,20 +22,34 @@ DTYPE_FLOAT64 = np.float64
22
22
  ctypedef cnp.float64_t DTYPE_FLOAT64_t
23
23
 
24
24
 
25
+ ctypedef fused cvm_type:
26
+ double
27
+ float
28
+ int
29
+ long
30
+ short
25
31
 
26
- def make_function(
27
- var_nodes: Sequence[VarNode],
28
- result_nodes: Sequence[CircuitNode],
29
- dtype: DTypeNumeric,
30
- ) -> Tuple[RawProgramFunction, int]:
32
+ DTYPE_TO_CVM_TYPE: Dict[DTypeNumeric, str] = {
33
+ np.float64: 'double',
34
+ np.float32: 'float',
35
+ np.intc: 'int',
36
+ np.long: 'long',
37
+ np.short: 'short',
38
+ }
39
+
40
+
41
+ def make_function(analysis: CircuitAnalysis, dtype: DTypeNumeric) -> Tuple[RawProgramFunction, int]:
31
42
  """
32
43
  Make a RawProgram function that interprets the circuit.
33
44
 
45
+ Args:
46
+ analysis: A circuit analysis object defining the function.
47
+ dtype: a numpy data type that must be a key in the dictionary, DTYPE_TO_CVM_TYPE.
48
+
34
49
  Returns:
35
50
  (function, number_of_tmps)
36
51
  """
37
52
 
38
- analysis: CircuitAnalysis = analyze_circuit(var_nodes, result_nodes)
39
53
  cdef Instructions instructions
40
54
  np_consts: NDArrayNumeric
41
55
  instructions, np_consts = _make_instructions_from_analysis(analysis, dtype)
@@ -43,23 +57,79 @@ def make_function(
43
57
  ptr_type = ct.POINTER(np.ctypeslib.as_ctypes_type(dtype))
44
58
  c_np_consts = np_consts.ctypes.data_as(ptr_type)
45
59
 
60
+ cvm_type_name: str = DTYPE_TO_CVM_TYPE[dtype]
61
+
46
62
  # RawProgramFunction = Callable[[ct.POINTER, ct.POINTER, ct.POINTER], None]
47
- def function(vars_in: ct.POINTER, tmps: ct.POINTER, result: ct.POINTER) -> None:
48
- cdef size_t vars_in_addr = ct.cast(vars_in, ct.c_void_p).value
49
- cdef size_t tmps_addr = ct.cast(tmps, ct.c_void_p).value
50
- cdef size_t consts_addr = ct.cast(c_np_consts, ct.c_void_p).value
51
- cdef size_t result_addr = ct.cast(result, ct.c_void_p).value
52
- cvm_float64(
53
- <double*> vars_in_addr,
54
- <double*> tmps_addr,
55
- <double*> consts_addr,
56
- <double*> result_addr,
57
- instructions,
58
- )
63
+ if cvm_type_name == 'double':
64
+ def function(vars_in: ct.POINTER, tmps: ct.POINTER, result: ct.POINTER) -> None:
65
+ cdef size_t vars_in_addr = ct.cast(vars_in, ct.c_void_p).value
66
+ cdef size_t tmps_addr = ct.cast(tmps, ct.c_void_p).value
67
+ cdef size_t consts_addr = ct.cast(c_np_consts, ct.c_void_p).value
68
+ cdef size_t result_addr = ct.cast(result, ct.c_void_p).value
69
+ cvm[double](
70
+ <double *> vars_in_addr,
71
+ <double *> tmps_addr,
72
+ <double *> consts_addr,
73
+ <double *> result_addr,
74
+ instructions,
75
+ )
76
+ elif cvm_type_name == 'float':
77
+ def function(vars_in: ct.POINTER, tmps: ct.POINTER, result: ct.POINTER) -> None:
78
+ cdef size_t vars_in_addr = ct.cast(vars_in, ct.c_void_p).value
79
+ cdef size_t tmps_addr = ct.cast(tmps, ct.c_void_p).value
80
+ cdef size_t consts_addr = ct.cast(c_np_consts, ct.c_void_p).value
81
+ cdef size_t result_addr = ct.cast(result, ct.c_void_p).value
82
+ cvm[float](
83
+ <float *> vars_in_addr,
84
+ <float *> tmps_addr,
85
+ <float *> consts_addr,
86
+ <float *> result_addr,
87
+ instructions,
88
+ )
89
+ elif cvm_type_name == 'int':
90
+ def function(vars_in: ct.POINTER, tmps: ct.POINTER, result: ct.POINTER) -> None:
91
+ cdef size_t vars_in_addr = ct.cast(vars_in, ct.c_void_p).value
92
+ cdef size_t tmps_addr = ct.cast(tmps, ct.c_void_p).value
93
+ cdef size_t consts_addr = ct.cast(c_np_consts, ct.c_void_p).value
94
+ cdef size_t result_addr = ct.cast(result, ct.c_void_p).value
95
+ cvm[int](
96
+ <int *> vars_in_addr,
97
+ <int *> tmps_addr,
98
+ <int *> consts_addr,
99
+ <int *> result_addr,
100
+ instructions,
101
+ )
102
+ elif cvm_type_name == 'long':
103
+ def function(vars_in: ct.POINTER, tmps: ct.POINTER, result: ct.POINTER) -> None:
104
+ cdef size_t vars_in_addr = ct.cast(vars_in, ct.c_void_p).value
105
+ cdef size_t tmps_addr = ct.cast(tmps, ct.c_void_p).value
106
+ cdef size_t consts_addr = ct.cast(c_np_consts, ct.c_void_p).value
107
+ cdef size_t result_addr = ct.cast(result, ct.c_void_p).value
108
+ cvm[long](
109
+ <long *> vars_in_addr,
110
+ <long *> tmps_addr,
111
+ <long *> consts_addr,
112
+ <long *> result_addr,
113
+ instructions,
114
+ )
115
+ elif cvm_type_name == 'short':
116
+ def function(vars_in: ct.POINTER, tmps: ct.POINTER, result: ct.POINTER) -> None:
117
+ cdef size_t vars_in_addr = ct.cast(vars_in, ct.c_void_p).value
118
+ cdef size_t tmps_addr = ct.cast(tmps, ct.c_void_p).value
119
+ cdef size_t consts_addr = ct.cast(c_np_consts, ct.c_void_p).value
120
+ cdef size_t result_addr = ct.cast(result, ct.c_void_p).value
121
+ cvm[short](
122
+ <short *> vars_in_addr,
123
+ <short *> tmps_addr,
124
+ <short *> consts_addr,
125
+ <short *> result_addr,
126
+ instructions,
127
+ )
128
+ else:
129
+ raise ValueError(f'cvm_type_name unexpected: {cvm_type_name!r}')
59
130
 
60
131
  return function, len(analysis.op_to_tmp)
61
132
 
62
-
63
133
  # VM instructions
64
134
  cdef int ADD = circuit.ADD
65
135
  cdef int MUL = circuit.MUL
@@ -71,14 +141,10 @@ cdef int TMPS = 1
71
141
  cdef int CONSTS = 2
72
142
  cdef int RESULT = 3
73
143
 
74
-
75
144
  cdef tuple[Instructions, cnp.ndarray] _make_instructions_from_analysis(
76
145
  object analysis: CircuitAnalysis,
77
146
  object dtype: DTypeNumeric,
78
- ): # -> Tuple[Instructions, NDArrayNumeric]:
79
- if dtype != np.float64:
80
- raise RuntimeError(f'only DType {np.float64} currently supported')
81
-
147
+ ):
82
148
  # Store const values in a numpy array
83
149
  node_to_const_idx: Dict[int, int] = {
84
150
  id(node): i
@@ -89,7 +155,7 @@ cdef tuple[Instructions, cnp.ndarray] _make_instructions_from_analysis(
89
155
  np_consts[i] = node.value
90
156
 
91
157
  # Where to get input values for each possible node.
92
- cdef dict[int, ElementID] node_to_element = {}
158
+ cdef dict[int, ElementID] node_to_element = {}
93
159
  # const nodes
94
160
  for node_id, const_idx in node_to_const_idx.items():
95
161
  node_to_element[node_id] = ElementID(CONSTS, const_idx)
@@ -121,19 +187,16 @@ cdef tuple[Instructions, cnp.ndarray] _make_instructions_from_analysis(
121
187
 
122
188
  return instructions, np_consts
123
189
 
124
-
125
190
  cdef struct ElementID:
126
191
  int array # VARS, TMPS, CONSTS, RESULT
127
192
  int index # index into the array
128
193
 
129
-
130
194
  cdef struct Instruction:
131
195
  int symbol # ADD, MUL, COPY
132
- int num_args
196
+ Py_ssize_t num_args
133
197
  ElementID* args
134
198
  ElementID dest
135
199
 
136
-
137
200
  cdef class Instructions:
138
201
  cdef Instruction* instructions
139
202
  cdef int allocated
@@ -142,14 +205,14 @@ cdef class Instructions:
142
205
  def __init__(self) -> None:
143
206
  self.num_instructions = 0
144
207
  self.allocated = 64
145
- self.instructions = <Instruction*> PyMem_Malloc(self.allocated * sizeof(Instruction))
208
+ self.instructions = <Instruction *> PyMem_Malloc(self.allocated * sizeof(Instruction))
146
209
 
147
210
  cdef void append_copy(
148
211
  self,
149
212
  ElementID src,
150
213
  ElementID dest,
151
214
  ) except*:
152
- c_args = <ElementID*> PyMem_Malloc(sizeof(ElementID))
215
+ c_args = <ElementID *> PyMem_Malloc(sizeof(ElementID))
153
216
  if not c_args:
154
217
  raise MemoryError()
155
218
 
@@ -158,14 +221,14 @@ cdef class Instructions:
158
221
 
159
222
  cdef void append_op(self, int symbol, object op_node: OpNode, dict[int, ElementID] node_to_element) except*:
160
223
  args = op_node.args
161
- cdef int num_args = len(args)
224
+ cdef Py_ssize_t num_args = len(args)
162
225
 
163
226
  # Create the instruction arguments array
164
- c_args = <ElementID*> PyMem_Malloc(num_args * sizeof(ElementID))
227
+ c_args = <ElementID *> PyMem_Malloc(num_args * sizeof(ElementID))
165
228
  if not c_args:
166
229
  raise MemoryError()
167
230
 
168
- cdef int i = num_args
231
+ cdef Py_ssize_t i = num_args
169
232
  while i > 0:
170
233
  i -= 1
171
234
  c_args[i] = node_to_element[id(args[i])]
@@ -174,8 +237,7 @@ cdef class Instructions:
174
237
 
175
238
  self._append(symbol, num_args, c_args, dest)
176
239
 
177
-
178
- cdef void _append(self, int symbol, int num_args, ElementID* c_args, ElementID dest) except *:
240
+ cdef void _append(self, int symbol, Py_ssize_t num_args, ElementID * c_args, ElementID dest) except*:
179
241
  cdef int i
180
242
 
181
243
  cdef int num_instructions = self.num_instructions
@@ -184,7 +246,7 @@ cdef class Instructions:
184
246
  cdef int allocated = self.allocated
185
247
  if num_instructions == allocated:
186
248
  allocated *= 2
187
- self.instructions = <Instruction*> PyMem_Realloc(
249
+ self.instructions = <Instruction *> PyMem_Realloc(
188
250
  self.instructions,
189
251
  allocated * sizeof(Instruction),
190
252
  )
@@ -202,65 +264,117 @@ cdef class Instructions:
202
264
  self.num_instructions = num_instructions + 1
203
265
 
204
266
  def __dealloc__(self) -> None:
205
- cdef Instruction* instructions = self.instructions
267
+ cdef Instruction * instructions = self.instructions
206
268
  if instructions:
207
269
  for i in range(self.num_instructions):
208
270
  PyMem_Free(instructions[i].args)
209
271
  PyMem_Free(instructions)
210
272
 
211
-
212
-
213
- @cython.boundscheck(False) # turn off bounds-checking for entire function
214
- @cython.wraparound(False) # turn off negative index wrapping for entire function
215
- cdef void cvm_float64(
216
- double* vars_in,
217
- double* tmps,
218
- double* consts,
219
- double* result,
220
- Instructions instructions,
221
- ) except *:
222
- # Core virtual machine (for dtype float64).
223
-
224
- cdef int i, symbol
225
- cdef double accumulator
226
- cdef ElementID* args
227
- cdef ElementID elem
273
+ cdef void cvm(
274
+ cvm_type * vars_in,
275
+ cvm_type * tmps,
276
+ cvm_type * consts,
277
+ cvm_type * result,
278
+ Instructions instructions,
279
+ ):
280
+ # Core virtual machine (for fused type 'cvm_type').
281
+
282
+ cdef int symbol
283
+ cdef cvm_type accumulator
284
+ cdef ElementID * args
285
+ cdef ElementID * args_end
286
+ cdef ElementID * dest
228
287
 
229
288
  # Index the four arrays by constants VARS, TMPS, CONSTS, and RESULT
230
- cdef (double*) arrays[4]
289
+ cdef (cvm_type *) arrays[4]
231
290
  arrays[VARS] = vars_in
232
291
  arrays[TMPS] = tmps
233
292
  arrays[CONSTS] = consts
234
293
  arrays[RESULT] = result
235
294
 
236
- cdef Instruction* instruction_ptr = instructions.instructions
237
- cdef int num_instructions = instructions.num_instructions
295
+ cdef Instruction * instruction_ptr = instructions.instructions
296
+ cdef Instruction * instruction_end = instruction_ptr + instructions.num_instructions
238
297
 
239
- while num_instructions > 0:
240
- num_instructions -= 1
298
+ while instruction_ptr < instruction_end:
241
299
 
242
300
  symbol = instruction_ptr.symbol
243
301
  args = instruction_ptr.args
244
302
 
245
- elem = args[0]
246
- accumulator = arrays[elem.array][elem.index]
303
+ accumulator = arrays[args.array][args.index]
247
304
 
248
305
  if symbol == ADD:
249
- i = instruction_ptr.num_args
250
- while i > 1:
251
- i -= 1
252
- elem = args[i]
253
- accumulator += arrays[elem.array][elem.index]
306
+ args_end = args + instruction_ptr.num_args
307
+ incr(args)
308
+ while args < args_end:
309
+ accumulator += arrays[args.array][args.index]
310
+ incr(args)
254
311
  elif symbol == MUL:
255
- i = instruction_ptr.num_args
256
- while i > 1:
257
- i -= 1
258
- elem = args[i]
259
- accumulator *= arrays[elem.array][elem.index]
312
+ args_end = args + instruction_ptr.num_args
313
+ incr(args)
314
+ while args < args_end:
315
+ accumulator *= arrays[args.array][args.index]
316
+ incr(args)
260
317
  # else symbol == COPY, nothing to do
261
318
 
262
- elem = instruction_ptr.dest
263
- arrays[elem.array][elem.index] = accumulator
319
+ dest = &instruction_ptr.dest
320
+ arrays[dest.array][dest.index] = accumulator
264
321
 
265
322
  # Advance the instruction pointer
266
- instruction_ptr = &(instruction_ptr[1])
323
+ incr(instruction_ptr)
324
+
325
+
326
+ # This is the older 4.0.0a17 version which seems to be 10% faster sometimes!
327
+ #
328
+ # cdef void cvm(
329
+ # cvm_type * vars_in,
330
+ # cvm_type * tmps,
331
+ # cvm_type * consts,
332
+ # cvm_type * result,
333
+ # Instructions instructions,
334
+ # ):
335
+ # # Core virtual machine (for fused type 'cvm_type').
336
+ #
337
+ # cdef int symbol
338
+ # cdef Py_ssize_t i
339
+ # cdef cvm_type accumulator
340
+ # cdef ElementID* args
341
+ # cdef ElementID elem
342
+ #
343
+ # # Index the four arrays by constants VARS, TMPS, CONSTS, and RESULT
344
+ # cdef (cvm_type*) arrays[4]
345
+ # arrays[VARS] = vars_in
346
+ # arrays[TMPS] = tmps
347
+ # arrays[CONSTS] = consts
348
+ # arrays[RESULT] = result
349
+ #
350
+ # cdef Instruction* instruction_ptr = instructions.instructions
351
+ # cdef Py_ssize_t num_instructions = instructions.num_instructions
352
+ #
353
+ # while num_instructions > 0:
354
+ # num_instructions -= 1
355
+ #
356
+ # symbol = instruction_ptr.symbol
357
+ # args = instruction_ptr.args
358
+ #
359
+ # elem = args[0]
360
+ # accumulator = arrays[elem.array][elem.index]
361
+ #
362
+ # if symbol == ADD:
363
+ # i = instruction_ptr.num_args
364
+ # while i > 1:
365
+ # i -= 1
366
+ # elem = args[i]
367
+ # accumulator += arrays[elem.array][elem.index]
368
+ # elif symbol == MUL:
369
+ # i = instruction_ptr.num_args
370
+ # while i > 1:
371
+ # i -= 1
372
+ # elem = args[i]
373
+ # accumulator *= arrays[elem.array][elem.index]
374
+ # # else symbol == COPY, nothing to do
375
+ #
376
+ # elem = instruction_ptr.dest
377
+ # arrays[elem.array][elem.index] = accumulator
378
+ #
379
+ # # Advance the instruction pointer
380
+ # instruction_ptr = &(instruction_ptr[1])
@@ -1,12 +1,13 @@
1
- from typing import Optional, Sequence
1
+ from typing import Optional, Sequence, Tuple
2
2
 
3
3
  import numpy as np
4
4
 
5
5
  from ck.circuit import CircuitNode, Circuit, VarNode
6
6
  from ck.circuit_compiler.support.input_vars import InferVars, InputVars, infer_input_vars
7
- from ck.program.raw_program import RawProgram
7
+ from ck.program.raw_program import RawProgram, RawProgramFunction
8
8
  from ck.utils.np_extras import DTypeNumeric
9
- from ._compiler import make_function as _make_function
9
+ from . import _compiler
10
+ from ..support.circuit_analyser import CircuitAnalysis, analyze_circuit
10
11
 
11
12
 
12
13
  def compile_circuit(
@@ -31,7 +32,11 @@ def compile_circuit(
31
32
  Raises:
32
33
  ValueError: if the circuit is unknown, but it is needed.
33
34
  ValueError: if not all nodes are from the same circuit.
35
+ ValueError: if the given dtype is not supported.
34
36
  """
37
+ if dtype not in _compiler.DTYPE_TO_CVM_TYPE.keys():
38
+ raise ValueError(f'dtype not supported: {dtype!r}')
39
+
35
40
  in_vars: Sequence[VarNode] = infer_input_vars(circuit, result, input_vars)
36
41
  return CythonRawProgram(in_vars, result, dtype)
37
42
 
@@ -70,7 +75,7 @@ class CythonRawProgram(RawProgram):
70
75
  'number_of_vars': self.number_of_vars,
71
76
  'number_of_tmps': self.number_of_tmps,
72
77
  'number_of_results': self.number_of_results,
73
- 'var_indices':self.var_indices,
78
+ 'var_indices': self.var_indices,
74
79
  #
75
80
  'in_vars': self.in_vars,
76
81
  'result': self.result,
@@ -94,3 +99,23 @@ class CythonRawProgram(RawProgram):
94
99
  result_nodes=self.result,
95
100
  dtype=self.dtype,
96
101
  )
102
+
103
+
104
+ def _make_function(
105
+ var_nodes: Sequence[VarNode],
106
+ result_nodes: Sequence[CircuitNode],
107
+ dtype: DTypeNumeric,
108
+ ) -> Tuple[RawProgramFunction, int]:
109
+ """
110
+ Make a RawProgram function that interprets the circuit.
111
+
112
+ Args:
113
+ var_nodes: The chosen input variable nodes of the circuit.
114
+ result_nodes: The chosen output result nodes of the circuit.
115
+ dtype: a numpy data type that must be a key in the dictionary, DTYPE_TO_CVM_TYPE.
116
+
117
+ Returns:
118
+ (function, number_of_tmps)
119
+ """
120
+ analysis: CircuitAnalysis = analyze_circuit(var_nodes, result_nodes)
121
+ return _compiler.make_function(analysis, dtype)
@@ -0,0 +1,13 @@
1
+ # There are two implementations of the `circuit_analyser` module are provided
2
+ # for developer R&D purposes. One is pure Python and the other is Cython.
3
+ # Which implementation is used can be selected here.
4
+ #
5
+ # A similar selection can be made for the `circuit` module.
6
+ # Note that if the Cython implementation is chosen for `circuit_analyser` then
7
+ # the Cython implementation must be chosen for `circuit`.
8
+
9
+ # from ._circuit_analyser_py import (
10
+ from ._circuit_analyser_cy import (
11
+ CircuitAnalysis,
12
+ analyze_circuit,
13
+ )
@@ -0,0 +1,98 @@
1
+ from dataclasses import dataclass
2
+ from typing import List, Dict, Sequence, Set
3
+
4
+ from ck.circuit._circuit_cy cimport Circuit, OpNode, VarNode, CircuitNode, ConstNode
5
+ from cython.operator cimport postincrement
6
+
7
+
8
+ @dataclass
9
+ class CircuitAnalysis:
10
+ """
11
+ A data structure representing the analysis of a function defined by
12
+ a circuit which chosen input variables and output result nodes.
13
+ """
14
+
15
+ var_nodes: Sequence[VarNode] # specified input var nodes
16
+ result_nodes: Sequence[CircuitNode] # specified result nodes
17
+ op_nodes: Sequence[OpNode] # in-use op nodes, in computation order
18
+ const_nodes: Sequence[ConstNode] # in_use const nodes, in arbitrary order
19
+ op_to_result: Dict[int, int] # op nodes in the result, op_node = result[idx]: id(op_node) -> idx
20
+ op_to_tmp: Dict[int, int] # op nodes needing tmp memory, using tmp[idx]: id(op_node) -> idx
21
+
22
+
23
+ def analyze_circuit(
24
+ var_nodes: Sequence[VarNode],
25
+ result_nodes: Sequence[CircuitNode],
26
+ ) -> CircuitAnalysis:
27
+ """
28
+ Analyzes a circuit as a function from var_nodes to result_nodes,
29
+ returning a CircuitAnalysis object.
30
+
31
+ Args:
32
+ var_nodes: The chosen input variable nodes of the circuit.
33
+ result_nodes: The chosen output result nodes of the circuit.
34
+
35
+ Returns:
36
+ A CircuitAnalysis object.
37
+ """
38
+ cdef list[CircuitNode] results_list = list(result_nodes)
39
+
40
+ # What op nodes are in use
41
+ cdef list[OpNode] op_nodes = _reachable_op_nodes(results_list)
42
+
43
+ # What constant values are in use
44
+ cdef set[int] seen_const_nodes = set()
45
+ cdef list[ConstNode] const_nodes = []
46
+
47
+ def _register_const(_node: ConstNode) -> None:
48
+ nonlocal seen_const_nodes
49
+ nonlocal const_nodes
50
+ _node_id: int = id(_node)
51
+ if _node_id not in seen_const_nodes:
52
+ const_nodes.append(_node)
53
+ seen_const_nodes.add(_node_id)
54
+
55
+ # Register all the used constants
56
+ for op_node in op_nodes:
57
+ for node in op_node.args:
58
+ if isinstance(node, ConstNode):
59
+ _register_const(node)
60
+ for node in results_list:
61
+ if isinstance(node, ConstNode):
62
+ _register_const(node)
63
+ for node in var_nodes:
64
+ if node.is_const():
65
+ _register_const(node.const)
66
+
67
+ # What op nodes are in the result.
68
+ # Dict op_to_result maps id(OpNode) to result index.
69
+ cdef dict[int, int] op_to_result = {
70
+ id(node): i
71
+ for i, node in enumerate(result_nodes)
72
+ if isinstance(node, OpNode)
73
+ }
74
+
75
+ # Assign all other op nodes to a tmp slot.
76
+ # Dict op_to_tmp maps id(OpNode) to tmp index.
77
+ cdef int tmp_idx = 0
78
+ op_to_tmp: Dict[int, int] = {
79
+ id(op_node): postincrement(tmp_idx)
80
+ for op_node in op_nodes
81
+ if id(op_node) not in op_to_result
82
+ }
83
+
84
+ return CircuitAnalysis(
85
+ var_nodes=var_nodes,
86
+ result_nodes=result_nodes,
87
+ op_nodes=op_nodes,
88
+ const_nodes=const_nodes,
89
+ op_to_result=op_to_result,
90
+ op_to_tmp=op_to_tmp,
91
+ )
92
+
93
+
94
+ cdef list[OpNode] _reachable_op_nodes(list[CircuitNode] results):
95
+ if len(results) == 0:
96
+ return []
97
+ cdef Circuit circuit = results[0].circuit
98
+ return circuit.find_reachable_op_nodes(results)
@@ -7,8 +7,13 @@ from ck.circuit import OpNode, VarNode, CircuitNode, ConstNode
7
7
 
8
8
  @dataclass
9
9
  class CircuitAnalysis:
10
- var_nodes: Sequence[VarNode] # input var nodes, in VarNode idx order
11
- result_nodes: Sequence[CircuitNode] # result nodes
10
+ """
11
+ A data structure representing the analysis of a function defined by
12
+ a circuit which chosen input variables and output result nodes.
13
+ """
14
+
15
+ var_nodes: Sequence[VarNode] # specified input var nodes
16
+ result_nodes: Sequence[CircuitNode] # specified result nodes
12
17
  op_nodes: Sequence[OpNode] # in-use op nodes, in computation order
13
18
  const_nodes: Sequence[ConstNode] # in_use const nodes, in arbitrary order
14
19
  op_to_result: Dict[int, int] # op nodes in the result, op_node = result[idx]: id(op_node) -> idx
@@ -22,6 +27,13 @@ def analyze_circuit(
22
27
  """
23
28
  Analyzes a circuit as a function from var_nodes to result_nodes,
24
29
  returning a CircuitAnalysis object.
30
+
31
+ Args:
32
+ var_nodes: The chosen input variable nodes of the circuit.
33
+ result_nodes: The chosen output result nodes of the circuit.
34
+
35
+ Returns:
36
+ A CircuitAnalysis object.
25
37
  """
26
38
  # What op nodes are in use
27
39
  op_nodes: List[OpNode] = (
@@ -1 +1 @@
1
- from .ace import compile_pgm, copy_ace_to_default_location, default_ace_location
1
+ from .ace import compile_pgm, copy_ace_to_default_location, default_ace_location, ace_available
@@ -1,3 +1,11 @@
1
+ # There are two implementations of the `circuit_table` module are provided
2
+ # for developer R&D purposes. One is pure Python and the other is Cython.
3
+ # Which implementation is used can be selected here.
4
+ #
5
+ # A similar selection can be made for the `circuit` module.
6
+ # Note that if the Cython implementation is chosen for `circuit_table` then
7
+ # the Cython implementation must be chosen for `circuit`.
8
+
1
9
  # from ._circuit_table_py import (
2
10
  from ._circuit_table_cy import (
3
11
  CircuitTable,