circuitry module
Embedded domain-specific combinator library for assembling abstract definitions of logical circuits and for automatically synthesizing circuits from those definitions.
This library makes it possible both to construct circuits programmatically (see
the documentation for the bit
class) and to synthesize circuits in an
automated manner from Python function definitions (see the documentation for
the synthesize
function).
- class circuitry.circuitry.bit(value: int, gate: Optional[gate] = None)[source]
Bases:
object
Class for representing an abstract bit. Such a bit can be interpreted concretely as a value, but it is also used to keep track of relationships between operators and to represent the individual wires within a circuit that is programmatically built up from gates that correspond to those operators.
The documentation for this class describes how circuits can be synthesized programmatically using the methods made available by this class. Consult the documentation for the
synthesize
function to learn how circuits can be synthesized automatically from Python function definitions.When constructing a circuit programmatically, it is first necessary to introduce a new circuit object and to designate this object as the circuit that is being constructed.
>>> c = circuit() >>> bit.circuit(c)
Subsequently, it is possible to instantiate subclasses of
bit
that serve specific roles (such asinput
, which represents an input value into the overall circuit being constructed). An operator method such asbit.and_
can be used to operate onbit
instances (and, at the same time to introduce a gate into the circuit).>>> b = output(input(1).and_(input(1)))
At this point, constructed circuit can be retrieved and evaluated on a vector of bit values (wherein each value is represented using the integer
0
or the integer1
).>>> b.value == bit.circuit().evaluate([1, 1])[0] True
It is possible to add custom hook functions that are applied whenever an operator is introduced using the
bit.hook_operation
method.>>> def make_hook_that_prints_created_gates(bit_): ... def hook(o, v, *args): ... print('created gate with operation "' + o.name() + '"') ... return bit_.constructor(*args)(v, bit_.gate(o, [a.gate for a in args])) ... return hook >>> bit.hook_operation(make_hook_that_prints_created_gates(bit)) >>> bit.circuit(circuit()) >>> b = output(input(0).and_(input(1)).not_()) created gate with operation "and" created gate with operation "not"
Note that a hook must be removed if its effects are not desired when subsequent circuits are constructed.
>>> bit.hook_operation()
- static circuit(circuit: Optional[circuit.circuit.circuit] = None) Optional[circuit.circuit.circuit] [source]
Designate the circuit object that is under construction. Any invocation of the
bit
constructor adds a gate to the circuit designated using this method.>>> c = circuit() >>> bit.circuit(c) >>> b = output(input(1).and_(input(1))) >>> b.value == bit.circuit().evaluate([1, 1])[0] True
Invoking this method with no argument retrieves and removes the circuit object (leaving no designated circuit object). Note that because of the invocation of this method at the end of the above example, the invocation below returns
None
.>>> bit.circuit() is None True
- static hook_operation(hook: Optional[Callable] = None)[source]
Assign a function that is invoked whenever the
bit.operation
method is used to create a new instance ofbit
.>>> def make_hook_that_prints_created_gates(bit_): ... def hook(o, v, *args): ... print('created gate with operation "' + o.name() + '"') ... return bit_.constructor(*args)(v, bit_.gate(o, [a.gate for a in args])) ... return hook >>> bit.hook_operation(make_hook_that_prints_created_gates(bit)) >>> bit.circuit(circuit()) >>> b = output(input(0).and_(input(1)).not_()) created gate with operation "and" created gate with operation "not" >>> b.value == bit.circuit().evaluate([0, 0])[0] True
If a hook function returns
None
, then the default instance ofbit
is returned when an operation is applied.>>> bit.hook_operation(lambda o, v, *args: None) >>> bit.circuit(circuit()) >>> b = output(input(1).and_(input(1))) >>> b.value == bit.circuit().evaluate([1, 1])[0] True
It is only possible to assign one hook function at a time. A hook must be removed if its effects are not desired when subsequent circuits are constructed.
>>> bit.hook_operation()
- static operation(o: Callable, *args) circuitry.circuitry.bit [source]
Apply the supplied operation method to zero or more
bit
arguments. Gate operations are represented using instances of thelogical
class exported by the logical library. This module indirectly imports thelogical
class via theop
synonym defined in the circuit library, enabling the more concise syntax used in the example below.>>> bit.circuit(circuit()) >>> b = output(bit.operation(op.and_, input(1), input(1))) >>> b.value == bit.circuit().evaluate([1, 1])[0] True
Arguments that are instances of
output
are not permitted.>>> bit.circuit(circuit()) >>> b0 = output(input(0).not_()) >>> b1 = b0.not_() Traceback (most recent call last): ... TypeError: cannot supply an output as an argument to an operation >>> _ = bit.circuit() # Remove designated circuit.
- static constructor(b1: Optional[circuitry.circuitry.bit] = None, b2: Optional[circuitry.circuitry.bit] = None) type [source]
Return the constructor to use for instantiating a
bit
object. This method is used by thebit.operation
andbits.from_byte
methods. It is provided primarily to aid in the implementation of custom hooks that can be set using thebit.hook_operation
method.
- static gate(operation: op, igs: Sequence[gate]) Optional[gate] [source]
Add a gate to the designated circuit object that is under construction. This method is primarily provided to aid in the implementation of custom hooks that can be set using the
bit.hook_operation
method. Gate operations are represented using instances of thelogical
class exported by the logical library (which is indirectly imported into this module as theop
synonym from the circuit library).>>> def make_hook(bit_): ... def hook(o, v, *args): ... return bit_.constructor(*args)(v, bit_.gate(o, [a.gate for a in args])) ... return hook >>> bit.hook_operation(make_hook(bit)) >>> bit.circuit(circuit()) >>> b = output(input(0).and_(input(0))) >>> b.value == bit.circuit().evaluate([0, 0])[0] True
- __int__() int [source]
Convert this
bit
instance into the integer representation of its value.>>> int(bit(1)) 1
- id_() circuitry.circuitry.bit [source]
Logical operation for an individual
bit
instance.>>> results = [] >>> for x in [0, 1]: ... bit.circuit(circuit()) ... b = output(input(x).id_()) ... results.append(int(b) == bit.circuit().evaluate([x])[0]) >>> all(results) True
- not_() circuitry.circuitry.bit [source]
Logical operation for an individual
bit
instance.>>> results = [] >>> for x in [0, 1]: ... bit.circuit(circuit()) ... b = output(input(x).not_()) ... results.append(int(b) == bit.circuit().evaluate([x])[0]) >>> all(results) True
- __invert__() circuitry.circuitry.bit [source]
Logical operation for an individual
bit
instance.>>> results = [] >>> for x in [0, 1]: ... bit.circuit(circuit()) ... b = output(~input(x)) ... results.append(int(b) == bit.circuit().evaluate([x])[0]) >>> all(results) True
- __rsub__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Arithmetic operation involving an individual
bit
instance that is often used to represent the logical negation operation. This method is provided primarily as a convenience enabling use of the syntax1 - b
to represent logical negation of a bit valueb
; it does not enable subtraction of onebit
instance from another.>>> results = [] >>> for x in [0, 1]: ... bit.circuit(circuit()) ... b = output(1 - input(x)) ... results.append(int(b) == bit.circuit().evaluate([x])[0]) >>> all(results) True >>> 2 - input(0) Traceback (most recent call last): ... ValueError: can only subtract a bit from the integer 1
- and_(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).and_(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __and__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x) & input(y)) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __rand__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> b = 0 & constant(1) >>> b.value 0
- nimp(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).nimp(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- nimp_(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).nimp_(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __gt__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x) > input(y)) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- nif(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).nif(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- nif_(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).nif_(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __lt__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x) < input(y)) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- xor(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).xor(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- xor_(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).xor_(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __xor__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x) ^ input(y)) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __rxor__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> b = 1 ^ constant(0) >>> b.value 1
- or_(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).or_(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __or__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x) | input(y)) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __ror__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> b = 1 | constant(0) >>> b.value 1
- nor(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).nor(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- nor_(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).nor_(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __mod__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x) % input(y)) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- xnor(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).xnor(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- xnor_(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).xnor_(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __eq__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x) == input(y)) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- if_(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).if_(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __ge__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x) >= input(y)) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- imp(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).imp(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- imp_(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).imp_(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __le__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x) <= input(y)) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- nand(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).nand(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- nand_(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x).nand_(input(y))) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- __matmul__(other: circuitry.circuitry.bit) circuitry.circuitry.bit [source]
Logical operation for individual
bit
instances.>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... b = output(input(x) @ input(y)) ... results.append(int(b) == bit.circuit().evaluate([x, y])[0]) >>> all(results) True
- class circuitry.circuitry.constant(value: int)[source]
Bases:
circuitry.circuitry.bit
Instance of a
bit
that is designated as a constant value.>>> constant(1).value 1
When a
constant
instance is introduced during circuit construction, a gate with a nullary operator (i.e., one of the two appearing innullary
) is added to the circuit.>>> bit.circuit(circuit()) >>> _ = output(constant(0)) >>> c = bit.circuit() >>> c.count() 2 >>> c.evaluate([]) [0]
- class circuitry.circuitry.input(value: int)[source]
Bases:
circuitry.circuitry.bit
Instance of a
bit
that is designated as a variable input. When aninput
instance is introduced during circuit construction, a gate is added to the circuit that is explicitly marked as an input gate (as defined in the circuit library).>>> b0 = output(input(1).not_()) >>> b0.value 0
- class circuitry.circuitry.input_one(value: int)[source]
Bases:
circuitry.circuitry.input
Instance of a
bit
that is designated as a variable input from one source.
- class circuitry.circuitry.input_two(value: int)[source]
Bases:
circuitry.circuitry.input
Instance of a
bit
that is designated as a variable input from a second source
- class circuitry.circuitry.output(b: circuitry.circuitry.bit)[source]
Bases:
circuitry.circuitry.bit
Instance of a
bit
that is designated as an output. When anoutput
instance is introduced during circuit construction, a gate is added to the circuit that is explicitly marked as an output gate (as defined in the circuit library).>>> bit.circuit(circuit()) >>> b0 = input(0).not_() >>> b1 = output(b0.not_()) >>> [b0.value, b1.value] [1, 0] >>> bit.circuit().evaluate([1]) [1]
It is not possible to apply an operation to an
output
instance. Any attempt to do so raises an exception (see implementation ofbit.operation
for more details).>>> bit.circuit(circuit()) >>> b0 = output(input(0).not_()) >>> b1 = b0.not_() Traceback (most recent call last): ... TypeError: cannot supply an output as an argument to an operation
If the value represented by a
bit
instance must be both an argument into another operation and an output, anbit.id_
operation can be introduced.>>> bit.circuit(circuit()) >>> b0 = input(1).not_() >>> b1 = output(b0.id_()) >>> b2 = output(b0.not_()) >>> [b0.value, b1.value, b2.value] [0, 0, 1] >>> bit.circuit().evaluate([0]) [1, 0]
- class circuitry.circuitry.bits(argument=None)[source]
Bases:
list
Class for representing a bit vector (i.e., a list of abstract
bit
instances).>>> bs = bits([constant(1)] * 3) >>> [b.value for b in bs] [1, 1, 1]
- static from_byte(b: int, constructor=bit) circuitry.circuitry.bits [source]
Convert an integer representing a single byte into a corresponding bit vector.
>>> [b.value for b in bits.from_byte(255)] [1, 1, 1, 1, 1, 1, 1, 1]
- static from_bytes(bs: Union[bytes, bytearray], constructor=bit) circuitry.circuitry.bits [source]
Convert a vector of bytes into a corresponding bit vector.
>>> [b.value for b in bits.from_bytes(bytes([255]))] [1, 1, 1, 1, 1, 1, 1, 1] >>> [b.value for b in bits.from_bytes(bytes([11, 0]))] [0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
- static zeros(n: int) circuitry.circuitry.bits [source]
Create a vector (of the specified length) of constant zero bits.
>>> bit.circuit(circuit()) >>> xs = bits.zeros(3) >>> ys = outputs(xs.not_()) >>> [y.value for y in ys] [1, 1, 1] >>> _ = bit.circuit() # Remove designated circuit.
- __int__() int [source]
Convert a bit vector into an integer.
>>> bit.circuit(circuit()) >>> xs = constants([0, 0, 0]) >>> ys = outputs(xs.not_()) >>> int(ys) 7 >>> _ = bit.circuit() # Remove designated circuit.
- not_() circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for x in [0, 1]: ... bit.circuit(circuit()) ... xs = inputs([x, x, x]) ... ys = outputs(xs.not_()) ... ns = [int(y) for y in ys] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x])) >>> all(results) True
- __invert__() circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for x in [0, 1]: ... bit.circuit(circuit()) ... xs = inputs([x, x, x]) ... ys = outputs(~xs) ... ns = [int(y) for y in ys] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x])) >>> all(results) True
- and_(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.and_(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- __and__(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs & ys) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- nimp(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.nimp(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- nimp_(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.nimp_(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- __gt__(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs > ys) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- nif(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.nif(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- nif_(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.nif_(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- __lt__(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs < ys) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- xor(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.xor(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- xor_(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.xor_(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- __xor__(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs ^ ys) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- or_(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.or_(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- __or__(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs | ys) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- nor(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.nor(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- nor_(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.nor_(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- __mod__(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs % ys) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- xnor(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.xnor(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- xnor_(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.xnor_(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- __eq__(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs == ys) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- if_(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.if_(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- __ge__(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs >= ys) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- imp(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.imp(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- imp_(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.imp_(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- __le__(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs <= ys) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- nand(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.nand(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- nand_(other: circuitry.circuitry.bits) circuitry.circuitry.bits [source]
Logical operation for bit vectors.
>>> results = [] >>> for (x, y) in [(0, 0), (0, 1), (1, 0), (1, 1)]: ... bit.circuit(circuit()) ... (xs, ys) = (inputs([x, x, x]), inputs([y, y, y])) ... zs = outputs(xs.nand_(ys)) ... ns = [int(z) for z in zs] ... c = bit.circuit() ... results.append(ns == c.evaluate([x, x, x, y, y, y])) >>> all(results) True
- __rshift__(other: Union[int, set]) circuitry.circuitry.bits [source]
Overloaded operator for performing rotation and shift operations on bit vectors. When the second parameter is an integer, a shift operation is performed. When the second parameter is a set containing a single integer, a rotation operation is performed.
>>> bs = bits(map(bit, [1, 1, 1, 1, 0, 0, 0, 0])) >>> bs = bs >> 3 >>> [b.value for b in bs] [0, 0, 0, 1, 1, 1, 1, 0] >>> bs = bits(map(bit, [0, 0, 0, 0, 1, 1, 1, 1])) >>> bs = bs >> {3} >>> [b.value for b in bs] [1, 1, 1, 0, 0, 0, 0, 1]
- __lshift__(other: int) circuitry.circuitry.bits [source]
Overloaded operator for performing shift operations on bit vectors.
>>> bs = bits(map(bit, [1, 1, 1, 1, 0, 0, 0, 0])) >>> bs = bs << 3 >>> [b.value for b in bs] [1, 0, 0, 0, 0, 0, 0, 0]
- __truediv__(other: [Union, set, list]) Sequence[bits] [source]
Overloaded operator for splitting a bit vector into a collection of smaller bit vectors. Three operations are possible depending on the type of the second parameter.
When the second parameter is an integer, this instance is split into that number of bit vectors. If the size of the vector is not an exact multiple of the integer, the parts in the result may not all be the same length.
>>> bs = bits(map(bit, [1, 1, 1, 1, 0, 0, 0, 0])) >>> bss = list(bs / 2) >>> ([b.value for b in bss[0]], [b.value for b in bss[1]]) ([1, 1, 1, 1], [0, 0, 0, 0]) >>> bs = bits(map(bit, [1, 1, 1, 1, 0, 0, 0, 0])) >>> [len(bs) for bs in list(bs / 3)] [2, 3, 3]
When the second parameter is a set containing a single integer, this instance is split into parts of the size specified by that integer.
>>> bs = bits(map(bit, [1, 1, 1, 1, 0, 0, 0, 0])) >>> bss = list(bs / {2}) >>> [[b.value for b in bs] for bs in bss] [[1, 1], [1, 1], [0, 0], [0, 0]] >>> bs = bits(map(bit, [1, 1, 1, 1, 0, 0, 0, 0])) >>> bss = list(bs / {1}) >>> [[b.value for b in bs] for bs in bss] [[1], [1], [1], [1], [0], [0], [0], [0]]
When the second parameter is a list of integers, this instance is split into parts the sizes of which correspond to the entries (in order) of the list of integers.
>>> bs = bits(map(bit, [1, 1, 1, 1, 0, 0, 0, 0])) >>> bss = list(bs / [1, 3, 4]) >>> [[b.value for b in bs] for bs in bss] [[1], [1, 1, 1], [0, 0, 0, 0]]
- __add__(other: Union[circuitry.circuitry.bits, Sequence[int]]) circuitry.circuitry.bits [source]
Overloaded operator for concatenating bit vectors.
>>> bs = bits(map(bit, [1, 1])) + bits(map(bit, [0, 0])) >>> [b.value for b in bs] [1, 1, 0, 0]
- __pow__(other: Union[circuitry.circuitry.bits, Sequence[int]]) circuitry.circuitry.bits [source]
Overloaded operator for concatenating bit vectors.
>>> bs = bits(map(bit, [1, 1])) ** bits(map(bit, [0, 0])) >>> [b.value for b in bs] [1, 1, 0, 0]
- circuitry.circuitry.constants(l: Sequence[int]) circuitry.circuitry.bits [source]
Synonym for concisely constructing a vector of
bit
objects that are designated as constants.>>> bit.circuit(circuit()) >>> xs = constants([0, 0, 0]) >>> ys = outputs(xs.not_()) >>> int(ys) 7 >>> _ = bit.circuit() # Remove designated circuit.
- circuitry.circuitry.inputs(l: Sequence[int]) circuitry.circuitry.bits [source]
Synonym for concisely constructing a vector of
bit
objects that are designated as inputs.>>> results = [] >>> bit.circuit(circuit()) >>> xs = inputs([1, 1, 1]) >>> ys = outputs(xs.not_()) >>> ns = [int(y) for y in ys] >>> c = bit.circuit() >>> ns == c.evaluate([1, 1, 1]) True
- circuitry.circuitry.outputs(l: Sequence[int]) circuitry.circuitry.bits [source]
Synonym for concisely constructing a vector of
bit
objects that are designated as outputs.>>> bit.circuit(circuit()) >>> xs = bits.zeros(3) >>> ys = outputs(xs.not_()) >>> [y.value for y in ys] [1, 1, 1] >>> _ = bit.circuit() # Remove designated circuit.
- circuitry.circuitry.synthesize(function: Callable, in_type=None, out_type=None) Callable [source]
Decorator for automatically synthesizing a circuit from a function that takes only
bit
and/orbits
objects as its arguments and returns an output of typebit
orbits
(or a tuple or list thereof).In the example below, a function
equal
is defined that determines whether two bits are equivalent. The use of this decorator causes a circuit that implements this function to be constructed.>>> @synthesize ... def equal(x: bit, y: bit) -> bit: ... return (x & y) | ((1 - x) & (1 - y))
The synthesized
circuit
object is introduced as an attribute of the function and can be evaluated on two bit values (as indicated by the function’s type annotation).>>> [equal.circuit.evaluate([[x], [y]]) for x in (0, 1) for y in (0, 1)] [[[1]], [[0]], [[0]], [[1]]]
Note that the function itself can still be invoked on its own in the usual manner if the supplied inputs are integers or
bit
instances. When the function is invoked in this way, the output of the function corresponds to its output type annotation.>>> equal(0, 1) 0 >>> b = equal(bit(0), bit(1)) >>> isinstance(b, bit) True >>> int(b) 0
This decorator can also be applied to functions that are defined explicitly as operating on bit vectors (in the form of
bits
objects).>>> @synthesize ... def conjunction(xy: bits(2)) -> bits(2): ... return (xy[0], xy[0] & xy[1]) >>> [conjunction.circuit.evaluate([[x, y]]) for x in (0, 1) for y in (0, 1)] [[[0, 0]], [[0, 0]], [[1, 0]], [[1, 1]]]
If the decorated function returns multiple outputs, the output type annotation should indicate this.
>>> @synthesize ... def swap(x: bit, y: bit) -> (bit, bit): ... return (y, x) >>> [swap.circuit.evaluate([[x], [y]]) for x in (0, 1) for y in (0, 1)] [[[0], [0]], [[1], [0]], [[0], [1]], [[1], [1]]]
Functions to which this decorator is applied must have valid type annotations that specify the lengths of the input and output bit vectors.
>>> @synthesize ... def equal(x, y): ... return x & y Traceback (most recent call last): ... ValueError: function must have a type annotation for every argument >>> @synthesize ... def equal(x: bit, y: bit): ... return x & y Traceback (most recent call last): ... ValueError: function must have an output type annotation >>> @synthesize ... def equal(x: 'ABC', y: 'ABC') -> bit: ... return x & y Traceback (most recent call last): ... TypeError: input type annotations must be specified using bit or bits >>> @synthesize ... def swap(x: bit, y: bit) -> ('ABC', 'ABC'): ... return (y, x) Traceback (most recent call last): ... TypeError: output type annotation components must be specified using bit or bits >>> @synthesize ... def swap(x: bit, y: bit) -> 'ABC': ... return (y, x) Traceback (most recent call last): ... TypeError: output type must be specified using bit/bits, or a list/tuple thereof
If an exception occurs during the execution (for the purpose of circuit synthesis) of the decorated function, then synthesis will fail.
>>> @synthesize ... def equal(x: bit, y: bit) -> bit: ... return 1 / 0 # Run-time error. Traceback (most recent call last): ... RuntimeError: automated circuit synthesis failed
To support programmatic synthesis (e.g., of circuit variants of many different sizes from the same function definition), this function can accept input and output type information via two optional parameters.
>>> def conjunction(xy): ... return (xy[0], xy[0] & xy[1]) >>> c = synthesize(conjunction, {'xy': bits(2)}, bits(2)) >>> [c.evaluate([[x, y]]) for x in (0, 1) for y in (0, 1)] [[[0, 0]], [[0, 0]], [[1, 0]], [[1, 1]]]
>>> def swap(x: bit, y: bit) -> (bit, bit): ... return (y, x) >>> c = synthesize(swap, (bit, bit), (bit, bit)) >>> [c.evaluate([[x], [y]]) for x in (0, 1) for y in (0, 1)] [[[0], [0]], [[1], [0]], [[0], [1]], [[1], [1]]]
When synthesizing programmatically, the input type information must be supplied either (1) as a dictionary that maps input type parameter names to types or (2) as a tuple or list (the length of which matches the number of parameters). The output type information must follow the same conventions as an output type annotation.
>>> def equal(x, y): ... return (x & y) | ((1 - x) & (1 - y)) >>> c = synthesize(equal, (bit, bit), bit) >>> [c.evaluate([[x], [y]]) for x in (0, 1) for y in (0, 1)] [[[1]], [[0]], [[0]], [[1]]] >>> def swap(xy): ... return [xy[1], xy[0]] >>> c = synthesize(swap, bits(2), bits(2)) >>> [c.evaluate([[x, y]]) for x in (0, 1) for y in (0, 1)] [[[0, 0]], [[1, 0]], [[0, 1]], [[1, 1]]]
If the supplied type information does not have the correct type or is incomplete, an exception is raised.
>>> c = synthesize(equal, (bit, bit)) Traceback (most recent call last): ... ValueError: must include input and output types when supplying type information via parameters >>> c = synthesize(equal, bits(2), bit) Traceback (most recent call last): ... ValueError: number of input type components does not match number of function arguments >>> c = synthesize(equal, ('ABC', 'ABC'), bit) Traceback (most recent call last): ... TypeError: input type components must be specified using bit or bits >>> c = synthesize(equal, 'bits(2)', bit) Traceback (most recent call last): ... TypeError: input type must be specified using bit/bits, or a dict/list/tuple thereof >>> c = synthesize(equal, {'x': 'ABC', 'y': 'ABC'}, bit) Traceback (most recent call last): ... TypeError: input type components must be specified using bit or bits >>> c = synthesize(equal, (bit, bit), 'ABC') Traceback (most recent call last): ... TypeError: output type must be specified using bit/bits, or a list/tuple thereof >>> c = synthesize(equal, (bit, bit), ('ABC', bit)) Traceback (most recent call last): ... TypeError: output type components must be specified using bit or bits