Example #1
0
 def build(self, context=None):
     """Override the build method to provide the native gates."""
     if context is not None:
         raise JaqalError(
             "Do not provide a context to the build method of CircuitBuilder"
         )
     return super().build(self.native_gates)
Example #2
0
 def build_let(self, sexpression, _context, _gate_context):
     args = list(sexpression.args)
     if len(args) != 2:
         raise JaqalError(
             f"let statement requires two arguments, found {args}")
     name, value = args
     return Constant(name, as_integer(value))
Example #3
0
    def build_usepulses(self, sexpression, context, gate_context):
        # Process a from ... usepulses * statement if autoload_pulses
        ret = {}
        if not self.autoload_pulses:
            return ret

        name, filt = sexpression.args

        module = import_module(str(name))
        native_gates = module.NATIVE_GATES

        if (filt is not all) and (filt != "*"):
            raise JaqalError("Only from ... usepulses * currently supported.")

        for g in native_gates:
            # inject_pulses overrides usepulses
            if self.inject_pulses and g.name in self.inject_pulses:
                continue

            # but later usepulses override earlier imports
            gate_context[g.name] = g
            ret[g.name] = g

        # Separate imported context from local
        return ret
Example #4
0
    def _store(self, cmd):
        gate = cmd.gate

        if len(self.measure_accumulator) == len(self._q) and len(self._q) > 0:
            self.measure_accumulator = set()
            self._block.gate("prepare_all")

        if gate == Allocate:
            qid = self._mapped_qubit_id(cmd.qubits[0][0])
            self._circuit.stretch_register(qid + 1)

        elif gate == Deallocate:
            pass  # The user might stop caring about the qubit, but we need to keep it around.

        elif gate == Measure:
            qid = self._mapped_qubit_id(cmd.qubits[0][0])
            if qid in self.measure_accumulator:
                raise JaqalError("Can't measure qubit %d twice!" % qid)
            else:
                self.measure_accumulator.add(qid)
                if len(self.measure_accumulator) == len(self._q):
                    self._block.gate("measure_all")

        elif gate == Barrier:
            self._block = UnscheduledBlockBuilder()
            self._circuit.expression.append(self._block.expression)

        elif type(gate) in one_qubit_gates:
            qid = self._mapped_qubit_id(cmd.qubits[0][0])
            if qid in self.measure_accumulator:
                raise JaqalError(
                    "Can't do gates in the middle of measurement!")
            else:
                self._block.gate(
                    *self.one_qubit_gates[type(gate)](gate, self._q[qid]))

        elif type(gate) in two_qubit_gates:
            qids = [self._mapped_qubit_id(qb[0]) for qb in cmd.qubits]
            for qid in qids:
                if qid in self.measure_accumulator:
                    raise JaqalError(
                        "Can't do gates in the middle of measurement!")
            self._block.gate(*self.two_qubit_gates[type(gate)](
                gate, *[self._q[qid] for qid in qids]))

        else:
            raise JaqalError("Unknown instruction! %s" % gate)
Example #5
0
 def visit_loop_statement(self, repetition_count, block):
     """Validate the repetition count is a non-negative integer."""
     num = self.extract_signed_number(repetition_count)
     if not is_non_negative_integer(num):
         raise JaqalError(
             f"While resolving let values: illegal loop statement count: {num}"
         )
     return self.make_loop_statement(repetition_count, block)
Example #6
0
 def visit_array_declaration(self, identifier, size):
     """Validate the size is a non-negative integer."""
     num = self.extract_signed_number(size)
     if not is_non_negative_integer(num):
         raise JaqalError(
             f"While resolving let values: illegal array declaration size: {num}"
         )
     return self.make_array_declaration(identifier, size)
Example #7
0
 def visit_macro_definition(self, name, arguments, block):
     name_id = self.extract_identifier(name)
     arg_ids = [self.extract_identifier(arg) for arg in arguments]
     if name_id in self.macro_mapping:
         raise JaqalError(f"Redefinition of {name_id} macro")
     self.macro_mapping[name_id] = MacroRecord(
         arg_ids, self.deconstruct_macro_gate_block(block))
     return self.make_macro_definition(name, arguments, block)
Example #8
0
def build_noiseless_native_model(
    registers,
    gates,
):
    """Builds a noise model for each Jaqal gate

    :param register: the Jaqal registers that the gates may act on
    :param gates: a dictionary of Jaqal gates
    :return: a pyGSTi noise model object
    """
    gate_names = []
    unitaries = {}
    availability = {}

    for g in gates.values():
        name = f"G{g.name.lower()}"
        gate_names.append(name)

        if len(g.quantum_parameters) == 0:
            # AER: We are special casing prepare and measurements right now
            if g.name in ("prepare_all", "measure_all"):
                unitaries[name] = np.identity(2)
            else:
                raise JaqalError(f"{g.name} not supported")
            continue

        if len(g.classical_parameters) > 0:
            unitaries[name] = g._ideal_unitary_pygsti
        else:
            unitaries[name] = g.ideal_unitary()

        if len(g.quantum_parameters) > 1:
            availability[name] = "all-permutations"

    fundamental_registers = [
        r for r in registers.values() if r._alias_from is None
    ]
    if len(fundamental_registers) > 1:
        print(
            "Warning:  More than one physical register name in use; ordering may be borked."
        )
    physical_qubit_list = []
    for r in fundamental_registers:
        for q in r:
            physical_qubit_list.append(q._name)

    num_qubits = len(physical_qubit_list)

    target_model = pygsti.construction.build_localnoise_model(
        nQubits=num_qubits,
        gate_names=gate_names,
        nonstd_gate_unitaries=unitaries,
        availability=availability,
        qubit_labels=physical_qubit_list,
        parameterization="static unitary",
    )

    return target_model
Example #9
0
    def run(self):
        """
        Does not run a previously loaded Jaqal program on an abstraction of the QSCOUT hardware.

        :raises JaqalError: Because the Quil API should not be used to try to execute programs on QSCOUT.
        """
        raise JaqalError(
            "QSCOUT cannot run programs through the Quil API. Generate a Jaqal file with compile() and submit it directly to the QSCOUT team."
        )
Example #10
0
def parse_jaqal_string(
    jaqal,
    override_dict=None,
    expand_macro=False,
    expand_let=False,
    expand_let_map=False,
    return_usepulses=False,
    inject_pulses=None,
    autoload_pulses=True,
):
    """Parse a string written in Jaqal into core types.

    :param str jaqal: The Jaqal code.
    :param override_dict:  An optional dictionary that overrides let statements in the Jaqal code.
        Note: all keys in this dictionary must exist as let statements or an error will be raised.
    :type override_dict: dict[str, float]
    :param bool expand_macro: Replace macro invocations by their body while parsing.
    :param bool expand_let: Replace let constants by their value while parsing.
    :param bool expand_let_map: Replace let constants and mapped qubits while parsing. expand_let is ignored if this is True.
    :param bool return_usepulses: Whether to both add a second return value and populate it with the usepulses statement.
    :param inject_pulses: If given, use these pulses specifically.
    :param bool autoload_pulses: Whether to employ the usepulses statement for parsing.  Requires appropriate gate definitions.
    :return: The circuit representation of the file and usepulses if
        requested. usepulses is stored in a dict under the key
        'usepulses'. It is itself a dict mapping :class:`Identifier`
        objects to what the import, which may be the special symbol all.

    """

    # The interface will automatically expand macros and scrape let, map, and register metadata.
    iface = Interface(jaqal, allow_no_usepulses=True)
    # Do some minimal processing to fill in all let and map values. The interface does not
    # automatically do this as they may rely on values from override_dict.
    let_dict = iface.make_let_dict(override_dict)
    tree = iface.tree
    expand_let = expand_let or expand_let_map
    if expand_macro:
        tree = iface.resolve_macro(tree)
    if expand_let:
        tree = iface.resolve_let(tree, let_dict=let_dict)
    if expand_let_map:
        tree = iface.resolve_map(tree)
    circuit = convert_to_circuit(
        tree, inject_pulses=inject_pulses, autoload_pulses=autoload_pulses
    )

    if return_usepulses:
        ret_extra = {"usepulses": iface.usepulses}
        ret_value = (circuit, ret_extra)
    else:
        ret_value = circuit

    if sum(reg.fundamental for reg in circuit.registers.values()) > 1:
        raise JaqalError(f"Circuit has too many registers: {list(circuit.registers)}")

    return ret_value
Example #11
0
 def __getitem__(self, key):
     name = make_item_name(self, key)
     if isinstance(key, slice):
         raise JaqalError(
             "Anonymous slices are not currently supported; slice only in a map statement."
         )
         # But if the backend ever supports it, just replace the above line with the below line:
         # return Register(self.name + '[' + str(key) + ']', alias_from=self, alias_slice=key)
     else:
         return NamedQubit(name, self, key)
Example #12
0
 def resolve_constant(self, const):
     """Return the value for the given constant defined either in the
     override_dict or in the circuit itself."""
     if const.name in self.override_dict:
         return self.override_dict[const.name]
     if isinstance(const.value, (int, float)):
         return const.value
     else:
         # I don't think this can happen
         raise JaqalError(f"Constant {const.name} has non-numeric value")
Example #13
0
 def visit_array_slice(self, identifier, index_slice):
     """Validate all parts of the slice are integers."""
     for value in [index_slice.start, index_slice.stop, index_slice.step]:
         if value is not None:
             num = self.extract_signed_number(value)
             if not is_integer(num):
                 raise JaqalError(
                     f"While resolving let values: illegal array slice value {num}"
                 )
     return self.make_array_slice(identifier, index_slice)
Example #14
0
    def visit_GateStatement(self, gate, context=None):
        if gate.name == self.p_gate:
            # We allow for multiple prepare_all's in a row. But gates between those
            # prepare_all's do nothing. Notice also, we would not yet know what the
            # measured or used qubits are, if we had partial measurements.  That would
            # have to wait until the measurement.
            c = self.current = Trace(self.address[:])
        elif gate.name == self.m_gate:
            if self.current is None:
                raise JaqalError(f"{self.p_gate} must follow a {self.m_gate}")
            self.current.end = self.address[:]
            self.current.used_qubits = self.qubits
            self.subcircuits.append(self.current)
            self.current = None
        else:
            if self.current is None:
                raise JaqalError(f"gates must follow a {self.p_gate}")

        return super().visit_GateStatement(gate, context=context)
Example #15
0
    def quantum_parameters(self):
        """The quantum parameters (qubits or registers) this gate takes.

        :raises JaqalError: If this gate has parameters without type annotations; for
            example, if it is a macro.
        """
        try:
            return [param for param in self.parameters if not param.classical]
        except JaqalError:
            pass
        raise JaqalError("Gate {self.name} has a parameter with unknown type")
Example #16
0
 def build(self, expression, context=None, gate_context=None):
     """Build the appropriate thing based on the expression."""
     if context is None:
         context = self.make_context()
     if gate_context is None:
         gate_context = self.make_gate_context()
     if isinstance(expression, str):
         # Identifiers
         if expression in context:
             return context[expression]
         raise JaqalError(f"Identifier {expression} not found in context")
     if not SExpression.is_convertible(expression):
         # This is either a number used as a gate argument or an already-created type.
         return expression
     sexpression = SExpression(expression)
     method_name = f"build_{sexpression.command}"
     if not hasattr(self, method_name):
         raise JaqalError(
             f"Cannot handle object of type {sexpression.command}")
     return getattr(self, method_name)(sexpression, context, gate_context)
Example #17
0
 def __init__(self, name, value):
     if isinstance(value, Constant):
         super().__init__(name, value.kind)
     elif isinstance(value, float):
         super().__init__(name, ParamType.FLOAT)
     elif isinstance(value, int):
         super().__init__(name, ParamType.INT)
     else:
         raise JaqalError(
             f"Invalid/non-numeric value {value} for constant {name}!")
     self._value = value
Example #18
0
 def build_map(self, sexpression, context, gate_context):
     args = list(sexpression.args)
     if len(args) > 1:
         if isinstance(args[1], Register):
             src = args[1]
         else:
             try:
                 name, src_name = args[:2]
                 src = context[src_name]
             except KeyError:
                 raise JaqalError(
                     f"Cannot map {src_name} to {name}, {src_name} does not exist"
                 )
     if len(args) == 2:
         # Mapping a whole register or alias onto this alias
         name, src_name = args
         return Register(name, alias_from=src)
     if len(args) == 3:
         # Mapping a single qubit
         name, src_name, src_index = args
         index = self.build(
             src_index,
             context)  # This may be either an integer or defined parameter.
         return NamedQubit(name, src, index)
     if len(args) == 5:
         # Mapping a slice of a register
         name, src_name, src_start, src_stop, src_step = args
         # These may be either integers, None, or let constants
         start = self.build(src_start, context, gate_context)
         if start is None:
             start = 0
         stop = self.build(src_stop, context, gate_context)
         if stop is None:
             stop = src.size
         step = self.build(src_step, context, gate_context)
         if step is None:
             step = 1
         return Register(name,
                         alias_from=src,
                         alias_slice=slice(start, stop, step))
     raise JaqalError(f"Wrong number of arguments for map, found {args}")
Example #19
0
 def enforce_signed_integer_if_numeric(cls, number):
     if cls.is_signed_integer(number):
         return number
     elif cls.is_integer(number):
         return cls.make_signed_integer(int(number))
     elif cls.is_number(number) or cls.is_signed_number(number):
         # A signed number token can be converted to a float but not an int, so we have a workaround here.
         if float(number) != int(float(number)):
             raise JaqalError(f"Expected signed integer, found {number}")
         return cls.make_signed_integer(int(float(number)))
     else:
         return number
Example #20
0
    def classical_parameters(self):
        """The classical parameters (ints or floats) this gate takes.

        :raises JaqalError: If this gate has parameters without type annotations; for
            example, if it is a macro.

        """
        try:
            return [param for param in self.parameters if param.classical]
        except JaqalError:
            pass
        raise JaqalError("Gate {self.name} has a parameter with unknown type")
Example #21
0
    def classical(self):
        """
        A boolean flag denoting whether this AnnotatedValue has a classical type
        (`ParamType.INT` or `ParamType.FLOAT`) or a quantum type (`ParamType.QUBIT` or
        `ParamType.REGISTER`).

        :raises JaqalError: If the AnnotatedValue doesn't have a type annotation.
        """

        if self._kind == ParamType.NONE:
            raise JaqalError(f"No type defined for parameter {self.name}.")
        return self._kind not in (ParamType.QUBIT, ParamType.REGISTER)
Example #22
0
    def get_gate_definition(self, name, arg_count, gate_context):
        """Return the definition for the given gate. If no such definition exists, and we
        aren't requiring all gates to be a native gate or macro, then create a new
        definition and return it."""
        if name in gate_context:
            gate_def = gate_context[name]
            if not isinstance(gate_def, AbstractGate):
                raise JaqalError(
                    f"Cannot call gate {name}: it is type {type(gate_def)}")
            return gate_def

        is_anonymous_gate_allowed = (self.inject_pulses is
                                     None) and not self.autoload_pulses

        if not is_anonymous_gate_allowed:
            raise JaqalError(f"No gate {name} defined")
        gate_def = GateDefinition(
            name,
            parameters=[Parameter(f"p{i}", None) for i in range(arg_count)])
        gate_context[name] = gate_def
        return gate_def
Example #23
0
    def visit_register_statement(self, array_declaration):
        """Record information about the given register. We check that map statements are compatible with
        register statements."""
        identifier, size = self.deconstruct_array_declaration(
            array_declaration)
        # Although in general this is not the right place to check for register inconsistencies, the errors might
        # be too confusing if we ignored the double register declaration error.
        identifier = Identifier(self.extract_identifier(identifier))
        if identifier in self.registers:
            raise JaqalError(f"Register {identifier} already declared")
        self.registers[identifier] = size

        return self.make_register_statement(array_declaration)
Example #24
0
def replace_gate(gate, macros):
    """Replace a gate with its definition in macros, or return the gate if
    it is not a macro."""
    if gate.name in macros:
        macro = macros[gate.name]
        if len(gate.parameters) != len(macro.parameters):
            raise JaqalError(
                f"Cannot expand {gate.name}: wrong argument count: {len(gate.parameters)} != {len(macro.parameters)}"
            )
        visitor = GateReplacer(gate.parameters, macros)
        return visitor.visit(macro)
    else:
        return gate
Example #25
0
    def resolve_map_element_single(self, identifier_value, index_value):
        """Find the array or register that this identifier maps to, and return the name and index."""

        if identifier_value not in self.map_dict:
            raise JaqalError(f"Cannot resolve map {identifier_value}")

        source = self.map_dict[identifier_value]

        if self.is_identifier(source):
            src_identifier = self.extract_identifier(source)
            src_index = index_value
        elif self.is_array_slice(source):
            src_id_token, src_slice = self.deconstruct_array_slice(source)
            src_identifier = self.extract_identifier(src_id_token)
            if any(comp is not None and not self.is_signed_integer(comp)
                   for comp in src_slice):
                raise JaqalError(f"Unresolved map element {identifier_value}")
            src_start, src_stop, src_step = [
                self.extract_signed_integer(comp) if comp is not None else comp
                for comp in src_slice
            ]
            limit = src_stop or maxsize
            src_start, src_stop, src_step = slice(src_start, src_stop,
                                                  src_step).indices(limit)
            src_range = range(src_start, src_stop, src_step)
            try:
                src_index = src_range[index_value]
            except IndexError:
                raise JaqalError(
                    f"Index {index_value} out of range for mapping {identifier_value}"
                )
        elif self.is_array_element(source):
            raise JaqalError(
                f"Cannot use map alias {identifier_value} as an array element")
        else:
            raise JaqalError(f"Unknown map source format: {source}")

        return src_identifier, src_index
Example #26
0
 def extract_token(cls, token):
     """Figure out what the token is and call the appropriate extract method."""
     if cls.is_identifier(token):
         return cls.extract_identifier(token)
     elif cls.is_integer(token):
         return cls.extract_integer(token)
     elif cls.is_signed_integer(token):
         return cls.extract_signed_integer(token)
     elif cls.is_number(token):
         return cls.extract_number(token)
     elif cls.is_signed_number(token):
         return cls.extract_signed_number(token)
     else:
         raise JaqalError(f"Unknown token: {token}")
Example #27
0
 def __init__(self, name, size=None, alias_from=None, alias_slice=None):
     self._name = name
     self._size = size
     if (alias_from is None) and not (alias_slice is None
                                      and size is not None):
         raise JaqalError(f"Invalid register declaration: {name}.")
     if (size is not None) and (alias_from is not None):
         raise JaqalError(
             f"Illegal size specification in map statement defining {name}."
         )
     self._alias_from = alias_from
     self._alias_slice = alias_slice
     if alias_slice is not None:
         if (isinstance(alias_slice.start, AnnotatedValue)
                 or isinstance(alias_slice.stop, AnnotatedValue)
                 or isinstance(alias_slice.step, AnnotatedValue)
                 or isinstance(alias_from, AnnotatedValue)):
             # Verify that the Parameters given have the correct types
             if isinstance(
                     alias_slice.start,
                     AnnotatedValue) and alias_slice.start.kind not in (
                         ParamType.INT, ParamType.NONE):
                 raise JaqalError(
                     f"Cannot slice register {alias_from.name} with parameter {alias_slice.start.name} of non-integer kind {alias_slice.start.kind}."
                 )
             elif isinstance(
                     alias_slice.stop,
                     AnnotatedValue) and alias_slice.stop.kind not in (
                         ParamType.INT, ParamType.NONE):
                 raise JaqalError(
                     f"Cannot slice register {alias_from.name} with parameter {alias_slice.stop.name} of non-integer kind {alias_slice.stop.kind}."
                 )
             elif isinstance(
                     alias_slice.step,
                     AnnotatedValue) and alias_slice.step.kind not in (
                         ParamType.INT, ParamType.NONE):
                 raise JaqalError(
                     f"Cannot slice register {alias_from.name} with parameter {alias_slice.step.name} of non-integer kind {alias_slice.step.kind}."
                 )
             elif isinstance(alias_from,
                             AnnotatedValue) and alias_from.kind not in (
                                 ParamType.REGISTER,
                                 ParamType.NONE,
                             ):
                 raise JaqalError(
                     f"Cannot slice parameter {alias_from.name} of non-register kind {alias_from.kind}."
                 )
         elif alias_from.size is not None and not isinstance(
                 alias_from.size, AnnotatedValue):
             if alias_slice.stop > alias_from.size:
                 raise JaqalError("Index out of range.")
Example #28
0
 def build_macro(self, sexpression, context, gate_context):
     args = list(sexpression.args)
     if len(args) < 2:
         raise JaqalError(
             f"Macro must have at least two arguments, found {args}")
     name = args[0]
     if name in gate_context:
         raise JaqalError(f"Attempting to redefine gate {name}")
     parameter_args = args[1:-1]
     block = args[-1]
     parameter_list = [
         param if isinstance(param, Parameter) else Parameter(param, None)
         for param in parameter_args
     ]
     parameter_dict = {param.name: param for param in parameter_list}
     macro_context = {
         **context,
         **parameter_dict,
     }  # parameters must be listed second to take precedence
     built_block = self.build(block, macro_context, gate_context)
     if not isinstance(built_block, BlockStatement):
         raise JaqalError(
             f"Macro body must be a block, found {type(built_block)}")
     return Macro(name, parameters=parameter_list, body=built_block)
Example #29
0
 def visit_gate_statement(self, gate_name, gate_args):
     """If this gate statement is really a macro, replace it with the macro's block."""
     gate_name_key = self.extract_qualified_identifier(gate_name)
     if gate_name_key in self.macro_mapping:
         arguments, block = self.macro_mapping[gate_name_key]
         if len(arguments) != len(gate_args):
             raise JaqalError(
                 f"In resolving macro {gate_name_key}, expected {len(arguments)} arguments, found {len(gate_args)}"
             )
         arg_dict = {
             arg: gate_arg
             for arg, gate_arg in zip(arguments, gate_args)
         }
         return substitute_macro_arguments(block, arg_dict)
     return self.make_gate_statement(gate_name, gate_args)
Example #30
0
    def visit_array_element_qual(self, identifier, index):
        """Validate the index is an integer."""
        identifier_value = self.extract_qualified_identifier(identifier)
        index_value = self.extract_signed_integer(index)

        if self.is_macro_argument(identifier_value):
            raise JaqalError(
                "This macro uses an argument as a register; please resolve macros before resolving maps"
            )

        identifier_value, index_value = self.resolve_map_element(
            identifier_value, index_value)
        identifier = self.make_qualified_identifier(identifier_value)
        index = self.make_signed_integer(index_value)

        return self.make_array_element_qual(identifier, index)