# Explore different approaches to simulate quantum systems

An overview of the choices and tradeoffs in deciding how to run a simulation

The Boulder Opal graph framework is a very flexible tool for the optimization and simulation of quantum systems. As part of this graph framework, Boulder Opal offers a series of graph operations that you can use to integrate the dynamics of a quantum system and determine how it evolves in time. To do this, Boulder Opal leverages its cloud infrastructure to run the calculations without taking memory or compute resources from you local machine.

To make the most of each simulation, you can choose the tools that are the most appropriate for your purposes. The choice of the graph operation to use will depend on the system that you want to simulate and on the quantity that you are interested in extracting from the simulation. You can find these graph operations under the heading "Time evolution" in our reference documentation. For general information about graphs in Boulder Opal, see the topic Understanding graphs in Boulder Opal.

## Simulation of closed quantum systems

For closed quantum systems, complete information about the evolution of the system can be obtained from the unitary time-evolution operators that come from solving the Schrödinger equation. You can express the controls that characterize your system's evolution in either piecewise-constant (PWC) form, or as smooth functions in the form sampleable tensor-valued functions (STF) of time. To learn more about the difference between these two, read our Working with time-dependent functions of time topic.

If the controls in your system are piecewise-constant (PWC), you can use the operation `graph.time_evolution_operators_pwc`

to obtain the exact unitaries.
This graph operation is adequate for systems that don't involve many dimensions.
Note that if your simulation involves a small system that is represented by a PWC, you might additionally benefit from eager execution.
To read more about this, see our topic Improving calculation performance in graphs.

If the controls of your system are continuous, you can express them as sampleable tensor-valued functions (STF).
If you decide to use STFs, you can obtain the time-evolution operators using the operation `graph.time_evolution_operators_stf`

.
As these functions can have arbitrary form, their integration is approximated by sampling it at the points you specify with the `evolution_times`

parameter.
The more points that you pass, the greater the precision of the integration.
On the other hand, the fewer points you pass, the fastest your integration will be.

It is possible that you are interested in the evolution of a specific initial state of the quantum system that you are considering, rather than the complete operation given by the unitary time evolution matrix.
In this case, it is easy to obtain the state vector evolution from the unitary matrices: you just need to multiply them by the initial state via the `graph.matmul`

operation (or its shorthand `@`

).
For an example of how to do this, see the user guide How to simulate quantum dynamics for noiseless systems using graphs.

### Adding noise

If your quantum system is subject to noise that is characterized by a spectral density, you can combine the methods presented previously with the capability of sampling from the noise distribution. By taking samples from this distribution and replacing them in the terms of the Hamiltonian that represent the noise, you can simulate time evolution trajectories of your system for particular realizations of the noise.

This possibility of sampling from probability distributions is provided by the random operations provided by the graph framework. You can find an example of how to do this in the user guide How to simulate quantum dynamics subject to noise with graphs.

## Simulation of large systems

The number of dimensions of a quantum system grows exponentially with the number of qubits that are included in it. You can use some specific strategies to improve the performance of a simulation of a system when the number of dimension is very large.

### State propagation

To simulate a close quantum system of a large size, it is recommended to propagate only the state vector of the system, rather than all the unitary time evolution operators. This is because the size of the matrices grows much faster than the size of the state vectors. For example, if we have a 32-dimensional system, the state vector will store 32 numbers, while the unitary evolution operator will store $32 \times 32 = 1024$ numbers. It is more efficient to just propagate the state vector, in this case.

You can use the graph operation `graph.state_evolution_pwc`

to obtain the approximate evolution of a state vector for a closed system.
This operation uses the Lanczos algorithm to perform the evolution using a subsystem with a smaller number of dimensions, which is efficient for large-dimension systems.
Increase the `krylov_subspace_dimension`

parameter to improve the precision of the simulation, or reduce it to make the simulation run faster.

### Sparse matrices

Another recommendation for large systems is to use sparse matrices to reduce the amount of data that needs to be stored. This happens because matrices in sparse format do not store the elements that have value zero, which might be most of the elements of a high-dimensional operator.

For example, imagine you have a five-qubit system, and you want to represent a term of the kind $I \otimes \sigma_x \otimes \sigma_x \otimes I \otimes I$. In this case, the operator would be a $32 \times 32$ square matrix, containing a total of 1024 elements. However, only 32 of these elements are not zero. It is therefore more efficient to only store the values of these non-zero elements, and to only perform operations on these non-zero values.

You can create a sparse PWC representation of your closed system using the graph operations for large systems, like `graph.sparse_pwc_operator`

.
Then, you can evolve the system described by these sparse PWCs using the graph operation `graph.state_evolution_pwc`

.
This function only propagates the state vector, which is adequate for larger systems, as described in the previous section.

## Simulation of open quantum systems

The evolution of open quantum systems is not unitary, and therefore their states should be represented by density matrices rather than state vectors.
To obtain the evolution of a density matrix according to a GKS–Lindblad equation, you can use the graph operation `graph.density_matrix_evolution_pwc`

.
For an example of how to use the graph operation `graph.density_matrix_evolution_pwc`

, see the user guide How to simulate open system dynamics.

If the system that you desire to simulate has many dimensions, the approach of using sparse matrices will save memory and improve performance, as many of the elements of these large matrices are zeros and can be omitted.
For an example of how to use the graph operation `graph.density_matrix_evolution_pwc`

together with sparse matrices, see the user guide How to simulate large open system dynamics.

Boulder Opal also comes with a dedicated operation for calculating the steady state of an open system, `graph.steady_state`

.
You can use it in place of the full density matrix evolution if you are only interested in the final steady state of the system, in which case this operation might be more efficient.
Note that in this kind of situation you will be simulating the effects of constant Hamiltonian and Lindblad operators, rather than time-dependent controls.
For an example, see the user guide How to calculate the steady state of an open quantum system.

## Simulation of quantum circuits

If you know the Hamiltonian that describes your quantum computer, the simulation of a circuit is a specific case of the simulation of a quantum system. In that case, you can concatenate the pulses that form each of the gates of the circuit to create the complete time evolution of the circuit. The same recommendations from the previous sections apply in this case, depending on the size of your circuits, and on whether your quantum computer is better modeled as an open system or as a closed system.

To see an example of how to do this, read the How to simulate multi-qubit circuits in quantum computing user guide.

## Next steps

For more information about the time evolution graph operations, see our reference documentation. You can also read more about how to improve the performance of the Boulder Opal calculations in general in the topic Improving calculation performance in graphs.