Skip to content

parser

Parser

Bases: GateLibrary, MeasurementLibrary, ResetLibrary

Source code in opensquirrel\parser\libqasm\parser.py
class Parser(GateLibrary, MeasurementLibrary, ResetLibrary):
    def __init__(
        self,
        gate_set: Iterable[Callable[..., Gate]] = default_gate_set,
        gate_aliases: Mapping[str, Callable[..., Gate]] = default_gate_aliases,
        measurement_set: Iterable[Callable[..., Measure]] = default_measurement_set,
        reset_set: Iterable[Callable[..., Reset]] = default_reset_set,
    ) -> None:
        GateLibrary.__init__(self, gate_set, gate_aliases)
        MeasurementLibrary.__init__(self, measurement_set)
        ResetLibrary.__init__(self, reset_set)
        self.ir = None

    @staticmethod
    def _ast_literal_to_ir_literal(
        cqasm_literal_expression: cqasm.values.ConstInt | cqasm.values.ConstFloat,
    ) -> Int | Float | None:
        if type(cqasm_literal_expression) not in [cqasm.values.ConstInt, cqasm.values.ConstFloat]:
            msg = f"unrecognized type: {type(cqasm_literal_expression)}"
            raise TypeError(msg)
        if isinstance(cqasm_literal_expression, cqasm.values.ConstInt):
            return Int(cqasm_literal_expression.value)
        if isinstance(cqasm_literal_expression, cqasm.values.ConstFloat):
            return Float(cqasm_literal_expression.value)
        return None

    @staticmethod
    def _type_of(ast_expression: Any) -> type:
        if isinstance(ast_expression, (cqasm.values.IndexRef, cqasm.values.VariableRef)):
            return type(ast_expression.variable.typ)
        return type(ast_expression)

    @staticmethod
    def _size_of(ast_expression: Any) -> int:
        if isinstance(ast_expression, cqasm.values.IndexRef):
            return len(ast_expression.indices)
        if isinstance(ast_expression, cqasm.values.VariableRef):
            return int(ast_expression.variable.typ.size)
        return 1

    @staticmethod
    def _is_qubit_type(ast_expression: Any) -> bool:
        ast_type = Parser._type_of(ast_expression)
        return bool(ast_type == cqasm.types.Qubit or ast_type == cqasm.types.QubitArray)

    @staticmethod
    def _is_bit_type(ast_expression: Any) -> bool:
        ast_type = Parser._type_of(ast_expression)
        return bool(ast_type == cqasm.types.Bit or ast_type == cqasm.types.BitArray)

    @staticmethod
    def _get_qubits(
        ast_qubit_expression: cqasm.values.VariableRef | cqasm.values.IndexRef,
        register_manager: RegisterManager,
    ) -> list[Qubit]:
        ret = []
        variable_name = ast_qubit_expression.variable.name
        if isinstance(ast_qubit_expression, cqasm.values.VariableRef):
            qubit_range = register_manager.get_qubit_range(variable_name)
            ret = [Qubit(index) for index in range(qubit_range.first, qubit_range.first + qubit_range.size)]
        if isinstance(ast_qubit_expression, cqasm.values.IndexRef):
            int_indices = [int(i.value) for i in ast_qubit_expression.indices]
            indices = [register_manager.get_qubit_index(variable_name, i) for i in int_indices]
            ret = [Qubit(index) for index in indices]
        return ret

    @staticmethod
    def _get_bits(
        ast_bit_expression: cqasm.values.VariableRef | cqasm.values.IndexRef,
        register_manager: RegisterManager,
    ) -> list[Bit]:
        ret = []
        variable_name = ast_bit_expression.variable.name
        if isinstance(ast_bit_expression, cqasm.values.VariableRef):
            bit_range = register_manager.get_bit_range(variable_name)
            ret = [Bit(index) for index in range(bit_range.first, bit_range.first + bit_range.size)]
        if isinstance(ast_bit_expression, cqasm.values.IndexRef):
            int_indices = [int(i.value) for i in ast_bit_expression.indices]
            indices = [register_manager.get_bit_index(variable_name, i) for i in int_indices]
            ret = [Bit(index) for index in indices]
        return ret

    @classmethod
    def _get_expanded_measure_args(cls, ast_args: Any, register_manager: RegisterManager) -> zip[tuple[Any, ...]]:
        """Construct a list with a list of bits and a list of qubits, then return a zip of both lists.
        For example: [(Qubit(0), Bit(0)), (Qubit(1), Bit(1))]

        Notice the  list is walked in reverse mode.
        This is because the AST measure node has a bit first operand and a qubit second operand.
        """
        expanded_args: list[list[Any]] = []
        for ast_arg in reversed(ast_args):
            if Parser._is_qubit_type(ast_arg):
                expanded_args.append(cls._get_qubits(ast_arg, register_manager))
            elif Parser._is_bit_type(ast_arg):
                expanded_args.append(cls._get_bits(ast_arg, register_manager))
            else:
                msg = "received argument is not a (qu)bit"
                raise TypeError(msg)
        return zip(*expanded_args)

    @classmethod
    def _get_expanded_reset_args(cls, ast_args: Any, register_manager: RegisterManager) -> zip[tuple[Any, ...]]:
        """Construct a list of qubits and return a zip.
        For example: [Qubit(0), Qubit(1), Qubit(2)]
        """
        expanded_args: list[Any] = []
        if len(ast_args) < 1:
            expanded_args += [Qubit(qubit_index) for qubit_index in range(register_manager.get_qubit_register_size())]
            return zip(expanded_args)
        for ast_arg in ast_args:
            if Parser._is_qubit_type(ast_arg):
                expanded_args += cls._get_qubits(ast_arg, register_manager)
            else:
                msg = "received argument is not a (qu)bit"
                raise TypeError(msg)
        return zip(expanded_args)

    @classmethod
    def _get_expanded_gate_args(cls, ast_args: Any, register_manager: RegisterManager) -> zip[tuple[Any, ...]]:
        """Construct a list with a list of qubits and a list of parameters, then return a zip of both lists.
        For example: [(Qubit(0), Float(pi)), (Qubit(1), Float(pi))]
        """
        number_of_operands = 0
        for ast_arg in ast_args:
            if Parser._is_qubit_type(ast_arg):
                number_of_operands += Parser._size_of(ast_arg)
        expanded_args: list[list[Any]] = []
        for ast_arg in ast_args:
            if Parser._is_qubit_type(ast_arg):
                expanded_args.append(cls._get_qubits(ast_arg, register_manager))
            else:
                expanded_args.append([cls._ast_literal_to_ir_literal(ast_arg)] * number_of_operands)
        return zip(*expanded_args)

    @staticmethod
    def _create_analyzer() -> cqasm.Analyzer:
        without_defaults = False
        return cqasm.Analyzer("3.0", without_defaults)

    @staticmethod
    def _check_analysis_result(result: Any) -> None:
        if isinstance(result, list):
            raise OSError("parsing error: " + ", ".join(result))

    def circuit_from_string(self, s: str) -> Circuit:
        # Analysis result will be either an Abstract Syntax Tree (AST) or a list of error messages
        analyzer = Parser._create_analyzer()
        analysis_result = analyzer.analyze_string(s)
        Parser._check_analysis_result(analysis_result)
        ast = analysis_result

        # Create RegisterManager
        register_manager = RegisterManager.from_ast(ast)

        # Parse statements
        ir = IR()
        for statement in ast.block.statements:
            if "measure" in statement.name:
                generator_f_measure = self.get_measurement_f(statement.name)
                expanded_args = Parser._get_expanded_measure_args(statement.operands, register_manager)
                for arg_set in expanded_args:
                    ir.add_measurement(generator_f_measure(*arg_set))
            elif "reset" in statement.name:
                generator_f_reset = self.get_reset_f(statement.name)
                expanded_args = Parser._get_expanded_reset_args(statement.operands, register_manager)
                for arg_set in expanded_args:
                    ir.add_reset(generator_f_reset(*arg_set))
            else:
                generator_f_gate = self.get_gate_f(statement.name)
                expanded_args = Parser._get_expanded_gate_args(statement.operands, register_manager)
                for arg_set in expanded_args:
                    ir.add_gate(generator_f_gate(*arg_set))

        return Circuit(register_manager, ir)