From b3f6d7466ff6a3956baf5a35fb94a56a59ec1ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Le=20H=C3=A9naff?= Date: Tue, 19 Dec 2023 16:30:06 +0100 Subject: [PATCH] Automatically add files to reference.md --- docs/reference.md | 23 +- docs/reference/index.html | 3252 +++++++++++++++++++++++++++++++++ mkdocs.yaml | 4 + poetry.lock | 89 +- pyproject.toml | 1 + scripts/gen_reference_page.py | 28 + 6 files changed, 3342 insertions(+), 55 deletions(-) create mode 100644 docs/reference/index.html create mode 100644 scripts/gen_reference_page.py diff --git a/docs/reference.md b/docs/reference.md index 63501e20..5a90aee0 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -1,19 +1,24 @@ -::: opensquirrel.circuit + +::: opensquirrel.circuit ::: opensquirrel.circuit_builder - ::: opensquirrel.circuit_matrix_calculator - ::: opensquirrel.common - ::: opensquirrel.default_gates - ::: opensquirrel.gate_library - ::: opensquirrel.mckay_decomposer - +::: opensquirrel.parsing.antlr.generated.CQasm3Lexer +::: opensquirrel.parsing.antlr.generated.CQasm3Listener +::: opensquirrel.parsing.antlr.generated.CQasm3Parser +::: opensquirrel.parsing.antlr.generated.CQasm3Visitor +::: opensquirrel.parsing.antlr.generated.CQasmLexer +::: opensquirrel.parsing.antlr.qubit_range_checker +::: opensquirrel.parsing.antlr.squirrel_error_handler +::: opensquirrel.parsing.antlr.squirrel_ir_creator +::: opensquirrel.parsing.antlr.squirrel_ir_from_string +::: opensquirrel.parsing.antlr.type_checker ::: opensquirrel.replacer - ::: opensquirrel.squirrel_ir - +::: opensquirrel.utils.matrix_expander ::: opensquirrel.writer + diff --git a/docs/reference/index.html b/docs/reference/index.html new file mode 100644 index 00000000..2631bf45 --- /dev/null +++ b/docs/reference/index.html @@ -0,0 +1,3252 @@ + + + + + + + + + + + + + + + + + + + + + Reference - OpenSquirrel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Reference

+ + + + + +
+ + + + +
+ + + +
+ + + + + + + + +
+ + + + +

+ Circuit + + +

+ + +
+ + +

The Circuit class is the only interface to access OpenSquirrel's features.

+ + + +

Examples:

+
>>> c = Circuit.from_string("version 3.0; qubit[3] q; h q[0]")
+>>> c
+version 3.0
+
+qubit[3] q
+
+h q[0]
+
+>>> c.decompose_mckay()
+>>> c
+version 3.0
+
+qubit[3] q
+
+x90 q[0]
+rz q[0], 1.5707963
+x90 q[0]
+
+ +
+ Source code in opensquirrel\circuit.py +
 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ 26
+ 27
+ 28
+ 29
+ 30
+ 31
+ 32
+ 33
+ 34
+ 35
+ 36
+ 37
+ 38
+ 39
+ 40
+ 41
+ 42
+ 43
+ 44
+ 45
+ 46
+ 47
+ 48
+ 49
+ 50
+ 51
+ 52
+ 53
+ 54
+ 55
+ 56
+ 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
class Circuit:
+    """The Circuit class is the only interface to access OpenSquirrel's features.
+
+    Examples:
+        >>> c = Circuit.from_string("version 3.0; qubit[3] q; h q[0]")
+        >>> c
+        version 3.0
+        <BLANKLINE>
+        qubit[3] q
+        <BLANKLINE>
+        h q[0]
+        <BLANKLINE>
+        >>> c.decompose_mckay()
+        >>> c
+        version 3.0
+        <BLANKLINE>
+        qubit[3] q
+        <BLANKLINE>
+        x90 q[0]
+        rz q[0], 1.5707963
+        x90 q[0]
+        <BLANKLINE>
+    """
+
+    def __init__(self, squirrel_ir: SquirrelIR):
+        """Create a circuit object from a SquirrelIR object."""
+
+        self.squirrel_ir = squirrel_ir
+
+    @classmethod
+    def from_string(
+        cls,
+        cqasm3_string: str,
+        gate_set: [Callable[..., Gate]] = default_gate_set,
+        gate_aliases: Dict[str, Callable[..., Gate]] = default_gate_aliases,
+    ):
+        """Create a circuit object from a cQasm3 string. All the gates in the circuit need to be defined in
+        the `gates` argument.
+
+        * type-checking is performed, eliminating qubit indices errors and incoherencies
+        * checks that used gates are supported and mentioned in `gates` with appropriate signatures
+        * does not support map or variables, and other things...
+        * for example of `gates` dictionary, please look at TestGates.py
+        """
+
+        return Circuit(squirrel_ir_from_string(cqasm3_string, gate_set=gate_set, gate_aliases=gate_aliases))
+
+    @property
+    def number_of_qubits(self) -> int:
+        return self.squirrel_ir.number_of_qubits
+
+    @property
+    def qubit_register_name(self) -> str:
+        return self.squirrel_ir.qubit_register_name
+
+    def decompose_mckay(self):
+        """Perform gate fusion on all one-qubit gates and decompose them in the McKay style.
+
+        * all one-qubit gates on same qubit are merged together, without attempting to commute any gate
+        * two-or-more-qubit gates are left as-is
+        * merged one-qubit gates are decomposed according to McKay decomposition, that is:
+                gate   ---->    Rz.Rx(pi/2).Rz.Rx(pi/2).Rz
+        * _global phase is deemed irrelevant_, therefore a simulator backend might produce different output
+            for the input and output circuit - those outputs should be equivalent modulo global phase.
+        """
+
+        self.squirrel_ir = mckay_decomposer.decompose_mckay(self.squirrel_ir)  # FIXME: inplace
+
+    def replace(self, gate_name: str, f):
+        """Manually replace occurrences of a given gate with a list of gates.
+
+        * this can be called decomposition - but it's the least fancy version of it
+        * function parameter gives the decomposition based on parameters of original gate
+        """
+
+        replacer.replace(self.squirrel_ir, gate_name, f)
+
+    def test_get_circuit_matrix(self) -> np.ndarray:
+        """Get the (large) unitary matrix corresponding to the circuit.
+
+        * this matrix has 4**n elements, where n is the number of qubits
+        * therefore this function is only here for testing purposes on small number of qubits
+        * result is stored as a numpy array of complex numbers
+        """
+
+        return circuit_matrix_calculator.get_circuit_matrix(self.squirrel_ir)
+
+    def __repr__(self) -> str:
+        """Write the circuit to a cQasm3 string.
+
+        * comments are removed
+        """
+
+        return writer.squirrel_ir_to_string(self.squirrel_ir)
+
+
+ + + +
+ + + + + + + + + + +
+ + + + +

+ __init__(squirrel_ir) + +

+ + +
+ +

Create a circuit object from a SquirrelIR object.

+ +
+ Source code in opensquirrel\circuit.py +
35
+36
+37
+38
def __init__(self, squirrel_ir: SquirrelIR):
+    """Create a circuit object from a SquirrelIR object."""
+
+    self.squirrel_ir = squirrel_ir
+
+
+
+ +
+ + +
+ + + + +

+ __repr__() + +

+ + +
+ +

Write the circuit to a cQasm3 string.

+
    +
  • comments are removed
  • +
+ +
+ Source code in opensquirrel\circuit.py +
 98
+ 99
+100
+101
+102
+103
+104
def __repr__(self) -> str:
+    """Write the circuit to a cQasm3 string.
+
+    * comments are removed
+    """
+
+    return writer.squirrel_ir_to_string(self.squirrel_ir)
+
+
+
+ +
+ + +
+ + + + +

+ decompose_mckay() + +

+ + +
+ +

Perform gate fusion on all one-qubit gates and decompose them in the McKay style.

+
    +
  • all one-qubit gates on same qubit are merged together, without attempting to commute any gate
  • +
  • two-or-more-qubit gates are left as-is
  • +
  • merged one-qubit gates are decomposed according to McKay decomposition, that is: + gate ----> Rz.Rx(pi/2).Rz.Rx(pi/2).Rz
  • +
  • global phase is deemed irrelevant, therefore a simulator backend might produce different output + for the input and output circuit - those outputs should be equivalent modulo global phase.
  • +
+ +
+ Source code in opensquirrel\circuit.py +
66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
def decompose_mckay(self):
+    """Perform gate fusion on all one-qubit gates and decompose them in the McKay style.
+
+    * all one-qubit gates on same qubit are merged together, without attempting to commute any gate
+    * two-or-more-qubit gates are left as-is
+    * merged one-qubit gates are decomposed according to McKay decomposition, that is:
+            gate   ---->    Rz.Rx(pi/2).Rz.Rx(pi/2).Rz
+    * _global phase is deemed irrelevant_, therefore a simulator backend might produce different output
+        for the input and output circuit - those outputs should be equivalent modulo global phase.
+    """
+
+    self.squirrel_ir = mckay_decomposer.decompose_mckay(self.squirrel_ir)  # FIXME: inplace
+
+
+
+ +
+ + +
+ + + + +

+ from_string(cqasm3_string, gate_set=default_gate_set, gate_aliases=default_gate_aliases) + + + classmethod + + +

+ + +
+ +

Create a circuit object from a cQasm3 string. All the gates in the circuit need to be defined in +the gates argument.

+
    +
  • type-checking is performed, eliminating qubit indices errors and incoherencies
  • +
  • checks that used gates are supported and mentioned in gates with appropriate signatures
  • +
  • does not support map or variables, and other things...
  • +
  • for example of gates dictionary, please look at TestGates.py
  • +
+ +
+ Source code in opensquirrel\circuit.py +
40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
@classmethod
+def from_string(
+    cls,
+    cqasm3_string: str,
+    gate_set: [Callable[..., Gate]] = default_gate_set,
+    gate_aliases: Dict[str, Callable[..., Gate]] = default_gate_aliases,
+):
+    """Create a circuit object from a cQasm3 string. All the gates in the circuit need to be defined in
+    the `gates` argument.
+
+    * type-checking is performed, eliminating qubit indices errors and incoherencies
+    * checks that used gates are supported and mentioned in `gates` with appropriate signatures
+    * does not support map or variables, and other things...
+    * for example of `gates` dictionary, please look at TestGates.py
+    """
+
+    return Circuit(squirrel_ir_from_string(cqasm3_string, gate_set=gate_set, gate_aliases=gate_aliases))
+
+
+
+ +
+ + +
+ + + + +

+ replace(gate_name, f) + +

+ + +
+ +

Manually replace occurrences of a given gate with a list of gates.

+
    +
  • this can be called decomposition - but it's the least fancy version of it
  • +
  • function parameter gives the decomposition based on parameters of original gate
  • +
+ +
+ Source code in opensquirrel\circuit.py +
79
+80
+81
+82
+83
+84
+85
+86
def replace(self, gate_name: str, f):
+    """Manually replace occurrences of a given gate with a list of gates.
+
+    * this can be called decomposition - but it's the least fancy version of it
+    * function parameter gives the decomposition based on parameters of original gate
+    """
+
+    replacer.replace(self.squirrel_ir, gate_name, f)
+
+
+
+ +
+ + +
+ + + + +

+ test_get_circuit_matrix() + +

+ + +
+ +

Get the (large) unitary matrix corresponding to the circuit.

+
    +
  • this matrix has 4**n elements, where n is the number of qubits
  • +
  • therefore this function is only here for testing purposes on small number of qubits
  • +
  • result is stored as a numpy array of complex numbers
  • +
+ +
+ Source code in opensquirrel\circuit.py +
88
+89
+90
+91
+92
+93
+94
+95
+96
def test_get_circuit_matrix(self) -> np.ndarray:
+    """Get the (large) unitary matrix corresponding to the circuit.
+
+    * this matrix has 4**n elements, where n is the number of qubits
+    * therefore this function is only here for testing purposes on small number of qubits
+    * result is stored as a numpy array of complex numbers
+    """
+
+    return circuit_matrix_calculator.get_circuit_matrix(self.squirrel_ir)
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + +
+ + + + +

+ CircuitBuilder + + +

+ + +
+

+ Bases: GateLibrary

+ + +

A class using the builder pattern to make construction of circuits easy from Python. +Adds corresponding gate when a method is called. Checks gates are known and called with the right arguments. +Mainly here to allow for Qiskit-style circuit construction:

+
+
+
+

CircuitBuilder(number_of_qubits=3).h(Qubit(0)).cnot(Qubit(0), Qubit(1)).cnot(Qubit(0), Qubit(2)).to_circuit() +version 3.0 + +qubit[3] q + +h q[0] +cnot q[0], q[1] +cnot q[0], q[2] +

+
+
+
+ +
+ Source code in opensquirrel\circuit_builder.py +
10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
class CircuitBuilder(GateLibrary):
+    """
+    A class using the builder pattern to make construction of circuits easy from Python.
+    Adds corresponding gate when a method is called. Checks gates are known and called with the right arguments.
+    Mainly here to allow for Qiskit-style circuit construction:
+
+    >>> CircuitBuilder(number_of_qubits=3).h(Qubit(0)).cnot(Qubit(0), Qubit(1)).cnot(Qubit(0), Qubit(2)).to_circuit()
+    version 3.0
+    <BLANKLINE>
+    qubit[3] q
+    <BLANKLINE>
+    h q[0]
+    cnot q[0], q[1]
+    cnot q[0], q[2]
+    <BLANKLINE>
+    """
+
+    _default_qubit_register_name = "q"
+
+    def __init__(
+        self,
+        number_of_qubits: int,
+        gate_set: [Callable[..., Gate]] = default_gate_set,
+        gate_aliases: Dict[str, Callable[..., Gate]] = default_gate_aliases,
+    ):
+        GateLibrary.__init__(self, gate_set, gate_aliases)
+        self.squirrel_ir = SquirrelIR(
+            number_of_qubits=number_of_qubits, qubit_register_name=self._default_qubit_register_name
+        )
+
+    def __getattr__(self, attr):
+        def add_comment(comment_string: str):
+            self.squirrel_ir.add_comment(Comment(comment_string))
+            return self
+
+        def add_this_gate(*args):
+            generator_f = GateLibrary.get_gate_f(self, attr)
+
+            for i, par in enumerate(inspect.signature(generator_f).parameters.values()):
+                if not isinstance(args[i], par.annotation):
+                    raise TypeError(
+                        f"Wrong argument type for gate `{attr}`, got {type(args[i])} but expected {par.annotation}"
+                    )
+
+            self.squirrel_ir.add_gate(generator_f(*args))
+            return self
+
+        return add_comment if attr == "comment" else add_this_gate
+
+    def to_circuit(self) -> Circuit:
+        return Circuit(self.squirrel_ir)
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + +
+ + + + +

+ get_circuit_matrix(squirrel_ir) + +

+ + +
+ +

Compute the Numpy unitary matrix corresponding to the circuit. +The size of this matrix grows exponentially with the number of qubits.

+ +
+ Source code in opensquirrel\circuit_matrix_calculator.py +
20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
def get_circuit_matrix(squirrel_ir: SquirrelIR):
+    """
+    Compute the Numpy unitary matrix corresponding to the circuit.
+    The size of this matrix grows exponentially with the number of qubits.
+    """
+
+    impl = _CircuitMatrixCalculator(squirrel_ir.number_of_qubits)
+
+    squirrel_ir.accept(impl)
+
+    return impl.matrix
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + +
+ + + + +

+ QubitRangeChecker + + +

+ + +
+

+ Bases: CQasm3Visitor

+ + +

This class checks that all qubit indices make sense in an ANTLR parse tree. +It is an instance of the ANTLR abstract syntax tree visitor class. +Therefore, method names are fixed and based on rule names in the Grammar .g4 file.

+ +
+ Source code in opensquirrel\parsing\antlr\qubit_range_checker.py +
 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
class QubitRangeChecker(CQasm3Visitor.CQasm3Visitor):
+    """
+    This class checks that all qubit indices make sense in an ANTLR parse tree.
+    It is an instance of the ANTLR abstract syntax tree visitor class.
+    Therefore, method names are fixed and based on rule names in the Grammar .g4 file.
+    """
+
+    def __init__(self):
+        self.number_of_qubits = 0
+
+    def visitProg(self, ctx):
+        self.visit(ctx.qubitRegisterDeclaration())
+        for gate in ctx.gateApplication():
+            self.visit(gate)
+
+    def visitQubitRegisterDeclaration(self, ctx):
+        self.number_of_qubits = int(str(ctx.INT()))
+
+    def visitGateApplication(self, ctx):
+        visited_args = (self.visit(arg) for arg in ctx.expr())
+        qubit_argument_sizes = [qubit_range_size for qubit_range_size in visited_args if qubit_range_size is not None]
+
+        if len(qubit_argument_sizes) > 0 and not all(s == qubit_argument_sizes[0] for s in qubit_argument_sizes):
+            raise Exception("Invalid gate call with qubit arguments of different sizes")
+
+    def visitQubit(self, ctx):
+        qubit_index = int(str(ctx.INT()))
+        if qubit_index >= self.number_of_qubits:
+            raise Exception(f"Qubit index {qubit_index} out of range")
+
+        return 1
+
+    def visitQubits(self, ctx):
+        qubit_indices = list(map(int, map(str, ctx.INT())))
+        for qubit_index in qubit_indices:
+            if qubit_index >= self.number_of_qubits:
+                raise Exception(f"Qubit index {qubit_index} out of range")
+
+        return len(qubit_indices)
+
+    def visitQubitRange(self, ctx):
+        first_qubit_index = int(str(ctx.INT(0)))
+        last_qubit_index = int(str(ctx.INT(1)))
+
+        if first_qubit_index > last_qubit_index:
+            raise Exception(f"Qubit index range {first_qubit_index}:{last_qubit_index} malformed")
+
+        if max(first_qubit_index, last_qubit_index) >= self.number_of_qubits:
+            raise Exception(f"Qubit index range {first_qubit_index}:{last_qubit_index} out of range")
+
+        return last_qubit_index - first_qubit_index + 1
+
+    def visitIntLiteral(self, ctx):
+        return None
+
+    def visitNegatedIntLiteral(self, ctx):
+        return None
+
+    def visitFloatLiteral(self, ctx):
+        return None
+
+    def visitNegatedFloatLiteral(self, ctx):
+        return None
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + +
+ + + + +

+ SquirrelIRCreator + + +

+ + +
+

+ Bases: GateLibrary, CQasm3Visitor

+ + +

This class creates a SquirrelIR object from an ANTLR parse tree. +It is an instance of the ANTLR abstract syntax tree visitor class. +Therefore, method names are fixed and based on rule names in the Grammar .g4 file.

+ +
+ Source code in opensquirrel\parsing\antlr\squirrel_ir_creator.py +
 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
class SquirrelIRCreator(GateLibrary, CQasm3Visitor.CQasm3Visitor):
+    """
+    This class creates a SquirrelIR object from an ANTLR parse tree.
+    It is an instance of the ANTLR abstract syntax tree visitor class.
+    Therefore, method names are fixed and based on rule names in the Grammar .g4 file.
+    """
+
+    def __init__(self, gate_set=default_gate_set, gate_aliases=default_gate_aliases):
+        GateLibrary.__init__(self, gate_set, gate_aliases)
+        self.squirrel_ir = None
+
+    def visitProg(self, ctx):
+        number_of_qubits, qubit_register_name = self.visit(ctx.qubitRegisterDeclaration())
+
+        self.squirrel_ir = SquirrelIR(number_of_qubits=number_of_qubits, qubit_register_name=qubit_register_name)
+
+        for gate_application in ctx.gateApplication():
+            self.visit(gate_application)
+
+        return self.squirrel_ir
+
+    def visitGateApplication(self, ctx):
+        gate_name = str(ctx.ID())
+
+        generator_f = GateLibrary.get_gate_f(self, gate_name)
+        parameters = inspect.signature(generator_f).parameters
+
+        number_of_operands = next(
+            len(self.visit(ctx.expr(i))) for i, par in enumerate(parameters.values()) if par.annotation == Qubit
+        )
+
+        # The below is for handling e.g. `cr q[1:3], q[5:7], 1.23`
+        expanded_args = [
+            self.visit(ctx.expr(i)) if par.annotation == Qubit else [self.visit(ctx.expr(i))] * number_of_operands
+            for i, par in enumerate(parameters.values())
+        ]
+
+        for individual_args in zip(*expanded_args):
+            self.squirrel_ir.add_gate(generator_f(*individual_args))
+
+    def visitQubitRegisterDeclaration(self, ctx):
+        return int(str(ctx.INT())), str(ctx.ID())
+
+    def visitQubit(self, ctx):
+        return [Qubit(int(str(ctx.INT())))]
+
+    def visitQubits(self, ctx):
+        return list(map(Qubit, map(int, map(str, ctx.INT()))))
+
+    def visitQubitRange(self, ctx):
+        first_qubit_index = int(str(ctx.INT(0)))
+        last_qubit_index = int(str(ctx.INT(1)))
+        return list(map(Qubit, range(first_qubit_index, last_qubit_index + 1)))
+
+    def visitFloatLiteral(self, ctx):
+        return Float(float(str(ctx.FLOAT())))
+
+    def visitNegatedFloatLiteral(self, ctx):
+        return Float(-float(str(ctx.FLOAT())))
+
+    def visitIntLiteral(self, ctx):
+        return Int(int(str(ctx.INT())))
+
+    def visitNegatedIntLiteral(self, ctx):
+        return Int(-int(str(ctx.INT())))
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + +
+ + + + +

+ squirrel_ir_from_string(s, gate_set, gate_aliases) + +

+ + +
+ +

ANTLR parsing entrypoint. +Performs type checking based on provided gate semantics and check that the qubit indices are valid. +Creates the IR where each gate node is mapped to its semantic function and arguments.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
gate_set + +
+

The set of supported gate semantics.

+
+
+ required +
gate_aliases + +
+

Dictionary mapping extra gate names to their semantic.

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ +
+

A corresponding SquirrelIR object. Throws in case of parsing error.

+
+
+ +
+ Source code in opensquirrel\parsing\antlr\squirrel_ir_from_string.py +
35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
def squirrel_ir_from_string(s: str, gate_set, gate_aliases):
+    """
+    ANTLR parsing entrypoint.
+    Performs type checking based on provided gate semantics and check that the qubit indices are valid.
+    Creates the IR where each gate node is mapped to its semantic function and arguments.
+
+    Args:
+        gate_set: The set of supported gate semantics.
+        gate_aliases: Dictionary mapping extra gate names to their semantic.
+
+    Returns:
+        A corresponding SquirrelIR object. Throws in case of parsing error.
+    """
+    tree = antlr_tree_from_string(s)
+
+    type_check_antlr_tree(tree, gate_set=gate_set, gate_aliases=gate_aliases)
+
+    check_qubit_ranges_of_antlr_tree(tree)
+
+    squirrel_ir_creator = SquirrelIRCreator(gate_set=gate_set, gate_aliases=gate_aliases)
+
+    return squirrel_ir_creator.visit(tree)
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + +
+ + + + +

+ TypeChecker + + +

+ + +
+

+ Bases: GateLibrary, CQasm3Visitor

+ + +

This class checks that all gate parameter types make sense in an ANTLR parse tree. +It is an instance of the ANTLR abstract syntax tree visitor class. +Therefore, method names are fixed and based on rule names in the Grammar .g4 file.

+ +
+ Source code in opensquirrel\parsing\antlr\type_checker.py +
 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
class TypeChecker(GateLibrary, CQasm3Visitor.CQasm3Visitor):
+    """
+    This class checks that all gate parameter types make sense in an ANTLR parse tree.
+    It is an instance of the ANTLR abstract syntax tree visitor class.
+    Therefore, method names are fixed and based on rule names in the Grammar .g4 file.
+    """
+
+    def __init__(self, gate_set=default_gate_set, gate_aliases=default_gate_aliases):
+        GateLibrary.__init__(self, gate_set, gate_aliases)
+        self.register_name = None
+
+    def visitProg(self, ctx):
+        self.visit(ctx.qubitRegisterDeclaration())
+        for gate in ctx.gateApplication():
+            self.visit(gate)
+
+    def visitQubitRegisterDeclaration(self, ctx):
+        self.register_name = str(ctx.ID())
+
+    def visitGateApplication(self, ctx):
+        # Check that the types of the operands match the gate generator function.
+        gate_name = str(ctx.ID())
+        generator_f = GateLibrary.get_gate_f(self, gate_name)
+
+        parameters = inspect.signature(generator_f).parameters
+
+        if len(ctx.expr()) > len(parameters):
+            raise Exception(f"Gate `{gate_name}` takes {len(parameters)} arguments, but {len(ctx.expr())} were given!")
+
+        for i, param in enumerate(parameters.values()):
+            actual_type = self.visit(ctx.expr(i))
+            expected_type = param.annotation
+            if actual_type != expected_type:
+                raise Exception(
+                    f"Argument #{i} passed to gate `{gate_name}` is of type"
+                    f" {actual_type} but should be {expected_type}"
+                )
+
+    def visitQubit(self, ctx):
+        if str(ctx.ID()) != self.register_name:
+            raise Exception(f"Qubit register {str(ctx.ID())} not declared")
+
+        return Qubit
+
+    def visitQubits(self, ctx):
+        if str(ctx.ID()) != self.register_name:
+            raise Exception(f"Qubit register {str(ctx.ID())} not declared")
+
+        return Qubit
+
+    def visitQubitRange(self, ctx):
+        if str(ctx.ID()) != self.register_name:
+            raise Exception(f"Qubit register {str(ctx.ID())} not declared")
+
+        return Qubit
+
+    def visitIntLiteral(self, ctx):
+        return Int
+
+    def visitNegatedIntLiteral(self, ctx):
+        return Int
+
+    def visitFloatLiteral(self, ctx):
+        return Float
+
+    def visitNegatedFloatLiteral(self, ctx):
+        return Float
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + +
+ + + + +

+ expand_ket(base_ket, reduced_ket, qubits) + +

+ + +
+ +

Given a base quantum ket on n qubits and a reduced ket on a subset of those qubits, this computes the expanded ket +where the reduction qubits and the other qubits are set based on the reduced ket and the base ket, respectively. +Roughly equivalent to the pdep assembly instruction (bits deposit).

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
base_ket + int + +
+

A quantum ket, represented by its corresponding non-negative integer. + By convention, qubit #0 corresponds to the least significant bit.

+
+
+ required +
reduced_ket + int + +
+

A quantum ket, represented by its corresponding non-negative integer. + By convention, qubit #0 corresponds to the least significant bit.

+
+
+ required +
qubits + List[Qubit] + +
+

The indices of the qubits to expand from the reduced ket. Order matters.

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

The non-negative integer corresponding to the expanded ket.

+
+
+ + + +

Examples:

+
>>> expand_ket(0b00000, 0b0, [Qubit(5)])   # 0b000000
+0
+>>> expand_ket(0b00000, 0b1, [Qubit(5)])   # 0b100000
+32
+>>> expand_ket(0b00111, 0b0, [Qubit(5)])   # 0b000111
+7
+>>> expand_ket(0b00111, 0b1, [Qubit(5)])   # 0b100111
+39
+>>> expand_ket(0b0000, 0b000, [Qubit(1), Qubit(2), Qubit(3)])    # 0b0000
+0
+>>> expand_ket(0b0000, 0b001, [Qubit(1), Qubit(2), Qubit(3)])    # 0b0010
+2
+>>> expand_ket(0b0000, 0b011, [Qubit(1), Qubit(2), Qubit(3)])    # 0b0110
+6
+>>> expand_ket(0b0000, 0b101, [Qubit(1), Qubit(2), Qubit(3)])   # 0b1010
+10
+>>> expand_ket(0b0001, 0b101, [Qubit(1), Qubit(2), Qubit(3)])   # 0b1011
+11
+
+ +
+ Source code in opensquirrel\utils\matrix_expander.py +
45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
def expand_ket(base_ket: int, reduced_ket: int, qubits: List[Qubit]) -> int:
+    """
+    Given a base quantum ket on n qubits and a reduced ket on a subset of those qubits, this computes the expanded ket
+    where the reduction qubits and the other qubits are set based on the reduced ket and the base ket, respectively.
+    Roughly equivalent to the `pdep` assembly instruction (bits deposit).
+
+    Args:
+        base_ket: A quantum ket, represented by its corresponding non-negative integer.
+                  By convention, qubit #0 corresponds to the least significant bit.
+        reduced_ket: A quantum ket, represented by its corresponding non-negative integer.
+                     By convention, qubit #0 corresponds to the least significant bit.
+        qubits: The indices of the qubits to expand from the reduced ket. Order matters.
+
+    Returns:
+        The non-negative integer corresponding to the expanded ket.
+
+    Examples:
+        >>> expand_ket(0b00000, 0b0, [Qubit(5)])   # 0b000000
+        0
+        >>> expand_ket(0b00000, 0b1, [Qubit(5)])   # 0b100000
+        32
+        >>> expand_ket(0b00111, 0b0, [Qubit(5)])   # 0b000111
+        7
+        >>> expand_ket(0b00111, 0b1, [Qubit(5)])   # 0b100111
+        39
+        >>> expand_ket(0b0000, 0b000, [Qubit(1), Qubit(2), Qubit(3)])    # 0b0000
+        0
+        >>> expand_ket(0b0000, 0b001, [Qubit(1), Qubit(2), Qubit(3)])    # 0b0010
+        2
+        >>> expand_ket(0b0000, 0b011, [Qubit(1), Qubit(2), Qubit(3)])    # 0b0110
+        6
+        >>> expand_ket(0b0000, 0b101, [Qubit(1), Qubit(2), Qubit(3)])   # 0b1010
+        10
+        >>> expand_ket(0b0001, 0b101, [Qubit(1), Qubit(2), Qubit(3)])   # 0b1011
+        11
+    """
+    expanded_ket = base_ket
+    for i, qubit in enumerate(qubits):
+        expanded_ket &= ~(1 << qubit.index)  # Erase bit.
+        expanded_ket |= ((reduced_ket & (1 << i)) >> i) << qubit.index  # Set bit to value from reduced_ket.
+
+    return expanded_ket
+
+
+
+ +
+ + +
+ + + + +

+ get_matrix(gate, number_of_qubits) + +

+ + +
+ +

Compute the unitary matrix corresponding to the gate applied to those qubit operands, taken among any number of qubits. +This can be used for, e.g., +- testing, +- permuting the operands of multi-qubit gates, +- simulating a circuit (simulation in this way is inefficient for large numbers of qubits).

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
gate + Gate + +
+

The gate, including the qubits on which it is operated on.

+
+
+ required +
number_of_qubits + int + +
+

The total number of qubits.

+
+
+ required +
+ + + +

Examples:

+
>>> X = lambda q: BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi, phase=math.pi / 2)
+>>> get_matrix(X(Qubit(1)), 2).astype(int)           # X q[1]
+array([[0, 0, 1, 0],
+       [0, 0, 0, 1],
+       [1, 0, 0, 0],
+       [0, 1, 0, 0]])
+
+
>>> CNOT02 = ControlledGate(Qubit(0), X(Qubit(2)))
+>>> get_matrix(CNOT02, 3).astype(int)     # CNOT q[0], q[2]
+array([[1, 0, 0, 0, 0, 0, 0, 0],
+       [0, 0, 0, 0, 0, 1, 0, 0],
+       [0, 0, 1, 0, 0, 0, 0, 0],
+       [0, 0, 0, 0, 0, 0, 0, 1],
+       [0, 0, 0, 0, 1, 0, 0, 0],
+       [0, 1, 0, 0, 0, 0, 0, 0],
+       [0, 0, 0, 0, 0, 0, 1, 0],
+       [0, 0, 0, 1, 0, 0, 0, 0]])
+>>> get_matrix(ControlledGate(Qubit(1), X(Qubit(2))), 3).astype(int)     # CNOT q[1], q[2]
+array([[1, 0, 0, 0, 0, 0, 0, 0],
+       [0, 1, 0, 0, 0, 0, 0, 0],
+       [0, 0, 0, 0, 0, 0, 1, 0],
+       [0, 0, 0, 0, 0, 0, 0, 1],
+       [0, 0, 0, 0, 1, 0, 0, 0],
+       [0, 0, 0, 0, 0, 1, 0, 0],
+       [0, 0, 1, 0, 0, 0, 0, 0],
+       [0, 0, 0, 1, 0, 0, 0, 0]])
+
+ +
+ Source code in opensquirrel\utils\matrix_expander.py +
143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
def get_matrix(gate: Gate, number_of_qubits: int) -> np.ndarray:
+    """
+    Compute the unitary matrix corresponding to the gate applied to those qubit operands, taken among any number of qubits.
+    This can be used for, e.g.,
+    - testing,
+    - permuting the operands of multi-qubit gates,
+    - simulating a circuit (simulation in this way is inefficient for large numbers of qubits).
+
+    Args:
+        gate: The gate, including the qubits on which it is operated on.
+        number_of_qubits: The total number of qubits.
+
+    Examples:
+        >>> X = lambda q: BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi, phase=math.pi / 2)
+        >>> get_matrix(X(Qubit(1)), 2).astype(int)           # X q[1]
+        array([[0, 0, 1, 0],
+               [0, 0, 0, 1],
+               [1, 0, 0, 0],
+               [0, 1, 0, 0]])
+
+        >>> CNOT02 = ControlledGate(Qubit(0), X(Qubit(2)))
+        >>> get_matrix(CNOT02, 3).astype(int)     # CNOT q[0], q[2]
+        array([[1, 0, 0, 0, 0, 0, 0, 0],
+               [0, 0, 0, 0, 0, 1, 0, 0],
+               [0, 0, 1, 0, 0, 0, 0, 0],
+               [0, 0, 0, 0, 0, 0, 0, 1],
+               [0, 0, 0, 0, 1, 0, 0, 0],
+               [0, 1, 0, 0, 0, 0, 0, 0],
+               [0, 0, 0, 0, 0, 0, 1, 0],
+               [0, 0, 0, 1, 0, 0, 0, 0]])
+        >>> get_matrix(ControlledGate(Qubit(1), X(Qubit(2))), 3).astype(int)     # CNOT q[1], q[2]
+        array([[1, 0, 0, 0, 0, 0, 0, 0],
+               [0, 1, 0, 0, 0, 0, 0, 0],
+               [0, 0, 0, 0, 0, 0, 1, 0],
+               [0, 0, 0, 0, 0, 0, 0, 1],
+               [0, 0, 0, 0, 1, 0, 0, 0],
+               [0, 0, 0, 0, 0, 1, 0, 0],
+               [0, 0, 1, 0, 0, 0, 0, 0],
+               [0, 0, 0, 1, 0, 0, 0, 0]])
+    """
+
+    expander = MatrixExpander(number_of_qubits)
+    return gate.accept(expander)
+
+
+
+ +
+ + +
+ + + + +

+ get_reduced_ket(ket, qubits) + +

+ + +
+ +

Given a quantum ket represented by its corresponding base-10 integer, this computes the reduced ket +where only the given qubits appear, in order. +Roughly equivalent to the pext assembly instruction (bits extraction).

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
ket + int + +
+

A quantum ket, represented by its corresponding non-negative integer. + By convention, qubit #0 corresponds to the least significant bit.

+
+
+ required +
qubits + List[Qubit] + +
+

The indices of the qubits to extract. Order matters.

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

The non-negative integer corresponding to the reduced ket.

+
+
+ + + +

Examples:

+
>>> get_reduced_ket(1, [Qubit(0)])         # 0b01
+1
+>>> get_reduced_ket(1111, [Qubit(2)])      # 0b01
+1
+>>> get_reduced_ket(1111, [Qubit(5)])      # 0b0
+0
+>>> get_reduced_ket(1111, [Qubit(2), Qubit(5)])   # 0b01
+1
+>>> get_reduced_ket(101, [Qubit(1), Qubit(0)])    # 0b10
+2
+>>> get_reduced_ket(101, [Qubit(0), Qubit(1)])    # 0b01
+1
+
+ +
+ Source code in opensquirrel\utils\matrix_expander.py +
10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
def get_reduced_ket(ket: int, qubits: List[Qubit]) -> int:
+    """
+    Given a quantum ket represented by its corresponding base-10 integer, this computes the reduced ket
+    where only the given qubits appear, in order.
+    Roughly equivalent to the `pext` assembly instruction (bits extraction).
+
+    Args:
+        ket: A quantum ket, represented by its corresponding non-negative integer.
+             By convention, qubit #0 corresponds to the least significant bit.
+        qubits: The indices of the qubits to extract. Order matters.
+
+    Returns:
+        The non-negative integer corresponding to the reduced ket.
+
+    Examples:
+        >>> get_reduced_ket(1, [Qubit(0)])         # 0b01
+        1
+        >>> get_reduced_ket(1111, [Qubit(2)])      # 0b01
+        1
+        >>> get_reduced_ket(1111, [Qubit(5)])      # 0b0
+        0
+        >>> get_reduced_ket(1111, [Qubit(2), Qubit(5)])   # 0b01
+        1
+        >>> get_reduced_ket(101, [Qubit(1), Qubit(0)])    # 0b10
+        2
+        >>> get_reduced_ket(101, [Qubit(0), Qubit(1)])    # 0b01
+        1
+    """
+    reduced_ket = 0
+    for i, qubit in enumerate(qubits):
+        reduced_ket |= ((ket & (1 << qubit.index)) >> qubit.index) << i
+
+    return reduced_ket
+
+
+
+ +
+ + + +
+ +
+ +
+ +
+ + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/mkdocs.yaml b/mkdocs.yaml index 761c693a..6862e7eb 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -5,6 +5,10 @@ theme: name: "material" plugins: + - search + - gen-files: + scripts: + - scripts/gen_reference_page.py - mkdocstrings nav: diff --git a/poetry.lock b/poetry.lock index abdcd4f6..b145a211 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,18 +13,17 @@ files = [ [[package]] name = "babel" -version = "2.13.1" +version = "2.14.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ - {file = "Babel-2.13.1-py3-none-any.whl", hash = "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"}, - {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"}, + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} -setuptools = {version = "*", markers = "python_version >= \"3.12\""} [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] @@ -310,13 +309,13 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "griffe" -version = "0.38.0" +version = "0.38.1" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ - {file = "griffe-0.38.0-py3-none-any.whl", hash = "sha256:6a5bc457320e8e199006aa5fbb03e162f5e21abe31aa6221f7a5c37ea0724c71"}, - {file = "griffe-0.38.0.tar.gz", hash = "sha256:9b97487b583042b543d1e28196caee638ecd766c8c4c98135071806cb5333ac2"}, + {file = "griffe-0.38.1-py3-none-any.whl", hash = "sha256:334c79d3b5964ade65c05dfcaf53518c576dedd387aaba5c9fd71212f34f1483"}, + {file = "griffe-0.38.1.tar.gz", hash = "sha256:bd68d7da7f3d87bc57eb9962b250db123efd9bbcc06c11c1a91b6e583b2a9361"}, ] [package.dependencies] @@ -324,31 +323,31 @@ colorama = ">=0.4" [[package]] name = "idna" -version = "3.4" +version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] name = "importlib-metadata" -version = "6.8.0" +version = "7.0.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, - {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, + {file = "importlib_metadata-7.0.0-py3-none-any.whl", hash = "sha256:d97503976bb81f40a193d41ee6570868479c69d5068651eb039c40d850c59d67"}, + {file = "importlib_metadata-7.0.0.tar.gz", hash = "sha256:7fc841f8b8332803464e5dc1c63a2e59121f46ca186c0e2e182e80bf8c1319f7"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] @@ -538,6 +537,20 @@ files = [ Markdown = ">=3.3" mkdocs = ">=1.1" +[[package]] +name = "mkdocs-gen-files" +version = "0.5.0" +description = "MkDocs plugin to programmatically generate documentation pages during the build" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs_gen_files-0.5.0-py3-none-any.whl", hash = "sha256:7ac060096f3f40bd19039e7277dd3050be9a453c8ac578645844d4d91d7978ea"}, + {file = "mkdocs_gen_files-0.5.0.tar.gz", hash = "sha256:4c7cf256b5d67062a788f6b1d035e157fc1a9498c2399be9af5257d4ff4d19bc"}, +] + +[package.dependencies] +mkdocs = ">=1.0.3" + [[package]] name = "mkdocs-material" version = "9.5.2" @@ -785,24 +798,24 @@ files = [ [[package]] name = "pathspec" -version = "0.11.2" +version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] name = "platformdirs" -version = "4.0.0" +version = "4.1.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, - {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, + {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, + {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, ] [package.extras] @@ -841,17 +854,17 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.4" +version = "10.5" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "pymdown_extensions-10.4-py3-none-any.whl", hash = "sha256:cfc28d6a09d19448bcbf8eee3ce098c7d17ff99f7bd3069db4819af181212037"}, - {file = "pymdown_extensions-10.4.tar.gz", hash = "sha256:bc46f11749ecd4d6b71cf62396104b4a200bad3498cb0f5dad1b8502fe461a35"}, + {file = "pymdown_extensions-10.5-py3-none-any.whl", hash = "sha256:1f0ca8bb5beff091315f793ee17683bc1390731f6ac4c5eb01e27464b80fe879"}, + {file = "pymdown_extensions-10.5.tar.gz", hash = "sha256:1b60f1e462adbec5a1ed79dac91f666c9c0d241fa294de1989f29d20096cfd0b"}, ] [package.dependencies] -markdown = ">=3.2" +markdown = ">=3.5" pyyaml = "*" [package.extras] @@ -1113,22 +1126,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "setuptools" -version = "69.0.2" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, - {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - [[package]] name = "six" version = "1.16.0" @@ -1153,13 +1150,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [[package]] @@ -1235,4 +1232,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "d5785681f1dc62923be8ec09c13c5a1d9ed66d70d49ae18933648e3d1b1e8995" +content-hash = "2144dcd65b8a37de2c6c30a920f7cd6047d0037db04859826fff3a5e99fd395f" diff --git a/pyproject.toml b/pyproject.toml index e219d8de..352cb140 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ numpy = [ ] mkdocs = {extras = ["python"], version = "^1.5.3"} mkdocstrings = {extras = ["python"], version = "^0.24.0"} +mkdocs_gen_files = {extras = ["python"], version = "^0.5.0"} mkdocs-material = "^9.4.12" [tool.poetry.group.dev.dependencies] diff --git a/scripts/gen_reference_page.py b/scripts/gen_reference_page.py new file mode 100644 index 00000000..4353347c --- /dev/null +++ b/scripts/gen_reference_page.py @@ -0,0 +1,28 @@ +""" +Automatically add all OpenSquirrel Python modules to docs/reference.md whenever the mkdocs documentation is built. +""" + +from pathlib import Path + +import mkdocs_gen_files + +repo = Path(__file__).parent.parent +opensquirrel = repo / "opensquirrel" +reference_file = repo / "docs" / "reference.md" + +reference_md_content = "\n\n" +for path in sorted(opensquirrel.rglob("*.py")): + module_path = path.relative_to(repo).with_suffix("") + + parts = list(module_path.parts) + + if parts[-1] == "__init__": + continue + elif parts[-1] == "__main__": + continue + + identifier = ".".join(parts) + reference_md_content += "::: " + identifier + "\n" + +with mkdocs_gen_files.open(reference_file, "w") as fd: + print(reference_md_content, file=fd)