Skip to content

Compilation

oqd_analog_emulator.passes

compiler_analog_circuit_to_qutipIR(model, fock_cutoff)

This compiles (AnalogCircuit to a list of QutipOperation objects

Parameters:

Name Type Description Default
model AnalogCircuit
required
fock_cutoff int

fock_cutoff for Ladder Operators

required

Returns:

Type Description
list(QutipOperation)
Source code in oqd-analog-emulator/src/oqd_analog_emulator/passes.py
def compiler_analog_circuit_to_qutipIR(model, fock_cutoff):
    """
    This compiles ([`AnalogCircuit`][oqd_core.interface.analog.operation.AnalogCircuit] to a list of  [`QutipOperation`][oqd_analog_emulator.interface.QutipOperation] objects

    Args:
        model (AnalogCircuit):
        fock_cutoff (int): fock_cutoff for Ladder Operators

    Returns:
        (list(QutipOperation)):

    """
    return Post(QutipBackendCompiler(fock_cutoff=fock_cutoff))(model=model)

compiler_analog_args_to_qutipIR(model)

This compiles TaskArgsAnalog to a list of TaskArgsQutip

Parameters:

Name Type Description Default
model TaskArgsAnalog
required

Returns:

Type Description
TaskArgsQutip
Source code in oqd-analog-emulator/src/oqd_analog_emulator/passes.py
def compiler_analog_args_to_qutipIR(model):
    """
    This compiles TaskArgsAnalog to a list of TaskArgsQutip


    Args:
        model (TaskArgsAnalog):

    Returns:
        (TaskArgsQutip):

    """
    return Post(QutipBackendCompiler(fock_cutoff=model.fock_cutoff))(model=model)

run_qutip_experiment(model: QutipExperimentVM, args)

This takes in a QutipExperiment and produces a TaskResultAnalog object

Parameters:

Name Type Description Default
model QutipExperiment
required
args

(Qutip

required

Returns:

Type Description
TaskResultAnalog

Contains results of the simulation

Source code in oqd-analog-emulator/src/oqd_analog_emulator/passes.py
def run_qutip_experiment(model: QutipExperimentVM, args):
    """
    This takes in a [`QutipExperiment`][oqd_analog_emulator.interface.QutipExperiment] and produces a TaskResultAnalog object

    Args:
        model (QutipExperiment):
        args: (Qutip

    Returns:
        (TaskResultAnalog): Contains results of the simulation

    """
    n_qreg = model.n_qreg
    n_qmode = model.n_qmode
    metrics = Post(QutipMetricConversion(n_qreg=n_qreg, n_qmode=n_qmode))(args.metrics)
    interpreter = Pre(
        QutipExperimentVM(
            qt_metrics=metrics,
            n_shots=args.n_shots,
            fock_cutoff=args.fock_cutoff,
            dt=args.dt,
        )
    )
    interpreter(model=model)

    return interpreter.children[0].results

oqd_analog_emulator.conversion

QutipMetricConversion

Bases: ConversionRule

This takes in a a dictionary containing Metrics, which get converted to lambda functions for QuTip

Parameters:

Name Type Description Default
model dict

The values are Analog layer Operators

required

Returns:

Name Type Description
model dict

The values are lambda functions

Note

n_qreg and n_qmode are given as compiler parameters

Source code in oqd-analog-emulator/src/oqd_analog_emulator/conversion.py
class QutipMetricConversion(ConversionRule):
    """
    This takes in a a dictionary containing Metrics, which get converted to lambda functions for QuTip

    Args:
        model (dict): The values are Analog layer Operators

    Returns:
        model (dict): The values are lambda functions

    Note:
        n_qreg and n_qmode are given as compiler parameters
    """

    def __init__(self, n_qreg, n_qmode):
        super().__init__()
        self._n_qreg = n_qreg
        self._n_qmode = n_qmode

    def map_QutipExpectation(self, model, operands):
        for idx, operator in enumerate(model.operator):
            coefficient = evaluate_math_expr(operator[1])
            op_exp = (
                coefficient * operator[0]
                if idx == 0
                else op_exp + coefficient * operator[0]
            )
        return lambda t, psi: qt.expect(op_exp, psi)

    def map_EntanglementEntropyVN(self, model, operands):
        return lambda t, psi: entanglement_entropy_vn(
            t, psi, model.qreg, model.qmode, self._n_qreg, self._n_qmode
        )

QutipExperimentVM

Bases: RewriteRule

This is a Virtual Machine which takes in a QutipExperiment object, simulates the experiment and then produces the results

Parameters:

Name Type Description Default
model QutipExperiment

This is the compiled [QutipExperiment][oqd_analog_emulator.qutip_backend.QutipExperiment] object

required

Returns:

Name Type Description
task TaskResultAnalog
Note

n_qreg and n_qmode are given as compiler parameters

Source code in oqd-analog-emulator/src/oqd_analog_emulator/conversion.py
class QutipExperimentVM(RewriteRule):
    """
    This is a Virtual Machine which takes in a QutipExperiment object, simulates the experiment and then produces the results

    Args:
        model (QutipExperiment): This is the compiled  [`QutipExperiment`][oqd_analog_emulator.qutip_backend.QutipExperiment] object

    Returns:
        task (TaskResultAnalog):

    Note:
        n_qreg and n_qmode are given as compiler parameters
    """

    def __init__(self, qt_metrics, n_shots, fock_cutoff, dt):
        super().__init__()
        self.results = TaskResultAnalog(runtime=0)
        self._qt_metrics = qt_metrics
        self._n_shots = n_shots
        self._fock_cutoff = fock_cutoff
        self._dt = dt

    def map_QutipExperiment(self, model):

        dims = model.n_qreg * [2] + model.n_qmode * [self._fock_cutoff]
        self.n_qreg = model.n_qreg
        self.n_qmode = model.n_qmode
        self.current_state = qt.tensor([qt.basis(d, 0) for d in dims])

        self.results.times.append(0.0)
        self.results.state = list(
            self.current_state.full().squeeze(),
        )
        self.results.metrics.update(
            {
                key: [self._qt_metrics[key](0.0, self.current_state)]
                for key in self._qt_metrics.keys()
            }
        )

    def map_QutipMeasurement(self, model):
        if self._n_shots is None:
            self.results.counts = {}
        else:
            probs = np.power(np.abs(self.current_state.full()), 2).squeeze()
            n_shots = self._n_shots
            inds = np.random.choice(len(probs), size=n_shots, p=probs)
            opts = self.n_qreg * [[0, 1]] + self.n_qmode * [
                list(range(self._fock_cutoff))
            ]
            bases = list(itertools.product(*opts))
            shots = np.array([bases[ind] for ind in inds])
            bitstrings = ["".join(map(str, shot)) for shot in shots]
            self.results.counts = {
                bitstring: bitstrings.count(bitstring) for bitstring in bitstrings
            }

        self.results.state = list(
            self.current_state.full().squeeze(),
        )

    def map_QutipOperation(self, model):

        duration = model.duration
        tspan = np.linspace(0, duration, round(duration / self._dt)).tolist()

        qutip_hamiltonian = []
        for op, coeff in model.hamiltonian:
            qutip_hamiltonian.append(
                [op, Chain(simplify_math_expr, print_math_expr)(coeff)]
            )

        start_runtime = time.time()
        result_qobj = qt.sesolve(
            qutip_hamiltonian,
            self.current_state,
            tspan,
            e_ops=self._qt_metrics,
            options={"store_states": True},
        )
        self.results.runtime = time.time() - start_runtime + self.results.runtime

        self.results.times.extend([t + self.results.times[-1] for t in tspan][1:])

        for idx, key in enumerate(self.results.metrics.keys()):
            self.results.metrics[key].extend(result_qobj.expect[idx].tolist()[1:])

        self.current_state = result_qobj.final_state

        self.results.state = list(
            result_qobj.final_state.full().squeeze(),
        )

QutipBackendCompiler

Bases: ConversionRule

This is a ConversionRule which compiles analog layer objects to QutipExperiment objects

Parameters:

Name Type Description Default
model VisitableBaseModel

This takes in objects in Analog level and converts them to representations which can be used to run QuTip simulations.

required

Returns:

Name Type Description
model Union[VisitableBaseModel, Any]

QuTip objects and representations which can be used to run QuTip simulations

Source code in oqd-analog-emulator/src/oqd_analog_emulator/conversion.py
class QutipBackendCompiler(ConversionRule):
    """
    This is a ConversionRule which compiles analog layer objects to QutipExperiment objects

    Args:
        model (VisitableBaseModel): This takes in objects in Analog level and converts them to representations which can be used to run QuTip simulations.

    Returns:
        model (Union[VisitableBaseModel, Any]): QuTip objects and representations which can be used to run QuTip simulations

    """

    def __init__(self, fock_cutoff=None):
        super().__init__()
        self._fock_cutoff = fock_cutoff

    def map_AnalogCircuit(self, model, operands):
        return QutipExperiment(
            instructions=operands["sequence"],
            n_qreg=operands["n_qreg"],
            n_qmode=operands["n_qmode"],
        )

    def map_TaskArgsAnalog(self, model, operands):
        return TaskArgsQutip(
            layer=model.layer,
            n_shots=model.n_shots,
            fock_cutoff=model.fock_cutoff,
            dt=model.dt,
            metrics=operands["metrics"],
        )

    def map_Expectation(self, model, operands):
        return QutipExpectation(operator=operands["operator"])

    def map_Evolve(self, model, operands):
        return QutipOperation(
            hamiltonian=operands["gate"],
            duration=model.duration,
        )

    def map_Measure(self, model, operands):
        return QutipMeasurement()

    def map_AnalogGate(self, model, operands):
        return operands["hamiltonian"]

    def map_OperatorAdd(self, model, operands):
        op = operands["op1"]
        op.append(operands["op2"][0])
        return op

    def map_OperatorScalarMul(self, model, operands):
        return [(operands["op"], model.expr)]

    def map_PauliI(self, model, operands):
        return qt.qeye(2)

    def map_PauliX(self, model, operands):
        return qt.sigmax()

    def map_PauliY(self, model, operands):
        return qt.sigmay()

    def map_PauliZ(self, model, operands):
        return qt.sigmaz()

    def map_Identity(self, model, operands):
        return qt.qeye(self._fock_cutoff)

    def map_Creation(self, model, operands):
        return qt.create(self._fock_cutoff)

    def map_Annihilation(self, model, operands):
        return qt.destroy(self._fock_cutoff)

    def map_OperatorMul(self, model, operands):
        return operands["op1"] * operands["op2"]

    def map_OperatorKron(self, model, operands):
        return qt.tensor(operands["op1"], operands["op2"])