compiled-knowledge 4.0.0a17__cp312-cp312-win_amd64.whl → 4.0.0a18__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 (23) hide show
  1. ck/circuit/__init__.py +7 -0
  2. ck/circuit/_circuit_cy.cp312-win_amd64.pyd +0 -0
  3. ck/circuit/_circuit_cy.pxd +33 -0
  4. ck/circuit/_circuit_cy.pyx +84 -110
  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 +6 -5
  8. ck/pgm_compiler/support/circuit_table/__init__.py +7 -0
  9. ck/pgm_compiler/support/circuit_table/_circuit_table_cy.cp312-win_amd64.pyd +0 -0
  10. ck/pgm_compiler/support/circuit_table/_circuit_table_cy.pyx +44 -37
  11. ck/pgm_compiler/support/circuit_table/_circuit_table_cy_cpp_verion.pyx +601 -0
  12. ck/pgm_compiler/support/circuit_table/_circuit_table_cy_minimal_version.pyx +311 -0
  13. ck/pgm_compiler/support/circuit_table/_circuit_table_cy_v4.0.0a17.pyx +325 -0
  14. ck/pgm_compiler/support/circuit_table/_circuit_table_py.py +76 -41
  15. ck/pgm_compiler/support/named_compiler_maker.py +12 -2
  16. ck/utils/iter_extras.py +8 -1
  17. ck_demos/utils/compare.py +5 -1
  18. {compiled_knowledge-4.0.0a17.dist-info → compiled_knowledge-4.0.0a18.dist-info}/METADATA +1 -1
  19. {compiled_knowledge-4.0.0a17.dist-info → compiled_knowledge-4.0.0a18.dist-info}/RECORD +22 -19
  20. {compiled_knowledge-4.0.0a17.dist-info → compiled_knowledge-4.0.0a18.dist-info}/WHEEL +1 -1
  21. ck/circuit_compiler/cython_vm_compiler/_compiler.c +0 -16946
  22. {compiled_knowledge-4.0.0a17.dist-info → compiled_knowledge-4.0.0a18.dist-info}/licenses/LICENSE.txt +0 -0
  23. {compiled_knowledge-4.0.0a17.dist-info → compiled_knowledge-4.0.0a18.dist-info}/top_level.txt +0 -0
ck/circuit/__init__.py CHANGED
@@ -1,3 +1,10 @@
1
+ # There are two implementations of the `circuit` 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
+ # A similar selection can be made for the `circuit_table` module.
5
+ # Note that if the Cython implementation is chosen for `circuit_table` then
6
+ # the Cython implementation must be chosen for `circuit`.
7
+
1
8
  # from ._circuit_py import (
2
9
  from ._circuit_cy import (
3
10
  Circuit,
Binary file
@@ -0,0 +1,33 @@
1
+ cdef class Circuit:
2
+ cdef public list[VarNode] vars
3
+ cdef public list[OpNode] ops
4
+ cdef public object zero
5
+ cdef public object one
6
+ cdef dict[object, ConstNode] _const_map
7
+ cdef object __derivatives
8
+
9
+ cdef OpNode op(self, int symbol, tuple[CircuitNode, ...] nodes)
10
+ cdef void _remove_unreachable_op_nodes(self, list[CircuitNode] nodes)
11
+ cdef list[OpNode] _reachable_op_nodes(self, list[CircuitNode] nodes)
12
+ cdef list[CircuitNode] _check_nodes(self, object nodes)
13
+ cdef void __check_nodes(self, object nodes, list[CircuitNode] result)
14
+ cdef object _derivatives(self, CircuitNode f)
15
+ cdef object _derivatives(self, CircuitNode f)
16
+
17
+ cdef class CircuitNode:
18
+ cdef public Circuit circuit
19
+ cdef public bint is_zero
20
+ cdef public bint is_one
21
+
22
+ cdef class ConstNode(CircuitNode):
23
+ cdef public object value
24
+
25
+ cdef class VarNode(CircuitNode):
26
+ cdef public int idx
27
+ cdef object _const
28
+
29
+ cpdef int is_const(self) except*
30
+
31
+ cdef class OpNode(CircuitNode):
32
+ cdef public tuple[object, ...] args
33
+ cdef public int symbol
@@ -4,7 +4,7 @@ For more documentation on this module, refer to the Jupyter notebook docs/6_circ
4
4
  from __future__ import annotations
5
5
 
6
6
  from itertools import chain
7
- from typing import Dict, Tuple, Optional, Iterable, Sequence, List, overload, Any
7
+ from typing import Dict, Tuple, Optional, Iterable, Sequence, List, overload
8
8
 
9
9
  # Type for values of ConstNode objects
10
10
  ConstValue = float | int | bool
@@ -15,6 +15,8 @@ Args = CircuitNode | ConstValue | Iterable[CircuitNode | ConstValue]
15
15
  ADD: int = 0
16
16
  MUL: int = 1
17
17
 
18
+ cdef int c_ADD = ADD
19
+ cdef int c_MUL = MUL
18
20
 
19
21
  cdef class Circuit:
20
22
  """
@@ -29,13 +31,6 @@ cdef class Circuit:
29
31
  `VarNode` may be temporarily be set to a constant value.
30
32
  """
31
33
 
32
- cdef public list[VarNode] vars
33
- cdef public list[OpNode] ops
34
- cdef public object zero
35
- cdef public object one
36
- cdef dict[Any, ConstNode] _const_map
37
- cdef object __derivatives
38
-
39
34
  def __init__(self, zero: ConstValue = 0, one: ConstValue = 1):
40
35
  """
41
36
  Construct a new, empty circuit.
@@ -127,72 +122,57 @@ cdef class Circuit:
127
122
  self._const_map[value] = node
128
123
  return node
129
124
 
130
- cdef OpNode _op(self, int symbol, tuple[CircuitNode, ...] nodes):
131
- cdef OpNode node = OpNode(self, symbol, nodes)
132
- self.ops.append(node)
133
- return node
134
-
135
125
  def add(self, *nodes: Args) -> OpNode:
136
126
  """
137
127
  Create and return a new 'addition' node, applied to the given arguments.
138
128
  """
139
129
  cdef list[CircuitNode] args = self._check_nodes(nodes)
140
- return self._op(ADD, tuple(args))
130
+ return self.op(c_ADD, tuple(args))
141
131
 
142
132
  def mul(self, *nodes: Args) -> OpNode:
143
133
  """
144
134
  Create and return a new 'multiplication' node, applied to the given arguments.
145
135
  """
146
136
  cdef list[CircuitNode] args = self._check_nodes(nodes)
147
- return self._op(MUL, tuple(args))
137
+ return self.op(c_MUL, tuple(args))
148
138
 
149
- cpdef object optimised_add(self, object nodes: Iterable[CircuitNode]): # -> CircuitNode:
150
- # Optimised circuit node addition.
151
- #
152
- # Performs the following optimisations:
153
- # * addition to zero is avoided: add(x, 0) = x,
154
- # * singleton addition is avoided: add(x) = x,
155
- # * empty addition is avoided: add() = 0.
156
-
157
- cdef list[CircuitNode] to_add = []
158
- cdef CircuitNode n
159
- for n in nodes:
160
- if n.circuit is not self:
161
- raise RuntimeError('node does not belong to this circuit')
162
- if not n.is_zero:
163
- to_add.append(n)
164
- cdef int len_to_add = len(to_add)
165
- if len_to_add == 0:
139
+ def optimised_add(self, *args: Args) -> CircuitNode:
140
+ """
141
+ Optimised circuit node addition.
142
+
143
+ Performs the following optimisations:
144
+ * addition to zero is avoided: add(x, 0) = x,
145
+ * singleton addition is avoided: add(x) = x,
146
+ * empty addition is avoided: add() = 0,
147
+ """
148
+ cdef tuple[CircuitNode] to_add = tuple(n for n in self._check_nodes(args) if not n.is_zero)
149
+ cdef size_t num_to_add = len(to_add)
150
+ if num_to_add == 0:
166
151
  return self.zero
167
- elif len_to_add == 1:
152
+ if num_to_add == 1:
168
153
  return to_add[0]
169
- else:
170
- return self._op(ADD, tuple(to_add))
154
+ return self.op(c_ADD, to_add)
171
155
 
172
- cpdef object optimised_mul(self, object nodes: Iterable[CircuitNode]): # -> CircuitNode:
173
- # Optimised circuit node multiplication.
174
- #
175
- # Performs the following optimisations:
176
- # * multiplication by zero is avoided: mul(x, 0) = 0,
177
- # * multiplication by one is avoided: mul(x, 1) = x,
178
- # * singleton multiplication is avoided: mul(x) = x,
179
- # * empty multiplication is avoided: mul() = 1.
180
- cdef list[CircuitNode] to_mul = []
181
- cdef CircuitNode n
182
- for n in nodes:
183
- if n.circuit is not self:
184
- raise RuntimeError('node does not belong to this circuit')
185
- if n.is_zero:
186
- return self.zero
187
- if not n.is_one:
188
- to_mul.append(n)
189
- cdef int len_to_mul = len(to_mul)
190
- if len_to_mul == 0:
156
+ def optimised_mul(self, *args: Args) -> CircuitNode:
157
+ """
158
+ Optimised circuit node multiplication.
159
+
160
+ Performs the following optimisations:
161
+ * multiplication by zero is avoided: mul(x, 0) = 0,
162
+ * multiplication by one is avoided: mul(x, 1) = x,
163
+ * singleton multiplication is avoided: mul(x) = x,
164
+ * empty multiplication is avoided: mul() = 1,
165
+ """
166
+ cdef tuple[CircuitNode] to_mul = tuple(n for n in self._check_nodes(args) if not n.is_one)
167
+ if any(n.is_zero for n in to_mul):
168
+ return self.zero
169
+ cdef size_t num_to_mul = len(to_mul)
170
+
171
+ if num_to_mul == 0:
191
172
  return self.one
192
- elif len_to_mul == 1:
173
+ if num_to_mul == 1:
193
174
  return to_mul[0]
194
- else:
195
- return self._op(MUL, tuple(to_mul))
175
+ return self.op(c_MUL, to_mul)
196
176
 
197
177
  def cartesian_product(self, xs: Sequence[CircuitNode], ys: Sequence[CircuitNode]) -> List[CircuitNode]:
198
178
  """
@@ -209,7 +189,7 @@ cdef class Circuit:
209
189
  xs: List[CircuitNode] = self._check_nodes(xs)
210
190
  ys: List[CircuitNode] = self._check_nodes(ys)
211
191
  return [
212
- self.optimised_mul((x, y))
192
+ self.optimised_mul(x, y)
213
193
  for x in xs
214
194
  for y in ys
215
195
  ]
@@ -306,24 +286,6 @@ cdef class Circuit:
306
286
  nodes = self._check_nodes(nodes)
307
287
  self._remove_unreachable_op_nodes(nodes)
308
288
 
309
- cdef void _remove_unreachable_op_nodes(self, list[CircuitNode] nodes):
310
- # Set of object ids for all reachable op nodes
311
- cdef set[int] seen = set()
312
-
313
- cdef CircuitNode node
314
- for node in nodes:
315
- _reachable_op_nodes_seen_r(node, seen)
316
-
317
- if len(seen) < len(self.ops):
318
- # Invalidate unreadable op nodes
319
- for op_node in self.ops:
320
- if id(op_node) not in seen:
321
- op_node.circuit = None
322
- op_node.args = ()
323
-
324
- # Keep only reachable op nodes, in the same order as `self.ops`.
325
- self.ops = [op_node for op_node in self.ops if id(op_node) in seen]
326
-
327
289
  def reachable_op_nodes(self, *nodes: Args) -> List[OpNode]:
328
290
  """
329
291
  Iterate over all op nodes reachable from the given nodes, via op arguments.
@@ -341,17 +303,6 @@ cdef class Circuit:
341
303
  nodes = self._check_nodes(nodes)
342
304
  return self._reachable_op_nodes(nodes)
343
305
 
344
- cdef list[OpNode] _reachable_op_nodes(self, list[CircuitNode] nodes):
345
- # Set of object ids for all reachable op nodes
346
- cdef set[int] seen = set()
347
-
348
- cdef list[OpNode] result = []
349
-
350
- cdef CircuitNode node
351
- for node in nodes:
352
- _reachable_op_nodes_r(node, seen, result)
353
- return result
354
-
355
306
  def dump(
356
307
  self,
357
308
  *,
@@ -415,6 +366,40 @@ cdef class Circuit:
415
366
  args_str = ' '.join(node_name[id(arg)] for arg in op.args)
416
367
  print(f'{next_prefix}{op_name}: {args_str}')
417
368
 
369
+ cdef OpNode op(self, int symbol, tuple[CircuitNode, ...] nodes):
370
+ cdef OpNode node = OpNode(self, symbol, nodes)
371
+ self.ops.append(node)
372
+ return node
373
+
374
+ cdef list[OpNode] _reachable_op_nodes(self, list[CircuitNode] nodes):
375
+ # Set of object ids for all reachable op nodes
376
+ cdef set[int] seen = set()
377
+
378
+ cdef list[OpNode] result = []
379
+
380
+ cdef CircuitNode node
381
+ for node in nodes:
382
+ _reachable_op_nodes_r(node, seen, result)
383
+ return result
384
+
385
+ cdef void _remove_unreachable_op_nodes(self, list[CircuitNode] nodes):
386
+ # Set of object ids for all reachable op nodes
387
+ cdef set[int] seen = set()
388
+
389
+ cdef CircuitNode node
390
+ for node in nodes:
391
+ _reachable_op_nodes_seen_r(node, seen)
392
+
393
+ if len(seen) < len(self.ops):
394
+ # Invalidate unreadable op nodes
395
+ for op_node in self.ops:
396
+ if id(op_node) not in seen:
397
+ op_node.circuit = None
398
+ op_node.args = ()
399
+
400
+ # Keep only reachable op nodes, in the same order as `self.ops`.
401
+ self.ops = [op_node for op_node in self.ops if id(op_node) in seen]
402
+
418
403
  cdef list[CircuitNode] _check_nodes(self, object nodes: Iterable[Args]): # -> Sequence[CircuitNode]:
419
404
  # Convert the given circuit nodes to a tuple, flattening nested iterables as needed.
420
405
  #
@@ -427,7 +412,7 @@ cdef class Circuit:
427
412
  self.__check_nodes(nodes, result)
428
413
  return result
429
414
 
430
- cdef void __check_nodes(self, nodes: Iterable[Args], list[CircuitNode] result):
415
+ cdef void __check_nodes(self, object nodes: Iterable[Args], list[CircuitNode] result):
431
416
  # Convert the given circuit nodes to a tuple, flattening nested iterables as needed.
432
417
  #
433
418
  # Args:
@@ -455,7 +440,6 @@ cdef class Circuit:
455
440
  self.__derivatives = derivatives
456
441
  return derivatives
457
442
 
458
-
459
443
  cdef class CircuitNode:
460
444
  """
461
445
  A node in an arithmetic circuit.
@@ -470,9 +454,6 @@ cdef class CircuitNode:
470
454
  A var node may be temporarily set to be a constant node, which may
471
455
  be useful for optimising a compiled circuit.
472
456
  """
473
- cdef public Circuit circuit
474
- cdef public bint is_zero
475
- cdef public bint is_one
476
457
 
477
458
  def __init__(self, circuit: Circuit, is_zero: bool, is_one: bool):
478
459
  self.circuit = circuit
@@ -485,11 +466,10 @@ cdef class CircuitNode:
485
466
  def __mul__(self, other: CircuitNode | ConstValue):
486
467
  return self.circuit.mul(self, other)
487
468
 
488
-
489
469
  cdef class ConstNode(CircuitNode):
490
- # A node in a circuit representing a constant value.
491
-
492
- cdef public object value
470
+ """
471
+ A node in a circuit representing a constant value.
472
+ """
493
473
 
494
474
  def __init__(self, circuit, value: ConstValue, is_zero: bool = False, is_one: bool = False):
495
475
  super().__init__(circuit, is_zero, is_one)
@@ -504,13 +484,10 @@ cdef class ConstNode(CircuitNode):
504
484
  else:
505
485
  return False
506
486
 
507
-
508
487
  cdef class VarNode(CircuitNode):
509
488
  """
510
489
  A node in a circuit representing an input variable.
511
490
  """
512
- cdef public int idx
513
- cdef object _const
514
491
 
515
492
  def __init__(self, circuit, idx: int):
516
493
  super().__init__(circuit, False, False)
@@ -552,13 +529,11 @@ cdef class OpNode(CircuitNode):
552
529
  """
553
530
  A node in a circuit representing an arithmetic operation.
554
531
  """
555
- cdef public tuple[object, ...] args
556
- cdef public int symbol
557
532
 
558
533
  def __init__(self, object circuit, symbol: int, tuple[object, ...] args: Tuple[CircuitNode]):
559
534
  super().__init__(circuit, False, False)
560
535
  self.args = tuple(args)
561
- self.symbol = int(symbol)
536
+ self.symbol = <int> symbol
562
537
 
563
538
  def __str__(self) -> str:
564
539
  return f'{self.op_str()}\\{len(self.args)}'
@@ -567,9 +542,9 @@ cdef class OpNode(CircuitNode):
567
542
  """
568
543
  Returns the op node operation as a string.
569
544
  """
570
- if self.symbol == MUL:
545
+ if self.symbol == c_MUL:
571
546
  return 'mul'
572
- elif self.symbol == ADD:
547
+ elif self.symbol == c_ADD:
573
548
  return 'add'
574
549
  else:
575
550
  return '?' + str(self.symbol)
@@ -667,7 +642,7 @@ class _DerivativeHelper:
667
642
  elif d is self.one:
668
643
  d_node.derivative_self_mul = node
669
644
  else:
670
- d_node.derivative_self_mul = self.circuit.optimised_mul((d, node))
645
+ d_node.derivative_self_mul = self.circuit.optimised_mul(d, node)
671
646
 
672
647
  return d_node.derivative_self_mul
673
648
 
@@ -685,7 +660,7 @@ class _DerivativeHelper:
685
660
  d_node.sum_prod = None
686
661
 
687
662
  # Construct the addition operation
688
- d_node.derivative = self.circuit.optimised_add(to_add)
663
+ d_node.derivative = self.circuit.optimised_add(*to_add)
689
664
 
690
665
  return d_node.derivative
691
666
 
@@ -706,7 +681,7 @@ class _DerivativeHelper:
706
681
  to_mul.append(arg)
707
682
 
708
683
  # Construct the multiplication operation
709
- return self.circuit.optimised_mul(to_mul)
684
+ return self.circuit.optimised_mul(*to_mul)
710
685
 
711
686
  def _mk_derivative_r(self, d_node: _DNode) -> None:
712
687
  """
@@ -718,11 +693,11 @@ class _DerivativeHelper:
718
693
  node: CircuitNode = d_node.node
719
694
 
720
695
  if isinstance(node, OpNode):
721
- if node.symbol == ADD:
696
+ if node.symbol == c_ADD:
722
697
  for arg in node.args:
723
698
  child_d_node = self._add(arg, d_node, [])
724
699
  self._mk_derivative_r(child_d_node)
725
- elif node.symbol == MUL:
700
+ elif node.symbol == c_MUL:
726
701
  for arg in node.args:
727
702
  prod = [arg2 for arg2 in node.args if arg is not arg2]
728
703
  child_d_node = self._add(arg, d_node, prod)
@@ -780,7 +755,6 @@ cdef void _reachable_op_nodes_r(CircuitNode node, set[int] seen, list[OpNode] re
780
755
  _reachable_op_nodes_r(arg, seen, result)
781
756
  result.append(node)
782
757
 
783
-
784
758
  cdef void _reachable_op_nodes_seen_r(CircuitNode node, set[int] seen):
785
759
  # Recursive helper for `remove_unreachable_op_nodes`. Performs a depth-first search.
786
760
  #
ck/circuit/_circuit_py.py CHANGED
@@ -721,7 +721,7 @@ class _DerivativeHelper:
721
721
  d_node.sum_prod = None
722
722
 
723
723
  # Construct the addition operation
724
- d_node.derivative = self.circuit.optimised_add(to_add)
724
+ d_node.derivative = self.circuit.optimised_add(*to_add)
725
725
 
726
726
  return d_node.derivative
727
727
 
@@ -742,7 +742,7 @@ class _DerivativeHelper:
742
742
  to_mul.append(arg)
743
743
 
744
744
  # Construct the multiplication operation
745
- return self.circuit.optimised_mul(to_mul)
745
+ return self.circuit.optimised_mul(*to_mul)
746
746
 
747
747
  def _mk_derivative_r(self, d_node: _DNode) -> None:
748
748
  """
@@ -129,7 +129,7 @@ cdef struct ElementID:
129
129
 
130
130
  cdef struct Instruction:
131
131
  int symbol # ADD, MUL, COPY
132
- int num_args
132
+ Py_ssize_t num_args
133
133
  ElementID* args
134
134
  ElementID dest
135
135
 
@@ -158,14 +158,14 @@ cdef class Instructions:
158
158
 
159
159
  cdef void append_op(self, int symbol, object op_node: OpNode, dict[int, ElementID] node_to_element) except*:
160
160
  args = op_node.args
161
- cdef int num_args = len(args)
161
+ cdef Py_ssize_t num_args = len(args)
162
162
 
163
163
  # Create the instruction arguments array
164
164
  c_args = <ElementID*> PyMem_Malloc(num_args * sizeof(ElementID))
165
165
  if not c_args:
166
166
  raise MemoryError()
167
167
 
168
- cdef int i = num_args
168
+ cdef Py_ssize_t i = num_args
169
169
  while i > 0:
170
170
  i -= 1
171
171
  c_args[i] = node_to_element[id(args[i])]
@@ -175,7 +175,7 @@ cdef class Instructions:
175
175
  self._append(symbol, num_args, c_args, dest)
176
176
 
177
177
 
178
- cdef void _append(self, int symbol, int num_args, ElementID* c_args, ElementID dest) except *:
178
+ cdef void _append(self, int symbol, Py_ssize_t num_args, ElementID* c_args, ElementID dest) except *:
179
179
  cdef int i
180
180
 
181
181
  cdef int num_instructions = self.num_instructions
@@ -221,7 +221,8 @@ cdef void cvm_float64(
221
221
  ) except *:
222
222
  # Core virtual machine (for dtype float64).
223
223
 
224
- cdef int i, symbol
224
+ cdef int symbol
225
+ cdef Py_ssize_t i
225
226
  cdef double accumulator
226
227
  cdef ElementID* args
227
228
  cdef ElementID elem
@@ -1,3 +1,10 @@
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
+ # A similar selection can be made for the `circuit` module.
5
+ # Note that if the Cython implementation is chosen for `circuit_table` then
6
+ # the Cython implementation must be chosen for `circuit`.
7
+
1
8
  # from ._circuit_table_py import (
2
9
  from ._circuit_table_cy import (
3
10
  CircuitTable,