### Installing Qiboml from Source (Bash) Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/installation.rst This sequence of commands demonstrates how to install Qiboml directly from its GitHub source repository. This method is suitable for development, allowing local changes to be reflected without re-installation. The '-e .' flag installs in editable mode. ```bash git clone https://github.com/qiboteam/qiboml.git cd qiboml pip install -e . ``` -------------------------------- ### Installing Qiboml with pip (Bash) Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/installation.rst This snippet shows the recommended way to install the Qiboml library using the standard Python package installer, pip. Note that this base installation does not include optional dependencies like PyTorch or TensorFlow. ```bash pip install qiboml ``` -------------------------------- ### Installing Qiboml from Source with Extras (Bash) Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/installation.rst When installing Qiboml from source, this command shows how to include optional dependencies, such as PyTorch and TensorFlow, by specifying them within square brackets using pip's extra dependencies syntax. ```bash pip install -e .[torch,tensorflow] ``` -------------------------------- ### Installing Required Libraries (Python) Source: https://github.com/qiboteam/qiboml/blob/main/tutorials/binary_mnist.ipynb Provides commented-out `pip install` commands to install the required libraries like `qiboml`, `torch`, `torchvision`, `torchmetrics`, and `matplotlib` for running the hybrid quantum-classical MNIST classification example. These commands are prerequisites for executing the rest of the code. ```python # qiboml, provides the means to build the quantum model # !pip install git+https://github.com/qiboteam/qiboml; # torch, handles the creation of all the classical layers # and provides the optimizer # !pip install torch; # some additional requirements to run this notebook # torchvision to gather the MNIST dataset # torchmetrics to evaluate the performance # matplotlib to do some plotting # !pip install torchvision torchmetrics matplotlib; ``` -------------------------------- ### Installing qiboml, PyTorch, and Dependencies (Python) Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/tutorials/binary_mnist.ipynb Installs necessary libraries for building and running the hybrid classical-quantum model. This includes `qiboml` for the quantum part, `torch` for the classical part and optimization, `torchvision` for dataset access, `torchmetrics` for evaluation, and `matplotlib` for plotting. ```python %%capture # qiboml, provides the means to build the quantum model !pip install git+https://github.com/qiboteam/qiboml; # torch, handles the creation of all the classical layers # and provides the optimizer !pip install torch; # some additional requirements to run this notebook # torchvision to gather the MNIST dataset # torchmetrics to evaluate the performance # matplotlib to do some plotting !pip install torchvision torchmetrics matplotlib; ``` -------------------------------- ### Referencing qiboml QuantumModel Interfaces in Python Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/interface.rst This snippet demonstrates importing the PyTorch and Keras interfaces for `qiboml`'s `QuantumModel` class. It shows how to alias the interface modules and then reference the specific `QuantumModel` class available in each interface for use in building quantum machine learning models within either framework. Dependencies include the `qiboml` library with both `pytorch` and `keras` interfaces installed. ```Python import qiboml.interfaces.pytorch as pt import qiboml.interfaces.keras as ks # the torch interface pt.QuantumModel # the keras interface ks.QuantumModel ``` -------------------------------- ### Setting Differentiation Engine for QuantumModel (Python) Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/differentiation.rst This snippet demonstrates how to explicitly set the autodifferentiation engine when instantiating a qiboml QuantumModel in Python. It shows examples of using both the Jax and Parameter Shift Rule (PSR) engines by passing them as the 'differentiation' argument to the QuantumModel constructor. This overrides the default engine selection. ```Python from qiboml.operations.differentiation import Jax, PSR from qiboml.interfaces.pytorch import QuantumModel # to use jax quantum_model = QuantumModel(encoding, circuit, decoding, differentiation=Jax) # to use PSR quantum_model = QuantumModel(encoding, circuit, decoding, differentiation=PSR) ``` -------------------------------- ### Building Quantum Model Components and Running Inference with PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/quickstart.rst Demonstrates how to construct the fundamental components of a qiboml quantum model, including encoder (PhaseEncoding), computation circuit (qibo.Circuit), and decoder (Expectation). It shows how to instantiate a QuantumModel using the qiboml PyTorch interface by combining these components and perform a basic forward pass using sample PyTorch tensor data. ```python import torch from qibo import Circuit, gates, hamiltonians from qiboml.models.encoding import PhaseEncoding from qiboml.models.decoding import Expectation from qiboml.interfaces.pytorch import QuantumModel # define the encoding encoding = PhaseEncoding(nqubits=3) # define the decoding given an observable observable = hamiltonians.Z(nqubits=3) decoding = Expectation(nqubits=3, observable=observable) # build the computation circuit circuit = Circuit(3) circuit.add((gates.RY(i, theta=0.4) for i in range(3))) circuit.add((gates.RZ(i, theta=0.2) for i in range(3))) circuit.add((gates.H(i) for i in range(3))) circuit.add((gates.CNOT(0,1), gates.CNOT(0,2))) circuit.draw() # join everything together through the torch interface quantum_model = QuantumModel(encoding, circuit, decoding) # run on some data data = torch.randn(3) outputs = quantum_model(data) ``` -------------------------------- ### Using Local Qiboml Backend Instances (Python) Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/backend.rst This code demonstrates creating local instances of qiboml backends like `PyTorchBackend` and `TensorflowBackend`. It shows how to assign a specific backend instance to a component (e.g., a decoding layer) upon initialization or switch it later, providing fine-grained control over backend usage for individual objects. ```python from qiboml.backends import PyTorchBackend, TensorflowBackend from qiboml.models.decoding import Probabilities # construct a local istance of the backend backend = PyTorchBackend() # assign it to the decoding layer upon initialization decoding = Probabilities(nqubits=3, backend=backend) # you can even switch it after initialization, # however, pay always attention to consistency! tf_backend = TensorflowBackend() decoding.set_backend(tf_backend) ``` -------------------------------- ### Training PyTorch Model Containing Quantum Component Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/quickstart.rst Shows the standard PyTorch optimization process applied to a composite model that includes the qiboml QuantumModel. It sets up an Adam optimizer, calculates MSE loss against a target, performs backpropagation using loss.backward(), and updates parameters with optimizer.step() in a basic training loop over 10 iterations. ```python optimizer = torch.optim.Adam(model.parameters()) data = torch.randn(8) for i in range(10): target = torch.tensor([[0.5]]) optimizer.zero_grad() outputs = model(data) loss = torch.nn.functional.mse_loss(outputs, target) loss.backward() optimizer.step() ``` -------------------------------- ### Integrating QuantumModel into PyTorch Sequential Container Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/quickstart.rst Illustrates the seamless integration of the qiboml QuantumModel, which is a torch.nn.Module, into a standard PyTorch torch.nn.Sequential container. It shows how to combine the quantum model with classical layers like Linear and Tanh and perform a forward pass through the combined model using sample PyTorch data. ```python linear = torch.nn.Linear(8, 3) activation = torch.nn.Tanh() model = torch.nn.Sequential( linear, activation, quantum_model, ) outputs = model(torch.randn(8)) ``` -------------------------------- ### Building qiboml Quantum Models with Agnostic Layers in Python Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/interface.rst This code snippet shows how to instantiate interface-agnostic encoding, ansatz, and decoding layers provided by `qiboml.models`. It then demonstrates using these same layer instances to construct both a PyTorch `QuantumModel` (`pt.QuantumModel`) and a Keras `QuantumModel` (`ks.QuantumModel`), highlighting the reusability of these components across interfaces. Dependencies include `qiboml` and the previously imported interface modules (`pt`, `ks`). ```Python from qiboml.models.encoding import BinaryEncoding from qiboml.models.decoding import Probabilities from qiboml.models.ansatze import ReuploadingCircuit # these are interface agnostic encoding = BinaryEncoding(2) decoding = Probabilities(2) circuit = ReuploadingCircuit(2) # build the torch model torch_model = pt.QuantumModel(encoding, circuit, decoding) # build the keras model keras_model = ks.QuantumModel(encoding, circuit, decoding) ``` -------------------------------- ### Constructing Quantum Classification Model with Qiboml (Python) Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/tutorials/binary_mnist.ipynb Defines the quantum part of the hybrid model using `qiboml`'s PyTorch interface. It sets the `qiboml` backend, defines a `PhaseEncoding` layer, constructs a simple parameterized `Circuit`, and specifies an `Expectation` decoding layer using the Z(0) observable. These components are then assembled into a `QuantumModel`. ```python import numpy as np from qibo import set_backend, Circuit, gates from qibo.symbols import Z from qibo.hamiltonians import SymbolicHamiltonian from qiboml.interfaces.pytorch import QuantumModel from qiboml.models.encoding import PhaseEncoding from qiboml.models.decoding import Expectation # then we build the quantum model, that will # act as a classification layer on top of the # classical image encoder # let's set the qibo global backend with the one we like, # e.g. pytorch set_backend(backend="qiboml", platform="pytorch") # we prepare a quantum encoder to encode the # classical data in a quantum circuit # the number of qubits has to be equal to the number of # features extracted on the preceding layer, i.e. the # outputs of the image encoder nqubits = n_out_features encoding = PhaseEncoding(nqubits=nqubits) # we construct a trainable parametrized circuit # that is the core of our quantum model circuit = Circuit(nqubits) for _ in range(5): for q in range(nqubits): circuit.add(gates.RY(q, theta=np.random.randn() * np.pi)) circuit.add(gates.RZ(q, theta=np.random.randn() * np.pi)) circuit.add(gates.CNOT(0,1)) print("Trainable Circuit:") circuit.draw() # and finally we need a decoder to decode the quantum # information and extract the classical predictions, # for instance the expectation value calculation # for this we need to define the observable we wish to # measure observable = SymbolicHamiltonian(Z(0), nqubits=nqubits) # and then construct the expectation decoder decoding = Expectation(nqubits=nqubits, observable=observable) # we can then build the complete quantum model q_model = QuantumModel( encoding, circuit, decoding ) ``` -------------------------------- ### Assembling Sequential Hybrid Model PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/tutorials/binary_mnist.ipynb Constructs the complete hybrid classical-quantum model pipeline using `torch.nn.Sequential`. It stacks the classical image encoder (`img_encoder`), the custom PiTanh activation function (`activation`), and the quantum model (`q_model`) in sequence. The structure of the resulting model is then printed. ```python # Finally, the complete hybrid classical-quantum pipeline can # be built by stacking the different components in a # torch.nn.Sequential as usual model = nn.Sequential( img_encoder, activation, q_model, ) print(model) ``` -------------------------------- ### Loading and Filtering MNIST Dataset (Python) Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/tutorials/binary_mnist.ipynb Loads the full MNIST dataset using `torchvision`, filters it to retain only images labeled 0 or 1, and prepares separate training and testing sets containing 100 samples each. It also demonstrates how to access and display a sample image and its label. ```python from torchvision.datasets import MNIST # get the MNIST dataset dataset = MNIST("./", download=True) # each element is an image of shape 28x28 print(f"First element of the dataset:\nlabel: {dataset[0][1]}\nshape: {dataset.data[0].shape}\nimage:\n") display(dataset[0][0]) # we keep only the zeros and ones for binary classification zeros_and_ones = [i for i, d in enumerate(dataset) if d[1] in (0,1)] # for this small example 100 data for training should be enough # we also extract 100 other data for testing # in practice we take the first 200 zeros and ones found in the dataset train_data, test_data = dataset.data[zeros_and_ones][:200].double().view(2, 100, 1, 28, 28) train_targets, test_targets = dataset.targets[zeros_and_ones][:200].double().view(2, 100, 1) # flatten the targets train_targets, test_targets = train_targets.view(-1,), test_targets.view(-1,) train_images = [dataset[i][0] for i in zeros_and_ones][:100] test_images = [dataset[i][0] for i in zeros_and_ones][100:200] print(f"\nFirst element of our binary classification task:\nlabel: {int(train_targets[0])}\nimage:\n") display(train_images[0]) ``` -------------------------------- ### Compiling and Using Keras QuantumModel with Keras API in Python Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/interface.rst This snippet illustrates how a `keras_model` instance, built using `qiboml`'s Keras interface, can be directly integrated into standard Keras workflows. It shows configuring an Adam optimizer and compiling the model with a categorical crossentropy loss, followed by a placeholder `fit(...)` call to indicate training. Prerequisites include a built `keras_model` object and the Keras library. ```Python import keras opt = keras.optimizers.Adam(learning_rate=0.01) keras_model.compile(loss='categorical_crossentropy', optimizer=opt) keras_model.fit(...) ``` -------------------------------- ### Setting Global Qiboml Backend (Python) Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/getting_started/backend.rst This snippet shows how to set the global execution backend for qiboml using the `qibo.set_backend` function. It illustrates how to switch between 'jax', 'pytorch', and 'tensorflow' platforms, which affects all qiboml operations globally. ```python from qibo import set_backend # set jax set_backend("qiboml", platform="jax") # set torch set_backend("qiboml", platform="pytorch") # set tensorflow set_backend("qiboml", platform="tensorflow") ``` -------------------------------- ### Assembling Sequential Hybrid Model PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/tutorials/binary_mnist.ipynb Creates a `torch.nn.Sequential` model by stacking previously defined components: `img_encoder` (classical part), `activation` (the custom `PiTanh`), and `q_model` (quantum part). This structure allows treating the entire pipeline as a single PyTorch module. The snippet then prints the model structure for inspection. ```Python # Finally, the complete hybrid classical-quantum pipeline can\n# be built by stacking the different components in a \n# torch.nn.Sequential as usual\nmodel = nn.Sequential(\n img_encoder,\n activation,\n q_model,\n)\n\nprint(model) ``` -------------------------------- ### Defining Quantum Classification Model (qiboml/PyTorch) Source: https://github.com/qiboteam/qiboml/blob/main/tutorials/binary_mnist.ipynb Constructs the quantum part of the hybrid model using `qiboml`'s PyTorch interface. It sets the `qiboml` backend to PyTorch, defines a `PhaseEncoding`, a custom variational `Circuit`, and an `Expectation` decoding based on a Pauli-Z observable, then combines them into a `QuantumModel`. ```python import numpy as np from qibo import set_backend, Circuit, gates from qibo.symbols import Z from qibo.hamiltonians import SymbolicHamiltonian from qiboml.interfaces.pytorch import QuantumModel from qiboml.models.encoding import PhaseEncoding from qiboml.models.decoding import Expectation # then we build the quantum model, that will # act as a classification layer on top of the # classical image encoder # let's set the qibo global backend with the one we like, # e.g. pytorch set_backend(backend="qiboml", platform="pytorch") # we prepare a quantum encoder to encode the # classical data in a quantum circuit # the number of qubits has to be equal to the number of # features extracted on the preceding layer, i.e. the # outputs of the image encoder nqubits = n_out_features encoding = PhaseEncoding(nqubits=nqubits) # we construct a trainable parametrized circuit # that is the core of our quantum model circuit = Circuit(nqubits) for _ in range(5): for q in range(nqubits): circuit.add(gates.RY(q, theta=np.random.randn() * np.pi)) circuit.add(gates.RZ(q, theta=np.random.randn() * np.pi)) circuit.add(gates.CNOT(0,1)) print("Trainable Circuit:") circuit.draw() # and finally we need a decoder to decode the quantum # information and extract the classical predictions, # for instance the expectation value calculation # for this we need to define the observable we wish to # measure observable = SymbolicHamiltonian(Z(0), nqubits=nqubits) # and then construct the expectation decoder decoding = Expectation(nqubits=nqubits, observable=observable) # we can then build the complete quantum model q_model = QuantumModel( encoding, circuit, decoding ) ``` -------------------------------- ### Training Hybrid Model with Adam PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/tutorials/binary_mnist.ipynb Implements the training loop for the hybrid model using the Adam optimizer and binary cross-entropy loss. The data is trained for 5 epochs, with parameters updated after processing each sample. The loss for each iteration is recorded, and the evolution of the loss is plotted to visualize the training progress. The average loss over the last 20 iterations is also printed. ```python import matplotlib.pyplot as plt from torch.optim import Adam # now we can train the model as we would do # with any other pytorch model # let's use the torch.optim.Adam optimizer optimizer = Adam(model.parameters()) # we are going to train for 5 epochs losses = [] for _ in range(5): # reshuffle the data before each epoch permutation = torch.randperm(len(train_data)) for x, y in zip(train_data[permutation], train_targets[permutation]): optimizer.zero_grad() # get the predictions out = model(x) # calculate the loss, we can take the # standard binary cross entropy loss = F.binary_cross_entropy_with_logits(out.view(1,), y.view(1,)) # backpropagate loss.backward() # update the parameters optimizer.step() losses.append(loss.item()) # we can plot the training loss over the epochs plt.plot(range(len(losses)), losses) plt.ylabel("Loss") plt.xlabel("Iteration") # for reference we calculate the average loss obtained for # the last 20 predictions after training print(f"Final Loss: {sum(losses[-20:])/20}") ``` -------------------------------- ### Implementing a Custom Heterogeneous Encoder in Python Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/advanced/encoder.rst This snippet defines a custom quantum encoder class `HeterogeneousEncoder` by inheriting from `qiboml.models.encoding.QuantumEncoding`. It demonstrates how to initialize the encoder with different qubit assignments for real and binary data parts and how to implement the `__call__` method to construct a `qibo.Circuit` that encodes the input data using `RY` gates for the real part and `RX` gates for the binary part. ```python import numpy as np from qibo import gates from qiboml.models.encoding import QuantumEncoding class HeterogeneousEncoder(QuantumEncoding): def __init__(self, nqubits: int, real_part_len: int, bin_part_len: int): if real_part_len + bin_part_len != nqubits: raise RuntimeError( "``real_part_len`` and ``bin_part_len`` don't sum to ``nqubits``." ) # use the general setup for a QuantumEncoding layer # which mainly initialize an empty n-qubits circuit (self.circuit) # and the set of qubits it insists on (by default self.qubits=range(nqubits)) super().__init__(nqubits) self.real_qubits = self.qubits[:real_part_len] self.bin_qubits = self.qubits[real_part_len:] def __call__(self, x: "ndarray") -> "Circuit": # check that the data is binary if not all((x[1] == 0) | (x[1] == 1)): raise RuntimeError("Received non binary data") # copy the internal circuit as we don't want to modify that # every time a new input is processed circuit = self.circuit.copy() # encode the real data # the first row of x contains the real data for qubit, value in zip(self.real_qubits, x[0]): circuit.add(gates.RY(qubit, theta=value, trainable=False)) # encode the binary data # the second row contains the binary data for qubit, bit in zip(self.bin_qubits, x[1]): circuit.add(gates.RX(qubit, theta=bit * np.pi, trainable=False)) return circuit ``` -------------------------------- ### Loading and Filtering MNIST Dataset (Python) Source: https://github.com/qiboteam/qiboml/blob/main/tutorials/binary_mnist.ipynb Loads the full MNIST dataset using `torchvision.datasets.MNIST`, filters it to include only images labeled 0 or 1, and extracts the first 100 samples for training and the next 100 for testing. It also prepares the data and targets in the required double precision format and performs initial checks. ```python from torchvision.datasets import MNIST # get the MNIST dataset dataset = MNIST("./", download=True) # each element is an image of shape 28x28 print(f"First element of the dataset:\nlabel: {dataset[0][1]}\nshape: {dataset.data[0].shape}\nimage:\n") display(dataset[0][0]) # we keep only the zeros and ones for binary classification zeros_and_ones = [i for i, d in enumerate(dataset) if d[1] in (0,1)] # for this small example 100 data for training should be enough # we also extract 100 other data for testing # in practice we take the first 200 zeros and ones found in the dataset train_data, test_data = dataset.data[zeros_and_ones][:200].double().view(2, 100, 1, 28, 28) train_targets, test_targets = dataset.targets[zeros_and_ones][:200].double().view(2, 100, 1) # flatten the targets train_targets, test_targets = train_targets.view(-1,), test_targets.view(-1,) train_images = [dataset[i][0] for i in zeros_and_ones][:100] test_images = [dataset[i][0] for i in zeros_and_ones][100:200] print(f"\nFirst element of our binary classification task:\nlabel: {int(train_targets[0])}\nimage:\n") display(train_images[0]) ``` -------------------------------- ### Training Hybrid Model with Adam PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/tutorials/binary_mnist.ipynb Trains the `model` using the `torch.optim.Adam` optimizer and `F.binary_cross_entropy_with_logits`. It iterates for 5 epochs, reshuffling the training data each epoch. For each sample, it performs a forward pass, calculates the loss, backpropagates gradients, and updates parameters. Loss values are collected and stored in a list `losses`. The snippet also plots the training loss evolution and prints the average loss for the last 20 iterations. Requires `matplotlib`. ```Python import matplotlib.pyplot as plt\nfrom torch.optim import Adam\n\n# now we can train the model as we would do\n# with any other pytorch model\n\n# let's use the torch.optim.Adam optimizer\noptimizer = Adam(model.parameters())\n# we are going to train for 5 epochs\nlosses = []\nfor _ in range(5):\n # reshuffle the data before each epoch\n permutation = torch.randperm(len(train_data))\n for x, y in zip(train_data[permutation], train_targets[permutation]):\n optimizer.zero_grad()\n # get the predictions\n out = model(x)\n # calculate the loss, we can take the\n # standard binary cross entropy\n loss = F.binary_cross_entropy_with_logits(out.view(1,), y.view(1,))\n # backpropagate\n loss.backward()\n # update the parameters\n optimizer.step()\n losses.append(loss.item())\n\n# we can plot the training loss over the epochs\nplt.plot(range(len(losses)), losses)\nplt.ylabel("Loss")\nplt.xlabel("Iteration")\n# for reference we calculate the average loss obtained for\n# the last 20 predictions after training\nprint(f"Final Loss: {sum(losses[-20:])/20}") ``` -------------------------------- ### Specify Custom Backend in Decoder Python Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/advanced/decoder.rst This Python snippet shows how to explicitly assign and use a custom backend within a decoder class `MyCustomDecoderWithCustomBackend`, instead of relying on the default backend handling of the base class. The custom backend (`MyCustomBackend`, assumed to exist) is set in the `__init__` method and passed to the observables. The `__call__` method directly uses the custom backend's `execute_circuit` method. It's crucial that the backend used by the decoder and any dependent objects (like `SymbolicHamiltonian` observables) are consistent. ```Python class MyCustomDecoderWithCustomBackend(QuantumDecoding): # always use my custom backend for execution and # expectation value calculation def __init__(self, nqubits: int): self.backend = MyCustomBackend() # the backends should match! self.o_even = SymbolicHamiltonian(Z(0)*Z(2), nqubits=nqubits, backend=self.backend) self.o_odd = SymbolicHamiltonian(Z(1)*Z(3), nqubits=nqubits, backend=self.backend) def __call__(self, x: Circuit): final_state = self.backend.execute_circuit(x).state() exp_even = self.o_even.expectation(final_state) exp_odd = self.o_odd.expectation(final_state) return np.abs(exp_even - exp_odd) ``` -------------------------------- ### Composing Trainable Encoding Layers in Python Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/advanced/encoder.rst This snippet demonstrates the recommended approach for creating trainable encoding transformations. Instead of making the encoder itself trainable, a separate trainable layer (`MyParametrizedTransformation`) is defined and placed *before* the quantum model (`quantum_model`) within a `Sequential` composition (typical in frameworks like PyTorch or TensorFlow). ```python # build your trainable transformation g = MyParametrizedTransformation(theta) # and stack it to the actual quantum model encoding_tunable_model = Sequential( g, quantum_model ) ``` -------------------------------- ### Evaluating Untrained Model Performance PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/tutorials/binary_mnist.ipynb Evaluates the assembled model's performance on the test set *before* any training occurs. It uses the binary F1 score metric to assess the model's initial state, calculating predictions without gradient tracking. It also displays sample test images and their corresponding raw and rounded predictions. ```python # to evaluate the performance of the model we can use the # standard binary F1 score from torcheval.metrics.functional import binary_f1_score # let's check how the model does before training on the test set with torch.no_grad(): predictions = torch.as_tensor([F.sigmoid(model(x)) for x in test_data]) print(f"Untrained F1 score: {binary_f1_score(predictions, test_targets)}\n") # we can also manually display and check some predictions and input images for prediction, image in zip(predictions[:5], test_images[:5]): print(f"Prediction: {prediction.round()}\nImage:") display(image) ``` -------------------------------- ### Implement Basic Custom Decoder Python Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/advanced/decoder.rst This Python snippet demonstrates how to create a custom decoder class `MyCustomDecoder` by subclassing `QuantumDecoding`. It calculates the absolute difference between the expectation values of two predefined observables (`Z0*Z2` and `Z1*Z3`) on the final state of the input circuit. It requires `numpy`, `qiboml.models.decoding.QuantumDecoding`, and Qibo components like `Circuit`, `Z`, and `SymbolicHamiltonian`. The `__init__` method initializes the observables, the `__call__` method executes the circuit, calculates expectation values, and returns their absolute difference, and `output_shape` is specified. ```Python import numpy as np from qiboml.models.decoding import QuantumDecoding from qibo import Circuit from qibo.symbols import Z from qibo.hamiltonians import SymbolicHamiltonian class MyCustomDecoder(QuantumDecoding): def __init__(self, nqubits: int): super().__init__(nqubits) # build the observables using qibo's SymbolicHamiltonian self.o_even = SymbolicHamiltonian(Z(0)*Z(2), nqubits=nqubits) self.o_odd = SymbolicHamiltonian(Z(1)*Z(3), nqubits=nqubits) def __call__(self, x: Circuit): # execute the circuit and collect the final state final_state = super().__call__(x).state() # calculate the expectation values exp_even = self.o_even.expectation(final_state) exp_odd = self.o_odd.expectation(final_state) # use numpy to calculate the distance return np.abs(exp_even - exp_odd) # specify the shape of the output @property def output_shape(self) -> tuple[int]: return (1, 1) ``` -------------------------------- ### Evaluating Trained Model Performance PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/tutorials/binary_mnist.ipynb Calculates the F1 score of the *trained* `model` on the `test_data` using `torcheval.metrics.functional.binary_f1_score`, similar to the pre-training evaluation. It uses `torch.no_grad()` and applies `F.sigmoid`. This allows comparison with the untrained score to assess training effectiveness. It also manually displays the first 5 rounded predictions and corresponding input images from the trained model. Requires `torcheval` and potentially a `display` function. ```Python # Finally, let's double check that the F1 score has improved\nwith torch.no_grad():\n predictions = torch.as_tensor([F.sigmoid(model(x)) for x in test_data])\n print(f"Trained F1 score: {binary_f1_score(predictions, test_targets)}")\n\n# once again, manual inspection of the predictions and the input images\n# should confirm the recorded score\nfor prediction, image in zip(predictions[:5], test_images[:5]):\n print(f"Prediction: {prediction.round()}\nImage:")\n display(image) ``` -------------------------------- ### Evaluating Trained Model Performance PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/tutorials/binary_mnist.ipynb Evaluates the model's performance on the test set *after* training is complete. It calculates the binary F1 score again to demonstrate the improvement achieved through training, also performed without gradient tracking. Sample test images and their corresponding predictions from the trained model are displayed for manual inspection. ```python # Finally, let's double check that the F1 score has improved with torch.no_grad(): predictions = torch.as_tensor([F.sigmoid(model(x)) for x in test_data]) print(f"Trained F1 score: {binary_f1_score(predictions, test_targets)}") # once again, manual inspection of the predictions and the input images # should confirm the recorded score for prediction, image in zip(predictions[:5], test_images[:5]): print(f"Prediction: {prediction.round()}\nImage:") display(image) ``` -------------------------------- ### Defining Classical Image Encoder with PyTorch (Python) Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/tutorials/binary_mnist.ipynb Defines a `torch.nn.Module` named `ImageEncoder` consisting of convolutional, max pooling, and linear layers. This network takes a 28x28 grayscale image as input and outputs a fixed-size feature vector (`n_out_features`), which will be used as input for the quantum model. The model is cast to double precision. ```python import torch import torch.nn as nn import torch.nn.functional as F # we can define a classical image encoder # composed of a combination of convolutional, pooling and linear layers # number of extracted output features n_out_features = 2 class ImageEncoder(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Conv2d(1, 3, 3) self.pool = nn.MaxPool2d(2, 2) self.fc1 = nn.Linear(3 * 13 * 13, 32) self.fc3 = nn.Linear(32, n_out_features) def forward(self, x): x = self.pool(F.relu(self.conv1(x))) x = torch.flatten(x) x = F.relu(self.fc1(x)) x = self.fc3(x) return x # we cast the layer to double precision, as this is the # default dtype used in qiboml, and more in general # for quantum simulation # this could could also be achieved by setting the torch # default dtype at the beginning with `torch.set_default_dtype(torch.float64)` img_encoder = ImageEncoder().double() ``` -------------------------------- ### Defining Classical Image Encoder (PyTorch) Source: https://github.com/qiboteam/qiboml/blob/main/tutorials/binary_mnist.ipynb Implements a classical image encoder using PyTorch's `nn.Module`. It consists of convolutional, pooling, and linear layers to process a 28x28 input image and output a feature vector of size `n_out_features`. The module is cast to double precision to match quantum simulation requirements. ```python import torch import torch.nn as nn import torch.nn.functional as F # we can define a classical image encoder # composed of a combination of convolutional, pooling and linear layers # number of extracted output features n_out_features = 2 class ImageEncoder(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Conv2d(1, 3, 3) self.pool = nn.MaxPool2d(2, 2) self.fc1 = nn.Linear(3 * 13 * 13, 32) self.fc3 = nn.Linear(32, n_out_features) def forward(self, x): x = self.pool(F.relu(self.conv1(x))) x = torch.flatten(x) x = F.relu(self.fc1(x)) x = self.fc3(x) return x # we cast the layer to double precision, as this is the # default dtype used in qiboml, and more in general # for quantum simulation # this could could also be achieved by setting the torch # default dtype at the beginning with `torch.set_default_dtype(torch.float64)` img_encoder = ImageEncoder().double() ``` -------------------------------- ### Defining PiTanh Activation Module PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/tutorials/binary_mnist.ipynb Defines a custom PyTorch `nn.Module` called `PiTanh`. Its `forward` method first scales the input by its maximum value to prevent tanh saturation, then applies `torch.tanh` and scales the result by `np.pi` to map the output to the (-pi, pi) range. It's intended for use as an activation function after an image encoder before a quantum model, particularly when angular encoding is used. ```Python class PiTanh(nn.Module):\n\n def forward(self, x):\n # we first rescale x to avoid the risk\n # of saturating the tanh too often and, thus,\n # producing always the same angle\n x = x / x.max()\n # then we just apply the tanh and rescale by pi\n return np.pi * F.tanh(x)\n\nactivation = PiTanh().double() ``` -------------------------------- ### Evaluating Untrained Model Performance PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/tutorials/binary_mnist.ipynb Calculates the F1 score of the untrained `model` on the `test_data` using `torcheval.metrics.functional.binary_f1_score`. It uses `torch.no_grad()` to disable gradient calculation during evaluation and applies `F.sigmoid` to the model output before calculating predictions. It also manually displays the first 5 rounded predictions and corresponding input images. Requires `torcheval` and potentially a `display` function. ```Python # to evaluate the performance of the model we can use the\n# standard binary F1 score\nfrom torcheval.metrics.functional import binary_f1_score\n\n# let's check how the model does before training on the test set\nwith torch.no_grad():\n predictions = torch.as_tensor([F.sigmoid(model(x)) for x in test_data])\n print(f"Untrained F1 score: {binary_f1_score(predictions, test_targets)}\n")\n\n# we can also manually display and check some predictions and input images \nfor prediction, image in zip(predictions[:5], test_images[:5]):\n print(f"Prediction: {prediction.round()}\nImage:")\n display(image) ``` -------------------------------- ### Defining Custom PiTanh Activation Function PyTorch Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/tutorials/binary_mnist.ipynb Defines a custom PyTorch module, PiTanh, that acts as an activation function. It rescales the input tensor by its maximum value to prevent saturation, applies the tanh function, and then scales the output by pi to map the results into the (-pi, pi) interval, suitable for angular encoding. ```python # Since we are using an angular encoding, it is better to # make sure that the output of the img_encoder are meaningful # angles. To do so we can define a custom torch activation # that rescales everything in the interval (-pi, pi) class PiTanh(nn.Module): def forward(self, x): # we first rescale x to avoid the risk # of saturating the tanh too often and, thus, # producing always the same angle x = x / x.max() # then we just apply the tanh and rescale by pi return np.pi * F.tanh(x) activation = PiTanh().double() ``` -------------------------------- ### Overriding the Differentiable Property in Python Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/advanced/encoder.rst This snippet shows how to override the default `differentiable` property in a custom `QuantumEncoding` class. By redifining this property, you can control whether the encoder's input derivatives are calculated during differentiation, influencing the gradient flow in a larger model. ```python @property def differentiable(self) -> bool: if is_my_encoder_differentiable: return True return False ``` -------------------------------- ### Define Analytic Property in Decoder Python Source: https://github.com/qiboteam/qiboml/blob/main/doc/source/advanced/decoder.rst This Python snippet illustrates how to define the `analytic` property in a custom decoder class. This boolean property indicates whether the decoding operation is analytically differentiable (e.g., if no sampling is involved). This information is used by Qiboml for gradient calculation. By default, this property is `True` in the base `QuantumDecoding` class, but it can be overridden as shown here to reflect the actual differentiability of the custom decoding logic. ```Python class MyCustomDecoder(QuantumDecoding): @property def analytic(self,) -> bool: if is_my_custom_decoder_differentiable: return True return False ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.