Core

The core module contains the most important code parts used in zyglrox

Circuit

The Circuit class contains all functionality for constructing quantum circuits and calculating observables. The circuit is defined by passing a list of Gate objects to the constructor. The combination of \(M\) quantum gates is equal to a single unitary operation

\[\mathcal{U} \equiv \prod_i^M \mathcal{U}_i\]

Our quantum computer then calculates the state

\[|\psi\rangle = \mathcal{U} |0\rangle\]

If the gates \(\mathcal{U}_i\) are paramterized by some parameter \(\theta_i\), so \(\mathcal{U}_i\to\mathcal{U}_i(\theta_i)\), we have what is called a variational quantum circuit

\[|\psi(\theta)\rangle = \mathcal{U}(\theta) |0\rangle\]

These variational quantum circuits are essential for algorithms such as the Quantum Variational Eigensolver, the Quantum Boltzmann Machine or Quantum Approximate Adiabatic Optimization. For more information on the available gates in zyglrox, see the section about Gates.

Note

This class inherits from the tf.keras.models.Model class, and can thus be accessed in a similar fashion as other Keras Models. This allows for easy integration of your variational quantum circuit with the deep learning tools available in TensorFlow and Keras.

class zyglrox.core.circuit.QuantumCircuit(tf.keras.models.Model)

Bases: object

Quantum Circuit TensorFlow Interface

This class is an interface to build quantum circuits using TensorFlow. On initialization, the graph for the circuit is constructed. The circuit is defined by passing a list of Gate objects.

Args:
nqubits (int):

Number of qubits.

gates (list):

List of Gate objects.

tensorboard (bool):

Boolean that indicates whether we store the store meta data for tensorboard. Default false.

Keyword Arguments
batch_size (int):

Number of parallel iterations for map_fn when using batches of parammeters.

Returns (inplace):

None

initialize() → None

Initialize session, variables, and the Tensorboard writer.

Args:

device (string):

Device of choice for running tensorflow.

Returns (inplace):

None

execute() → tensorflow.python.framework.ops.Tensor

Exectute the circuit, starting from the initial zero state \(|0\rangle^{\otimes N}\) .

Returns (Tensor):

Circuit output, a wave function of shape (None, 2,…,2).

call(inputs, training=False) → tensorflow.python.framework.ops.Tensor

Call the circuit for a given wave function or batch of wave functions

Args:
inputs (Tensor):

Input tensor corresponding to the wave function of shape (None, 2,…,2). In the case of a single wave function we get a shape (1,2,…,2)

training (Bool):

Indicates whether to run the circuit in training mode or inference mode.

Returns (Tensor):

Circuit output, a wave function of shape (None, 2,…,2).

set_parameters(theta)

Set the parameters of the quantum circuit by an external input. Also works for batches of parameters if the QuantumCircuit has been initialized with batch_params=True.

Args:
theta (tensor):

Parameters to be fed into the circuit, either of dimension (1, nparams, 1) or (None,nparams,1) where nparams is the total number of parameters in the circuit.

Returns:

print_layer_ordering()

Print the layer gates per layer.

Returns (inplace):

None

draw()

Draw the circuit using matplotlib.

Returns (inplace):

None

Gates

A quantum gate acting \(\mathcal{U}\) on a single qubit \(i\), acts on full the tensor subspace of the \(N\)-qubit state vector. To perform this calculation, we can calculate the tensor representation of this operation on the full state space as follows:

\[\mathcal{U}_N = \otimes^{i-1}_{j=0} I \otimes \mathcal{U} \otimes^{N}_{j=i+1} I\]

However, this provides a lot of overhead, since for every gate we need to calculate and store a \(2^N\times2^N\) matrix.

A more efficient method is to only let the quantum gate act on the relevant tensor subspace. Throughout zyglrox, the wave function \(|\psi\rangle^{\otimes N}\) is stored as a tensor of shape \((2,2,\ldots,2)\). The gate operation can then be performed as:

\[|\hat{\psi}\rangle = \otimes^{i-1}_{j=0} |\psi_j\rangle \otimes \mathcal{U} |\psi_i\rangle \otimes^{N}_{j=i+1} |\psi_j\rangle\]

Which can be implemented in tensorflow in the following way:

phi_hat = tf.tensordot(self.op, phi,
                       axes=[list(range(self.nqubits, 2 * self.nqubits)), self.wires]
                       )
# tensordot sets the contracted axes first, so we need to reshuffle them
unused_idxs = [idx for idx in range(self.total_qubits) if idx not in self.wires]
perm = self.wires + unused_idxs
inv_perm = np.argsort(perm)
tf.transpose(phi, perm=inv_perm)
class zyglrox.core.gates.Gate(nparams: int, wires: List[int], value: List[float] = None, name=None, **kwargs)

Abstract Quantum Gate class

The Gate class performs unitary quantum gates on the tensor subspace of a wave function

Args:
nparams (int):

Number of parameters of gate.

wires (list):

List of numbers on which the gate is acting.

value (None of ndarray):

Intial value of parameterized gate in 1D numpy array. When combined with setting trainable=False, this will create a static gate that cannot be altered during any optimization.

name (str):

Name of the gate.

Keyword Arguments
trainable (bool):

Boolean that indicates whether the paramaters of this gate are trainable.

Returns (inplace):

None

set_external_input(external_input: tensorflow.python.framework.ops.Tensor)

Connect an external tensor to the gate parameters. Tensor must have shape (nparams, 1). This function enables the construction of hybrid architectures by having the parameters of the circuit be fed from some external model.

Args:
external_input (Tensor):

Tensor of shape (nparams, 1) with gate parameters.

Returns (inplace):

None

build(input_shape)

Called once from __call__, when we know the shapes of inputs and dtype. Should initialize the trainable variables, and call the super’s build().

Args:
input_shape (list):

Input shapes of the incoming tensor.

call(inputs: tensorflow.python.framework.ops.Tensor, **kwargs) → tensorflow.python.framework.ops.Tensor

Gate Logic goes here.

Gate Templates

Below is a table of the most commonly used quantum gates that are already implemented in zyglrox.

gates.CNOT(wires[, value, conjugate, name])

Gate that implements the CNOT unitary operation.

gates.Hadamard(wires[, value, conjugate, name])

Gate that implements the Hadamard unitary operation.

gates.PauliX(wires[, value, conjugate, name])

Gate that implements the Pauli X unitary operation.

gates.PauliY(wires[, value, conjugate, name])

Gate that implements the Pauli Y unitary operation.

gates.PauliZ(wires[, value, conjugate, name])

Gate that implements the Pauli Z unitary operation.

gates.RX(wires[, value, conjugate, name])

Gate that implements a rotation around the spin x-axis.

gates.RY(wires[, value, conjugate, name])

Gate that implements a rotation around the spin y-axis.

gates.RZ(wires[, value, conjugate, name])

Gate that implements a rotation around the spin z-axis.

gates.R3(wires[, value, conjugate, name])

Gate that implements a rotation around an arbitrary spin axis.

gates.CRX(wires[, value, conjugate, name])

Gate that implements a conditional rotation around the spin x-axis.

gates.CRY(wires[, value, conjugate, name])

Gate that implements a conditional rotation around the spin y-axis.

gates.CRZ(wires[, value, conjugate, name])

Gate that implements a conditional rotation around the spin z-axis.

gates.CR3(wires[, value, conjugate, name])

Gate that implements a conditional rotation around an arbitrary spin axis.

gates.CZ(wires[, value, conjugate, name])

Gate that implements the CZ unitary operation.

gates.Phase(wires[, value, conjugate, name])

Gate that implements a phase rotation

gates.Projector(pi, wires[, name])

Projector that projects the state onto an eigenvector of a Hermitian observable

gates.WeightedProjector(observable, wires[, …])

Projector that projects the state onto an eigenvector of a Hermitian observable

gates.XX(wires[, value, conjugate, name])

Gate that implements a conditional rotation Ising XX spin interaction.

gates.YY(wires[, value, conjugate, name])

Gate that implements a conditional rotation Ising YY spin interaction.

gates.ZZ(wires[, value, conjugate, name])

Gate that implements a conditional rotation Ising ZZ spin interaction.

gates.Swap(wires[, value, conjugate, name])

Gate that implements the SWAP unitary operation.

gates.Toffoli(wires[, value, conjugate, name])

Gate that implements a controlled-controlled flip by applying the Toffoli gate.

Observables

We can calculate observables \(\mathcal{O}\) in a similar fashion as quantum gates. By letting the observable of interest work on the required tensor subspace, we can efficiently obtain the corresponding expectation value.

\[\langle \psi| \mathcal{O} |\psi \rangle = \langle \psi |\hat{\psi}\rangle\]

where \(|\hat{\psi}\rangle\) is calculated as in the section about Gates

class zyglrox.core.observables.Observable(op_name: str, wires: List[int], **kwargs)

Bases: tensorflow.python.keras.engine.base_layer.Layer

Abstract Observable class

The Observable class calculates Hermitian observables from the quantum circuit.

Args:
op_name (str):

Name of an operation defined in the observable_dict

wires (list):

List of numbers where the observable of interest is to be measured

**kwargs:

Additional arguments.

Returns (inplace):

None

build(input_shape, **kwargs)

Called once from __call__, when we know the shapes of inputs and dtype. Should initialize the trainable variables, and call the super’s build().

Args:
input_shape (list):

Input shapes of the incoming tensor.

get_evals_and_projectors(O)

Get the eigenvalues and projectors of a Hermitian observable

Args:
O (Tensor):

Tensorflow tensor representing a Hermitian observable.

Returns (Tuple):

eigenvalues and the corresponding stacked projectors.

call(inputs, **kwargs)

Called in the Keras Model __call__ method after making sure build() has been called once. Should actually perform the logic of applying the layer to the input tensors (which should be passed in as the first argument).

Args:
inputs (Tensor):

Input tensor corresponding to the wave function

**kwargs:

Additional arguments.

Returns (Tensor):

Output tensor corresponding to wave function after the circuit has been applied

class zyglrox.core.observables.ExpectationValue(observables)

Bases: tensorflow.python.keras.engine.base_layer.Layer

Abstract Observable class

The ExpectationValue class calculates batches of Hermitian observables from the quantum circuit.

Args:
observables (str):

List of Observable objects

Returns (inplace):

None

build(input_shape)

Called once from __call__, when we know the shapes of inputs and dtype. Should initialize the trainable variables, and call the super’s build().

Args:
input_shape (list):

Input shapes of the incoming tensor.

call(inputs, **kwargs)

Called in the Keras Model __call__ method after making sure build() has been called once. Should actually perform the logic of applying the layer to the input tensors (which should be passed in as the first argument).

Args:
inputs (Tensor):

Input tensor corresponding to the wave function

**kwargs:

Additional arguments.

Returns (Tensor):

Batch of expectation values of shape (None, n_observables,1,1)

class zyglrox.core.observables.SampleExpectationValue(observables: List[zyglrox.core.observables.Observable], number_of_samples: int = 100)

Bases: tensorflow.python.keras.engine.base_layer.Layer

Abstract Observable class

The ExpectationValue class calculates batches of Hermitian observables from the quantum circuit.

Args:
observables (str):

List of Observable objects

number_of_samples (int):

The number of samples used for determining the observation values.

Returns (inplace):

None

build(input_shape)

Called once from __call__, when we know the shapes of inputs and dtype. Should initialize the trainable variables, and call the super’s build().

Args:
input_shape (list):

Input shapes of the incoming tensor.

call(inputs, **kwargs)

Called in the Keras Model __call__ method after making sure build() has been called once. Should actually perform the logic of applying the layer to the input tensors (which should be passed in as the first argument).

Args:
inputs (Tensor):

Input tensor corresponding to the wave function

**kwargs:

Additional arguments.

Returns (Tensor):

Batch of expectation values of shape (None, n_observables,1,1)

Hamiltonians

class zyglrox.core.hamiltonians.Hamiltonian(topology: dict, interactions: dict, model_parameters: dict = {}, **kwargs)

Hamiltonian is the abstract class for defining Hamiltonians of physical systems. For our purposes, the Hamiltonian exists of three components:

1. A topology \(\Lambda\) defining the lattice that our model lives on. This can be as simple as a line or square lattice, or as complicated as a fully connected model where each site is physically connected to each other site.

2. An interaction graph \(\Lambda_a \subseteq \Lambda\) which is sub-graph of the full topology with a corresponding string \(\alpha\beta\ldots\) with \(\alpha,\beta,\ldots \in \{x,y,z\}\) that indicates which Pauli interaction we are considering.

3. Model parameters that correspond to the strength of the interactions. This can be either a single value, so that the interaction strength is the same everywhere, or this can be a set of nodes where each vertex has its own interaction strength.

With these three ingredients, a wide range of spin models can be described. When the this class is initialized, a subfolder ./hamiltonians is automatically created relative to the root. Additionally, one can pass the file_path kwarg to specify a different location.

Args:
topology (dict):

A dict with nodes as keys and a list of edges as values.

interactions (dict):

A dict with strings of the type \(\alpha\beta\ldots\) as keys and topology dicts as values.

model_parameters (dict):

A dict with strings of the type \(\alpha\beta\ldots\) as keys and floats as values. If the interaction strength varies per site this can be a dict of vertices with each its own interaction strength

**kwargs:

Additional arguments.

Returns (inplace):

None

get_hamiltonian_terms() → numpy.ndarray

Get all the interactions in the Hamiltonian and add them to a 1d array. When calculating expectation values, this array can be used to multiply with the observables to get the energies.

Returns (np.ndarray):

Array with interaction strengths according to self.interaction_order, a sorted list of the interactions provided.

get_observables() → List[zyglrox.core.observables.Observable]

Get a list of Observable objects corresponding to all the terms in the hamiltonian. The order of the observables is according to self.interaction_order, a sorted list of the interactions provided.

Returns (list):

A list of Observable objects.

get_hamiltonian()

Get a sparse matrix representation of the hamiltonian and calculate the eigenvalues and eigenvectors. When the system is degenerate, we store all \(N\) degenerate eigenstates and energies. The Hamiltonian is automatically saved in the ./hamiltonians path or in the otherwise specified file_path kwarg

Returns (inplace):

None

draw_lattice(**kwargs)

Use Networkx to plot a Kamada-Kawai layout of the lattice. Takes the kwargs pos which is a dict of vertices and coordinates that indicates the location of the vertices in the plot.

Args:
**kwargs:

Additional arguments.

Returns (inplace):

None

draw_color_lattice(**kwargs)

Use Networkx to plot an edge coloring of the graph. Makes use of applyHeuristic in zyglrox.core.edge_coloring to find a suitable edge coloring. Per default uses the Kamada-Kawai layout of the lattice. Takes the kwargs pos which is a dict of vertices and coordinates that indicates the location of the vertices in the plot.

Args:
**kwargs:

Additional arguments.

Returns (inplace):

None

Hamiltonian Templates

Below is a table of the most commonly used quantum gates that are already implemented in zyglrox.

hamiltonians.TFI(topology[, g])

The transverse field Ising-model is given by the Hamiltonian

hamiltonians.HeisenbergXXX(topology, **kwargs)

The XXX Heisenberg model is given by the Hamiltonian

hamiltonians.HeisenbergXXZ(topology[, delta])

The XXZ Heisenberg model is given by the Hamiltonian

hamiltonians.HeisenbergXYZ(topology, delta, …)

The XYZ Heisenberg model is given by the Hamiltonian

hamiltonians.RandomFullyConnectedXYZ(L[, seed])

The fully-connected random couplings is given by the Hamiltonian

hamiltonians.J1J2(topology, J1, J2, **kwargs)

The \(J_1-J_2\) model is given by the Hamiltonian

Circuit Templates

zyglrox.core.circuit_templates.verify_circuit_input(N, depth, initial_parameters, desired_shape)
zyglrox.core.circuit_templates.staircase_cnot(N: int, mod=True)

Circuit architecture where \(N\) CNOTs are applied to qubit \(i\) with target \((i+1)\text{mod} (N)\)

#TODO: insert figure of circuit

Args:
N (int):

Number of qubits

Returns (list):

List of Gate objects.

zyglrox.core.circuit_templates.target_staircase_cnot(N: int, target: int)

Circuit architecture where \(N-1\) CNOTs are applied, all with the same target qubit.

#TODO: insert figure of circuit

Args:
N (int):

Number of qubits.

target (int):

Target qubit in the staircase circuit.

Returns (list):

List of Gate objects.

zyglrox.core.circuit_templates.circuit6(N: int)

The most expressive circuit at depth \(L=1\) according to Sim et al. (2019).

Args:
N (int):

Number of qubits.

Returns (list):

List of Gate objects.

zyglrox.core.circuit_templates.custom_cnot(locs: List)

Make custom CNOT padding

Args:
locs (list):

List with tuples with (control, target).

Returns (list):

List of Gate objects.

zyglrox.core.circuit_templates.tfi_1d_hva_circuit(N: int, depth: int, initial_parameters, boundary_condition: str)
zyglrox.core.circuit_templates.tfi_1d_projective_hva_circuit(N: int, depth: int, projectors, initial_parameters, boundary_condition: str, nmeas: int)
zyglrox.core.circuit_templates.xy_1d_hva_alt_circuit(N: int, depth: int, initial_parameters, boundary_condition)
zyglrox.core.circuit_templates.xy_1d_hva_circuit(N: int, depth: int, initial_parameters, boundary_condition)
zyglrox.core.circuit_templates.tfi_2d_hva_circuit(N: int, depth: int, edge_coloring: dict, initial_parameters, f_or_af: f)
zyglrox.core.circuit_templates.xxz_1d_hva_circuit(N: int, depth: int, initial_parameters, boundary_condition)
zyglrox.core.circuit_templates.xxz_1d_projective_hva_circuit(N: int, depth: int, projectors, initial_parameters, boundary_condition: str, nmeas: int)
zyglrox.core.circuit_templates.xxz_1d_projective_hva_circuit_multi_measure(N: int, depth: int, projectors, initial_parameters, boundary_condition: str, nmeas: int)
zyglrox.core.circuit_templates.xxz_1d_projective_hva_circuit_single_measure(N: int, depth: int, projectors, initial_parameters, boundary_condition: str, nmeas: int)
zyglrox.core.circuit_templates.xxz_1d_perm_hva_circuit(N: int, depth: int, initial_parameters, boundary_condition, permutation)
zyglrox.core.circuit_templates.xxz_2d_hva_circuit(depth: int, edge_coloring: dict, initial_parameters: numpy.ndarray)
zyglrox.core.circuit_templates.kitaev_honeycomb_circuit(depth: int, edge_coloring: dict, initial_parameters: numpy.ndarray)
zyglrox.core.circuit_templates.kitaev_ladder_circuit(depth: int, edge_coloring: dict, initial_parameters: numpy.ndarray)

Topologies

zyglrox.core.topologies.graph_kagome_12(boundary_condition='open')
zyglrox.core.topologies.graph_kagome_18b(boundary_condition='open')
zyglrox.core.topologies.graph_kagome_24(boundary_condition='open')
zyglrox.core.topologies.graph_honeycomb_8(link=None)
zyglrox.core.topologies.graph_honeycomb_16(link=None)
zyglrox.core.topologies.graph_ladder(L: int, link=None, boundary_condition='open')
zyglrox.core.topologies.graph_honeycomb_13()
zyglrox.core.topologies.graph_kagome_18b_torus()
zyglrox.core.topologies.graph_kagome_12_torus()
zyglrox.core.topologies.graph_kagome_24_torus()
zyglrox.core.topologies.graph_kagome_27()

Edge Coloring

zyglrox.core.edge_coloring.properlyColored(G, u, D)
zyglrox.core.edge_coloring.checkEdgeColoring(G, D)
zyglrox.core.edge_coloring.conflictLevel(G, u)
zyglrox.core.edge_coloring.createConflictDictionary(G, D)
zyglrox.core.edge_coloring.updateConflictDictionary(G, u, conflict_dictionary, old_conflict_level_u)
zyglrox.core.edge_coloring.maxConflictLevel(conflict_dictionary)
zyglrox.core.edge_coloring.totalNumberOfConflicts(conflict_dictionary)
zyglrox.core.edge_coloring.colorEdgeAndUpdate(G, u, v, color, conflict_dictionary)
zyglrox.core.edge_coloring.KempeNext(G, last, node, new_color, conflict_dictionary)
zyglrox.core.edge_coloring.KempeStep(G, last, node, new_color, conflict_dictionary)
zyglrox.core.edge_coloring.KempeProcess(G, last, node, new_color, conflict_dictionary)
zyglrox.core.edge_coloring.KempeStart(G, D, node, conflict_dictionary)
zyglrox.core.edge_coloring.preColoring(G, D)
zyglrox.core.edge_coloring.heuristic(G, D, repetition_limit)
zyglrox.core.edge_coloring.applyHeuristic(G, D, repetition_limit, iteration_limit)

Optimizers

zyglrox.core.optimizers.Newton(loss, vrs, optimizer=None)

Newton’s second order gradient method.

Args:

loss (Tensor):

Loss function that uses the provided state.

vrs (Variables):

Variables to be optimized.

optimizer (Tensorflow Optimizer):

Tensorflow optimizer from the tf.train API

Returns (Operation):

Train step operation.

zyglrox.core.optimizers.ImaginaryTimeEvolution(state, loss, vrs, optimizer=None, stability_shift=None, learning_rate=0.01)

Implementation of the imaginary time evolution gradient method

Args:
state (Tensor):

Output state of the circuit.

loss (Tensor):

Loss function that uses the provided state.

vrs (Variables):

Variables to be optimized.

optimizer (Tensorflow Optimizer):

Tensorflow optimizer from the tf.train API

Returns (Operation):

Train step operation.

zyglrox.core.optimizers.QuantumNaturalGradient(state, loss, vrs, optimizer=None, stability_shift=None, learning_rate: float = 0.01)

Implementation of the quantum natural gradient gradient method

Args:
state (Tensor):

Output state of the circuit.

loss (Tensor):

Loss function that uses the provided state.

vrs (Variables):

Variables to be optimized.

optimizer (Tensorflow Optimizer):

Tensorflow optimizer from the tf.train API

Returns (Operation):

Train step operation.

zyglrox.core.optimizers.prepGeometricTensor(state, vrs)
zyglrox.core.optimizers.prep_variables(vrs)

Utils

zyglrox.core.utils.integer_generator(start)

Generator for infinite integers. Always useful.

Args:
start (int):

starting integer for the generator.

Returns (int):

Infinite integers.

zyglrox.core.utils.tf_kron(a: tensorflow.python.framework.ops.Tensor, b: tensorflow.python.framework.ops.Tensor) → tensorflow.python.framework.ops.Tensor

Implementation of Kronecker product for tensorflow Tensors.

Args:
a (Tensor):

Tensor of size \(N \times M\)

b (Tensor):

Tensor of size \(P \times K\)

Returns (Tensor):

Tensor of size \((N \cdot P) \times (M \cdot K)\)

zyglrox.core.utils.partial_trace_np(psi: numpy.ndarray, keep: list, dims: list) → numpy.ndarray

Calculate the partial trace of an outer product

\[\rho_a = \text{Tr}_b (| u \rangle \langle u |)\]
Args:
psi (tensor):

Quantum state of shape (None ,2,2,…,2), where None is a batch dimension.

keep (list):

An array of indices of the spaces to keep after being traced. For instance, if the space is A x B x C x D and we want to trace out B and D, keep = [0,2]

dims (list):
An array of the dimensions of each space. For instance, if the space is A x B x C x D,

dims = [None, dim_A, dim_B, dim_C, dim_D]. None is used as a batch dimension.

Returns (Tensor):

Partially traced out matrix

zyglrox.core.utils.partial_trace(psi: tensorflow.python.framework.ops.Tensor, keep: list, dims: list) → tensorflow.python.framework.ops.Tensor

Calculate the partial trace of an outer product

\[\rho_a = \text{Tr}_b (| u \rangle \langle u |)\]
Args:
psi (tensor):

Quantum state of shape (None ,2,2,…,2), where None is a batch dimension.

keep (list):

An array of indices of the spaces to keep after being traced. For instance, if the space is A x B x C x D and we want to trace out B and D, keep = [0,2]

dims (list):
An array of the dimensions of each space. For instance, if the space is A x B x C x D,

dims = [None, dim_A, dim_B, dim_C, dim_D]. None is used as a batch dimension.

Returns (Tensor):

Partially traced out matrix

zyglrox.core.utils.von_neumann_entropy(rho: Union[tensorflow.python.framework.ops.Tensor, numpy.ndarray]) → Union[tensorflow.python.framework.ops.Tensor, numpy.ndarray]

Calculate the Von Neumann entropy of a reduced density matrix.

\[S(\rho) = -\text{Tr} \rho \log \rho\]
Args:
red_rho (tensor):

Density matrix.

Returns (tensor):

Scalar containing the Von Neumann entropy.

zyglrox.core.utils.logarithmic_negativity(rho: Union[tensorflow.python.framework.ops.Tensor, numpy.ndarray]) → Union[tensorflow.python.framework.ops.Tensor, numpy.ndarray]

Calculate the Von Neumann entropy of a reduced density matrix.

\[S(\rho) = -\text{Tr} \rho \log \rho\]
Args:
red_rho (tensor):

Density matrix.

Returns (tensor):

Scalar containing the Von Neumann entropy.

zyglrox.core.utils.get_available_devices(device_type)
zyglrox.core.utils.renyi_entropy(rho: tensorflow.python.framework.ops.Tensor, alpha: float = 0.5) → tensorflow.python.framework.ops.Tensor

Calculate the Von Neumann entropy of a reduced density matrix.

\[S(\rho) = \frac{1}{1-\alpha}\log \text{Tr} \rho^\alpha\]
Args:
red_rho (tensor):

Density matrix.

Returns (tensor):

Scalar containing the Von Neumann entropy.

zyglrox.core.utils.flatten(x: tensorflow.python.framework.ops.Tensor) → tensorflow.python.framework.ops.Tensor

Flatten tensor to 1D array.

Args:
x (Tensor):

Input tensor with shape \((M_{i_1},M_{i_2},\ldots,M_{i_m})\).

Returns (Tensor):

Flattened tensor with shape \((\prod_n^m M_{i_n}, )\).

zyglrox.core.utils.ops_print(observables)

Print the observables in a readable manner.

Args:
observables (list):

List of Observable objects.

Returns (inplace):

None

zyglrox.core.utils.tensordot(a, b, axes, name: Optional[str] = None) → tensorflow.python.framework.ops.Tensor

Full credit for this part goes to the developers of the Google TensorNetworks library, thanks to Martin for mentioning this. Source: https://github.com/google/TensorNetwork/blob/master/tensornetwork/backends/tensorflow/tensordot2.py

Tensor contraction of a and b along specified axes. Tensordot (also known as tensor contraction) sums the product of elements from a and b over the indices specified by a_axes and b_axes. The lists a_axes and b_axes specify those pairs of axes along which to contract the tensors. The axis a_axes[i] of a must have the same dimension as axis b_axes[i] of b for all i in range(0, len(a_axes)). The lists a_axes and b_axes must have identical length and consist of unique integers that specify valid axes for each of the tensors. This operation corresponds to numpy.tensordot(a, b, axes).

Example 1: When a and b are matrices (order 2), the case axes = 1 is equivalent to matrix multiplication.

Example 2: When a and b are matrices (order 2), the case axes = [[1], [0]] is equivalent to matrix multiplication.

Example 3: Suppose that \(a_{ijk}\) and \(b_{lmn}\) represent two tensors of order 3. Then, contract(a, b, [[0], [2]]) is the order 4 tensor \(c_{jklm}\) whose entry corresponding to the indices \((j,k,l,m)\) is given by: \( c_{jklm} = sum_i a_{ijk} b_{lmi} \).

In general, order(c) = order(a) + order(b) - 2*len(axes[0]).

Args:
tf:

The TensorFlow module. This must be passed in instead of imported

since we don’t assume users have TensorFlow installed.

a:

Tensor of type float32 or float64.

b:

Tensor with the same type as a.

axes:

Either a scalar N, or a list or an int32 Tensor of shape [2, k]. If axes is a scalar, sum over the last N axes of a and the first N axes of b in order. If axes is a list or Tensor the first and second row contain the set of unique integers specifying axes along which the contraction is computed, for a and b, respectively. The number of axes for a and b must be equal.

name:

A name for the operation (optional).

Returns:

A Tensor with the same type as a.

Raises:
ValueError:

If the shapes of a, b, and axes are incompatible.

IndexError:

If the values in axes exceed the rank of the corresponding tensor.