QfMajoranaOperator

struct QfMajoranaOperator

A Majorana fermion operator.

Note

This is an opaque data structure to the C API whose internals are implemented entirely in Rust. The remainder of this page describes the design and related functions to work with this struct.


Definition

This operator is defined by a linear combination of products of Majorana operators [1], which can be defined in terms of the standard fermionic second-quantization creation and annihilation operators (see also .QfFermionOperator):

\[\gamma = a^\dagger + a ~~\text{and}~~ \gamma' = i(a^\dagger - a)\]

The key property that a Majorana fermion is its own antiparticle becomes immediately apparent:

\[\gamma_i = \gamma_i^\dagger ~~\text{and}~~ \gamma_i^2 = (\gamma_i^\dagger)^2 = 1\]

This result in the following anti-commutation relations for \(2n\) Majorana fermions:

\[\left\{\gamma_i,\gamma_j\right\} = 2\delta_{ij}\]

This makes the definition of the entire operator the following:

\[\text{\texttt{MajoranaOperator}} = \sum_i c_i \bigotimes_j \hat{\gamma_j} \, ,\]

where \(c_i\) is the (complex) coefficient making up the linear combination of products of \(\gamma_j\). The index \(j\) can take any value between 0 and the number of majorana fermionic modes acted upon by the operator minus 1.


Implementation

This struct stores the terms and coefficients in multiple sparse vectors, akin to the compressed sparse row format commonly used for sparse matrices. More concretely, a single operator contains 4 arrays:

coeffs

A vector of complex coefficients consisting of two 64-bit floating point numbers.

modes

A vector of 32-bit integers storing the majorana mode indices acted upon.

boundaries

A vector of integers indicating the boundaries in actions and indices.

The integers in modes index the Majorana modes, \(j\). When using the convenience function gamma(), even (odd) indices are used for :math`gamma` (\(\gamma'\)).

This data structure allows for very efficient construction and manipulation of operators. However, it implies that duplicate terms may be contained in an operator at any moment. These must be resolved manually through the use of qf_maj_op_simplify().

Construction

A new operator can be constructed directly by specifying the corresponding arrays outlined above. Alternatively, an empty QfMajoranaOperator can be initialized with qf_maj_op_zero() and terms can be added iteratively via qf_maj_op_add_term().

qf_maj_op_new()

Constructs a new operator from the provided arrays.

qf_maj_op_zero()

Constructs the additive identity operator.

qf_maj_op_one()

Constructs the multiplicative identity operator.

qf_maj_op_add_term()

Adds a term to an existing QfMajoranaOperator.

Note

A QfMajoranaOperator can be freed with qf_maj_op_free().

Arithmetics

The following functions provide arithmetic manipulation:

qf_maj_op_add()

Adds two operators together.

qf_maj_op_mul()

Multiplies an operator by a scalar.

qf_maj_op_compose()

Composes two operators with each other.

qf_maj_op_adjoint()

Returns the Hermitian conjugate operator.

Manipulation

The following functions provide operator manipulation logic:

qf_maj_op_ichop()

Removes terms with small coefficient magnitudes.

qf_maj_op_simplify()

Returns an equivalent but simplified operator.

qf_maj_op_normal_ordered()

Returns an equivalent operator with normal ordered terms.

Properties

The following functions exist to check certain properties of an operator.

qf_maj_op_is_hermitian()

Returns whether an operator is Hermitian.

qf_maj_op_many_body_order()

Returns the many-body order of an operator.

qf_maj_op_is_even()

Returns whether an operator is even.


Members

QfMajoranaOperator *qf_maj_op_new(uint64_t num_terms, uint64_t num_modes, const QkComplex64 *coeffs, const uint32_t *modes, const uint32_t *boundaries)

Constructs a new operator.

Any of the pointer arguments may be NULL if and only if their corresponding length is zero.

Example

1uint64_t num_terms = 3;
2uint64_t num_modes = 4;
3uint32_t modes[4] = {0, 1, 2, 3};
4QkComplex64 coeffs[3] = {{1.0, 0.0}, {-1.0, 0.0}, {0.0, -1.0}};
5uint32_t boundaries[4] = {0, 0, 2, 4};
6QfMajoranaOperator *op = qf_maj_op_new(num_terms, num_modes, coeffs,
7                                       modes, boundaries);

Parameters:
  • num_terms – The number of terms in the operator.

  • num_modes – The number of modes summed over all terms.

  • coeffs – A pointer to an array of term coefficients. The length of this array should be num_terms.

  • modes – A pointer to an array of modes over all terms. The length of this array should be num_modes.

  • boundaries – A pointer to an array of the boundaries between terms. The length of this array should be num_terms + 1.

void qf_maj_op_free(QfMajoranaOperator *op)

Frees an existing operator.

Example

1QfMajoranaOperator *op = qf_maj_op_one();
2qf_maj_op_free(op);

Parameters:
  • op – A pointer to the Majorana operator to be freed.

QfMajoranaOperator *qf_maj_op_zero(void)

Constructs the additive identity operator.

Adding the operator that is constructed by this method to another one has no effect.

Example

1QfMajoranaOperator *zero = qf_maj_op_zero();
2
3QfMajoranaOperator *op_plus_zero = qf_maj_op_add(op, zero);
4
5assert(qf_maj_op_equal(op, op_plus_zero));

Returns:

A pointer to the created operator.

QfMajoranaOperator *qf_maj_op_one(void)

Constructs the multiplicative identity operator.

Composing the operator that is constructed by this method with another one has no effect.

Example

1QfMajoranaOperator *one = qf_maj_op_one();
2
3QfMajoranaOperator *op_times_one = qf_maj_op_compose(op, one);
4
5assert(qf_maj_op_equal(op, op_times_one));

Returns:

A pointer to the created operator.

void qf_maj_op_add_term(QfMajoranaOperator *op, uint64_t num_modes, const uint32_t *modes, const QkComplex64 *coeff)

Adds a term to an existing operator.

Any of the pointer arguments may be NULL if and only if their corresponding length is zero.

Example

1QfMajoranaOperator *one = qf_maj_op_one();
2
3QfMajoranaOperator *op = qf_maj_op_zero();
4uint32_t modes[0] = {};
5QkComplex64 coeff = {1.0, 0.0};
6
7qf_maj_op_add_term(op, 0, modes, &coeff);
8
9assert(qf_maj_op_equal(op, one));

Parameters:
  • op – A pointer to the Majorana operator to be modified.

  • num_modes – The length of the modes array.

  • modes – A pointer to an array of mode indices. The length of this array should be num_modes.

  • coeff – A pointer to the complex coefficient.

QfMajoranaOperator *qf_maj_op_add(const QfMajoranaOperator *left, const QfMajoranaOperator *right)

Adds two operators together.

Example

1QfMajoranaOperator *one = qf_maj_op_one();
2QfMajoranaOperator *zero = qf_maj_op_zero();
3
4QfMajoranaOperator *result = qf_maj_op_add(one, zero);
5
6assert(qf_maj_op_equal(result, one));

Parameters:
  • left – A pointer to the left operator.

  • right – A pointer to the right operator.

Returns:

A pointer to the resulting operator.

QfMajoranaOperator *qf_maj_op_mul(const QfMajoranaOperator *op, const QkComplex64 *scalar)

Multiplies an operator by a scalar.

Example

1QfMajoranaOperator *one = qf_maj_op_one();
2QkComplex64 coeff = {2.0, 0.0};
3QfMajoranaOperator *result = qf_maj_op_mul(one, &coeff);
4
5QfMajoranaOperator *expected = qf_maj_op_zero();
6uint32_t modes[0] = {};
7qf_maj_op_add_term(expected, 0, modes, &coeff);
8
9assert(qf_maj_op_equal(result, expected));

Parameters:
  • op – A pointer to the operator.

  • scalar – A pointer to the scalar.

Returns:

A pointer to the resulting operator.

QfMajoranaOperator *qf_maj_op_compose(const QfMajoranaOperator *left, const QfMajoranaOperator *right)

Composes two operators with each other.

Example

1QfMajoranaOperator *one = qf_maj_op_one();
2QfMajoranaOperator *zero = qf_maj_op_zero();
3
4QfMajoranaOperator *result = qf_maj_op_compose(one, zero);
5
6assert(qf_maj_op_equal(result, zero));

Parameters:
  • left – A pointer to the left operator.

  • right – A pointer to the right operator.

Returns:

A pointer to the resulting operator.

QfMajoranaOperator *qf_maj_op_adjoint(const QfMajoranaOperator *op)

Returns the Hermitian conjugate (or adjoint) of an operator.

This affects the terms and coefficients as follows:

  • the actions in each term reverse their order

  • the coefficients are complex conjugated

Example

 1QfMajoranaOperator *op = qf_maj_op_zero();
 2uint32_t modes[0] = {};
 3QkComplex64 coeff = {0.0, 1.0};
 4qf_maj_op_add_term(op, 0, modes, &coeff);
 5
 6QfMajoranaOperator *adjoint = qf_maj_op_adjoint(op);
 7
 8QfMajoranaOperator *expected = qf_maj_op_zero();
 9QkComplex64 coeff_adj = {0.0, -1.0};
10qf_maj_op_add_term(expected, 0, modes, &coeff_adj);
11
12assert(qf_maj_op_equal(adjoint, expected));

Parameters:
  • op – A pointer to the operator.

Returns:

A pointer to the created operator.

void qf_maj_op_ichop(QfMajoranaOperator *op, double atol)

Removes terms whose coefficient magnitude lies below the provided threshold.

Caution

This functions truncates coefficients greedily! If the acted upon operator may contain separate coefficients for duplicate terms consider calling qf_maj_op_simplify() instead!

Example

 1QfMajoranaOperator *op = qf_maj_op_zero();
 2uint32_t modes[0] = {};
 3QkComplex64 coeff = {1e-8};
 4qf_maj_op_add_term(op, 0, modes, &coeff);
 5
 6qf_maj_op_ichop(op, 1e-6);
 7
 8QfMajoranaOperator *expected = qf_maj_op_zero();
 9
10assert(qf_maj_op_equal(op, expected));

Parameters:
  • op – A pointer to the operator.

  • atol – The absolute tolerance for coefficient truncation.

QfMajoranaOperator *qf_maj_op_simplify(const QfMajoranaOperator *op, double atol)

Returns an equivalent but simplified operator.

The simplification process first sums all coefficients that belong to equal terms and then only retains those whose total coefficient exceeds the specified tolerance (just like qf_maj_op_ichop()).

When an operator has been arithmetically manipulated or constructed in a way that does not guarantee unique terms, this method should be called before applying any method that filters numerically small coefficients to avoid loss of information. See the example below which showcases how qf_maj_op_ichop() can truncate terms that sum to a total coefficient magnitude which should not be truncated:

 1uint64_t num_terms = 100000;
 2uint64_t num_modes = 0;
 3uint32_t modes[0] = {};
 4QkComplex64 coeffs[100000];
 5uint32_t boundaries[100001];
 6for (int i = 0; i < 100000; i++) {
 7  coeffs[i].re = 1e-5;
 8  coeffs[i].im = 0.0;
 9  boundaries[i] = 0;
10}
11boundaries[100000] = 0;
12QfMajoranaOperator *op =
13    qf_maj_op_new(num_terms, num_modes, coeffs, modes, boundaries);
14
15QfMajoranaOperator *canon = qf_maj_op_simplify(op, 1e-4);
16
17QfMajoranaOperator *one = qf_maj_op_one();
18bool canon_is_equal = qf_maj_op_equiv(canon, one, 1e-6);
19
20qf_maj_op_ichop(op, 1e-4);
21
22QfMajoranaOperator *zero = qf_maj_op_zero();
23bool ichop_is_equal = qf_maj_op_equiv(op, zero, 1e-6);

Parameters:
  • op – A pointer to the Majorana operator to be simplified.

  • atol – The absolute tolerance for coefficient truncation.

Returns:

An equivalent but simplified operator.

QfMajoranaOperator *qf_maj_op_normal_ordered(const QfMajoranaOperator *op, bool reduce)

Returns an equivalent operator with normal ordered terms.

The normal order of an operator term is defined such that all actions are ordered by lexicographically descending indices.

Example

 1QfMajoranaOperator *op = qf_maj_op_zero();
 2uint32_t modes[4] = {0, 2, 1, 3};
 3QkComplex64 coeff = {1.0, 0.0};
 4qf_maj_op_add_term(op, 4, modes, &coeff);
 5
 6QfMajoranaOperator *normal_ordered = qf_maj_op_normal_ordered(op);
 7
 8QkComplex64 coeff_minus = {-1.0, 0.0};
 9QfMajoranaOperator *expected = qf_maj_op_zero();
10uint32_t modes_exp[4] = {3, 2, 1, 0};
11qf_maj_op_add_term(expected, 4, modes_exp, &coeff_minus);
12
13assert(qf_maj_op_equal(normal_ordered, expected));

Parameters:
  • op – A pointer to the operator.

Returns:

A pointer to the created operator.

bool qf_maj_op_is_hermitian(const QfMajoranaOperator *op, double atol)

Checks whether an operator is Hermitian.

Example

 1QfMajoranaOperator *op = qf_maj_op_zero();
 2uint32_t modes1[2] = {0, 1};
 3QkComplex64 coeff1 = {0.0, 1.00001};
 4qf_maj_op_add_term(op, 2, modes1, &coeff1);
 5uint32_t modes2[2] = {0, 1};
 6QkComplex64 coeff2 = {0.0, -1};
 7qf_maj_op_add_term(op, 2, modes2, &coeff2);
 8
 9assert(qf_maj_op_is_hermitian(op, 1e-4));
10assert(!qf_maj_op_is_hermitian(op, 1e-8));

Note

This check is implemented using qf_maj_op_equiv() on the qf_maj_op_normal_ordered() difference of op and its qf_maj_op_adjoint() and qf_maj_op_zero().

Parameters:
  • op – A pointer to the Majorana operator to be checked.

  • atol – The absolute tolerance upto which coefficients are considered equal.

Returns:

Whether the provided operator is Hermitian.

uint32_t qf_maj_op_many_body_order(const QfMajoranaOperator *op)

Checks the many-body order of an operator.

Example

1QfMajoranaOperator *op = qf_maj_op_zero();
2uint32_t modes[4] = {0, 1, 2, 3};
3QkComplex64 coeff = {1.0, 0.0};
4qf_maj_op_add_term(op, 4, modes, &coeff);
5
6assert(qf_maj_op_many_body_order(op, 4));

Note

The many-body order is defined as the length of the longest term contained in the operator.

Parameters:
  • op – A pointer to the Majorana operator to be checked.

Returns:

The many-body order of the operator.

bool qf_maj_op_is_even(const QfMajoranaOperator *op)

Checks whether an operator is even.

Example

 1QfMajoranaOperator *op = qf_maj_op_zero();
 2QkComplex64 coeff = {1.0, 0.0};
 3uint32_t modes1[2] = {0, 1};
 4qf_maj_op_add_term(op, 2, modes1, &coeff);
 5
 6assert(qf_maj_op_is_even(op));
 7
 8uint32_t modes2[1] = {2};
 9qf_maj_op_add_term(op, 2, modes2, &coeff);
10
11assert(!qf_maj_op_is_even(op));

Note

An operator is considered even when all of its terms contain an even number of actions.

Parameters:
  • op – A pointer to the Majorana operator to be checked.

Returns:

Whether the provided operator is even.

bool qf_maj_op_equal(const QfMajoranaOperator *left, const QfMajoranaOperator *right)

Compare two operators for equality.

Example

1QfMajoranaOperator *one = qf_maj_op_one();
2QfMajoranaOperator *zero = qf_maj_op_zero();
3
4assert(qf_maj_op_equal(one, one));
5assert(!qf_maj_op_equal(one, zero));

Parameters:
  • left – A pointer to the left operator.

  • right – A pointer to the right operator.

Returns:

Whether the two operators are equal.

bool qf_maj_op_equiv(const QfMajoranaOperator *left, const QfMajoranaOperator *right, double atol)

Compare two operators for equivalence.

Equivalence in this context means approximate equality up to the specified absolute tolerance. To be more precise, this method returns True, when all the absolute values of the coefficients in the difference other - self are below the specified threshold atol.

Example

1QfMajoranaOperator *zero = qf_maj_op_zero();
2
3QfMajoranaOperator *op = qf_maj_op_zero();
4uint32_t modes[0] = {};
5QkComplex64 coeff = {1e-7, 0.0};
6qf_maj_op_add_term(op, 0, modes, &coeff);
7
8assert(qf_maj_op_equiv(op, zero, 1e-6));
9assert(!qf_maj_op_equiv(op, zero, 1e-8));

Parameters:
  • left – A pointer to the left operator.

  • right – A pointer to the right operator.

  • atol – The absolute tolerance for coefficient equivalence.

Returns:

Whether the two operators are equivalent.

size_t qf_maj_op_len(const QfMajoranaOperator *op)

Returns the length (or number of terms) of the provided operator.

Example

1QfMajoranaOperator *op = qf_maj_op_zero();
2uint32_t modes[4] = {0, 1, 2, 3};
3QkComplex64 coeff = {1.0, 0.0};
4qf_maj_op_add_term(op, 4, modes, &coeff);
5
6assert(qf_maj_op_len(op) == 1);

Parameters:
  • op – A pointer to the Majorana operator.

Returns:

The length (or number of terms) of the operator.