bfcl module
Python library for working with circuit definitions represented using the Bristol Fashion.
- class bfcl.bfcl.operation(iterable=(), /)
Bases:
logical.logical.logical
Data structure for an individual gate operation. This class is derived from the
logical
class exported by the logical library. This module indirectly imports thelogical
class via theop
synonym defined in the circuit library. See the documentation for thelogical
class for more information on this data structure and how logical operations are represented as tuples of integers.- token_op_pairs = [('LID', (0, 1)), ('INV', (1, 0)), ('FLS', (0, 0, 0, 0)), ('AND', (0, 0, 0, 1)), ('NIM', (0, 0, 1, 0)), ('FST', (0, 0, 1, 1)), ('NIF', (0, 1, 0, 0)), ('SND', (0, 1, 0, 1)), ('XOR', (0, 1, 1, 0)), ('LOR', (0, 1, 1, 1)), ('NOR', (1, 0, 0, 0)), ('XNR', (1, 0, 0, 1)), ('NSD', (1, 0, 1, 0)), ('LIF', (1, 0, 1, 1)), ('NFT', (1, 1, 0, 0)), ('IMP', (1, 1, 0, 1)), ('NND', (1, 1, 1, 0)), ('TRU', (1, 1, 1, 1))]
List of pairs of string representations and corresponding unary/binary operations.
- static parse(token: str) bfcl.bfcl.operation
Parse a Bristol Fashion circuit gate operator token.
>>> operation.parse('AND') (0, 0, 0, 1)
- bfcl.bfcl.op
alias of
bfcl.bfcl.operation
- class bfcl.bfcl.gate(wire_in_count: Optional[int] = None, wire_out_count: Optional[int] = None, wire_in_index: Optional[Sequence[int]] = None, wire_out_index: Optional[Sequence[int]] = None, operation: Optional[bfcl.bfcl.operation] = None)
Bases:
object
Data structure for an individual circuit logic gate.
>>> gate.parse('2 1 0 1 15 AND').emit() '2 1 0 1 15 AND' >>> gate.parse('1 1 100 200 INV').emit() '1 1 100 200 INV'
- static parse(tokens) bfcl.bfcl.gate
Parse a Bristol Fashion gate string or token list.
- class bfcl.bfcl.bfc(raw=None)
Bases:
object
Data structure for circuits represented using the Bristol Fashion. A string representing a circuit that conforms to the Bristol Fashion syntax can be parsed into an instance of this class.
>>> circuit_string = ['7 36', '2 4 4', '1 1'] >>> circuit_string.extend(['2 1 0 1 15 AND', '2 1 2 3 16 AND']) >>> circuit_string.extend(['2 1 15 16 8 AND', '2 1 4 5 22 AND']) >>> circuit_string.extend(['2 1 6 7 23 AND', '2 1 22 23 9 AND']) >>> circuit_string.extend(['2 1 8 9 35 AND']) >>> circuit_string = "\n".join(circuit_string) >>> c = bfc(circuit_string)
The string representation can be recovered from an instance of this class, as well.
>>> c.emit() == circuit_string True >>> for line in c.emit().split("\n"): ... print(line) 7 36 2 4 4 1 1 2 1 0 1 15 AND 2 1 2 3 16 AND 2 1 15 16 8 AND 2 1 4 5 22 AND 2 1 6 7 23 AND 2 1 22 23 9 AND 2 1 8 9 35 AND
We could just add a ‘1 1 35 36 LID’ line, and increment ‘8 16’, but the force_id_outputs is perhaps not as lazy as it could be and performs a full bfc->`circuit_`->`bfc` conversion to get the identity gates, hence the wire renumbering. >>> for line in c.emit(force_id_outputs=True).split(”n”): … print(line) 8 16 2 4 4 1 1 2 1 0 1 8 AND 2 1 2 3 9 AND 2 1 8 9 10 AND 2 1 4 5 11 AND 2 1 6 7 12 AND 2 1 11 12 13 AND 2 1 10 13 14 AND 1 1 14 15 LID
A circuit can also be consructed using an instance of the
circuit
class defined in the circuit library (see the documentation for thecircuit.circuit
method defined as part of this class).Common properties of the circuit can be found in the attributes of an instance.
>>> c.gate_count 7 >>> c.wire_count 36 >>> c.value_in_count 2 >>> c.value_in_length [4, 4] >>> c.value_out_count 1 >>> c.wire_in_count 8 >>> c.wire_in_index [0, 1, 2, 3, 4, 5, 6, 7] >>> c.wire_out_count 1 >>> c.wire_out_index [35]
The individual gates are stored within a list consisting of zero or more instances of the
gate
class.>>> (c.gate[0].wire_in_index, c.gate[0].wire_out_index) ([0, 1], [15]) >>> (c.gate[1].wire_in_index, c.gate[1].wire_out_index) ([2, 3], [16]) >>> (c.gate[2].wire_in_index, c.gate[2].wire_out_index) ([15, 16], [8]) >>> (c.gate[3].wire_in_index, c.gate[3].wire_out_index) ([4, 5], [22]) >>> (c.gate[4].wire_in_index, c.gate[4].wire_out_index) ([6, 7], [23]) >>> (c.gate[5].wire_in_index, c.gate[5].wire_out_index) ([22, 23], [9]) >>> (c.gate[6].wire_in_index, c.gate[6].wire_out_index) ([8, 9], [35]) >>> {c.gate[i].operation for i in range(7)} == {op.and_} True
A circuit can also be evaluated an on a sequence of input bit vectors using the
bfcl.evaluate
method.>>> from itertools import product >>> inputs = list(product(*([[0, 1]]*4))) >>> pairs = product(inputs, inputs) >>> outputs = ([0]*255) + [1] >>> [c.evaluate(p)[0][0] for p in pairs] == outputs True
- circuit(c: Optional[circuit.circuit.circuit] = None) Union[Type[None], circuit.circuit.circuit]
Populate this Bristol Fashion circuit instance using an instance of the
circuit
class defined in the circuit library.>>> c_ = circuit_.circuit() >>> c_.count() 0 >>> g0 = c_.gate(op.id_, is_input=True) >>> g1 = c_.gate(op.id_, is_input=True) >>> g2 = c_.gate(op.and_, [g0, g1]) >>> g3 = c_.gate(op.id_, [g2], is_output=True) >>> c_.count() 4 >>> c = bfc(c_) >>> c.emit().split("\n") ['2 4', '1 2', '1 1', '2 1 0 1 2 AND', '1 1 2 3 LID'] >>> c_reparsed = bfc(bfc(c_).circuit()) >>> c_reparsed.emit().split("\n") ['2 4', '1 2', '1 1', '2 1 0 1 2 AND', '1 1 2 3 LID']
- parse(raw: str)
Parse a string representation of a circuit that conforms to the Bristol Fashion syntax.
>>> s = ['7 36', '2 4 4', '1 1'] >>> s.extend(['2 1 0 1 15 AND', '2 1 2 3 16 AND']) >>> s.extend(['2 1 15 16 8 AND', '2 1 4 5 22 AND']) >>> s.extend(['2 1 6 7 23 AND', '2 1 22 23 9 AND']) >>> s.extend(['2 1 8 9 35 AND']) >>> s = "\n".join(s) >>> c = bfc() >>> c.parse(s) >>> for line in c.emit().split("\n"): ... print(line) 7 36 2 4 4 1 1 2 1 0 1 15 AND 2 1 2 3 16 AND 2 1 15 16 8 AND 2 1 4 5 22 AND 2 1 6 7 23 AND 2 1 22 23 9 AND 2 1 8 9 35 AND
- emit(force_id_outputs=False, progress=lambda _: ...) str
Emit a string representation of a Bristol Fashion circuit definition.
In the example below, a circuit object is first constructed using the circuit library.
>>> c_ = circuit_.circuit() >>> c_.count() 0 >>> g0 = c_.gate(op.id_, is_input=True) >>> g1 = c_.gate(op.id_, is_input=True) >>> g2 = c_.gate(op.and_, [g0, g1]) >>> g3 = c_.gate(op.id_, [g2], is_output=True)
The
c_
object above can be converted into an instance of the classcircuit
.>>> c = bfc(c_)
This method can be used to emit a string representation of an object, where the string conforms to the Bristol Fashion syntax.
>>> c.emit().split("\n") ['2 4', '1 2', '1 1', '2 1 0 1 2 AND', '1 1 2 3 LID']
>>> c.emit(True).split("\n") ['2 4', '1 2', '1 1', '2 1 0 1 2 AND', '1 1 2 3 LID']
- evaluate(inputs: Sequence[Sequence[int]]) Sequence[Sequence[int]]
Evaluate a circuit on a sequence of input bit vectors.
>>> s = ['7 36', '2 4 4', '1 1'] >>> s.extend(['2 1 0 1 15 AND', '2 1 2 3 16 AND']) >>> s.extend(['2 1 15 16 8 AND', '2 1 4 5 22 AND']) >>> s.extend(['2 1 6 7 23 AND', '2 1 22 23 9 AND']) >>> s.extend(['2 1 8 9 35 AND']) >>> c = bfc("\n".join(s)) >>> c.evaluate([[1, 0, 1, 1], [1, 1, 1, 0]]) [[0]] >>> c.evaluate([[1, 1, 1, 1], [1, 1, 1, 1]]) [[1]]
The example below confirms that the circuit
c
defined above has correct behavior when evaluated on all compatible inputs (i.e., inputs consisting of a pair of 4-bit vectors).>>> from itertools import product >>> inputs = list(product(*([[0, 1]]*4))) >>> pairs = product(inputs, inputs) >>> outputs = ([0]*255) + [1] >>> [c.evaluate(p)[0][0] for p in pairs] == outputs True