QfFermionOperator¶
-
struct QfFermionOperator¶
A spin-less fermionic 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 fermionic creation and annihilation operators acting on spin-less fermionic modes. That is to say, the individual terms fulfill the following anti-commutation relations: [1]
where \(\alpha\) and \(\beta\) do not distinguish the spin species of the fermionic modes they are indexing.
This makes the definition of the entire operator the following:
where \(\hat{A_j} \in \{ a_j, a^\dagger_j \}\) and \(c_i\) is the (complex) coefficient making up the linear combination of products. The index \(j\) can take any value between 0 and the number of 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:
|
A vector of complex coefficients consisting of two 64-bit floating point numbers. |
|
A vector of booleans storing the nature of the second-quantization actions. |
|
A vector of 32-bit integers storing the fermionic mode indices acted upon. |
|
A vector of integers indicating the boundaries in |
Entries in actions indicate creation (annihilation) operators by True (False).
Fermionic modes indexed by modes are considered spinless.
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_ferm_op_simplify().
Construction¶
A new operator can be constructed directly by specifying the corresponding arrays outlined above.
Alternatively, an empty QfFermionOperator can be initialized with
qf_ferm_op_zero() and terms can be added iteratively via qf_ferm_op_add_term().
Constructs a new operator from the provided arrays. |
|
Constructs the additive identity operator. |
|
Constructs the multiplicative identity operator. |
|
Adds a term to an existing |
Note
A QfFermionOperator can be freed with qf_ferm_op_free().
Arithmetics¶
The following functions provide arithmetic manipulation:
Adds two operators together. |
|
Multiplies an operator by a scalar. |
|
Composes two operators with each other. |
|
Returns the Hermitian conjugate operator. |
Manipulation¶
The following functions provide operator manipulation logic:
Removes terms with small coefficient magnitudes. |
|
Returns an equivalent but simplified operator. |
|
Returns an equivalent operator with normal ordered terms. |
Properties¶
The following functions exist to check certain properties of an operator.
Returns whether an operator is Hermitian. |
|
Returns the many-body order of an operator. |
|
Returns whether an operator is particle-number conserving. |
Members¶
-
QfFermionOperator *qf_ferm_op_new(uint64_t num_terms, uint64_t num_actions, const QkComplex64 *coeffs, const bool *actions, const uint32_t *modes, const uint32_t *boundaries)¶
Constructs a new operator.
Any of the pointer arguments may be
NULLif and only if their corresponding length is zero.Example¶
1uint64_t num_terms = 3; 2uint64_t num_actions = 4; 3bool actions[4] = {true, false, true, false}; 4uint32_t modes[4] = {0, 1, 2, 3}; 5QkComplex64 coeffs[3] = {{1.0, 0.0}, {-1.0, 0.0}, {0.0, -1.0}}; 6uint32_t boundaries[4] = {0, 0, 2, 4}; 7QfFermionOperator *op = qf_ferm_op_new(num_terms, num_actions, coeffs, 8 actions, modes, boundaries);
- Parameters:
num_terms – The number of terms in the operator.
num_actions – The number of actions summed over all terms.
coeffs – A pointer to an array of term coefficients. The length of this array should be
num_terms.actions – A pointer to an array of actions over all terms. The length of this array should be
num_actions.modes – A pointer to an array of action modes over all terms. The length of this array should be
num_actions.boundaries – A pointer to an array of the boundaries between terms. The length of this array should be
num_terms + 1.
-
void qf_ferm_op_free(QfFermionOperator *op)¶
Frees an existing operator.
Example¶
1QfFermionOperator *op = qf_ferm_op_one(); 2qf_ferm_op_free(op);
- Parameters:
op – A pointer to the fermionic operator to be freed.
-
QfFermionOperator *qf_ferm_op_zero(void)¶
Constructs the additive identity operator.
Adding the operator that is constructed by this method to another one has no effect.
Example¶
1QfFermionOperator *zero = qf_ferm_op_zero(); 2 3QfFermionOperator *op_plus_zero = qf_ferm_op_add(op, zero); 4 5assert(qf_ferm_op_equal(op, op_plus_zero));
- Returns:
A pointer to the created operator.
-
QfFermionOperator *qf_ferm_op_one(void)¶
Constructs the multiplicative identity operator.
Composing the operator that is constructed by this method with another one has no effect.
Example¶
1QfFermionOperator *one = qf_ferm_op_one(); 2 3QfFermionOperator *op_times_one = qf_ferm_op_compose(op, one); 4 5assert(qf_ferm_op_equal(op, op_times_one));
- Returns:
A pointer to the created operator.
-
void qf_ferm_op_add_term(QfFermionOperator *op, uint64_t num_actions, const bool *actions, const uint32_t *modes, const QkComplex64 *coeff)¶
Adds a term to an existing operator.
Any of the pointer arguments may be
NULLif and only if their corresponding length is zero.Example¶
1QfFermionOperator *one = qf_ferm_op_one(); 2 3QfFermionOperator *op = qf_ferm_op_zero(); 4bool actions[0] = {}; 5uint32_t modes[0] = {}; 6QkComplex64 coeff = {1.0, 0.0}; 7 8qf_ferm_op_add_term(op, 0, actions, modes, &coeff); 9 10assert(qf_ferm_op_equal(op, one));
- Parameters:
op – A pointer to the fermionic operator to be modified.
num_actions – The length of the actions array.
actions – A pointer to an array of actions. The length of this array should be
num_actions.modes – A pointer to an array of action modes. The length of this array should be
num_actions.coeff – A pointer to the complex coefficient.
-
QfFermionOperator *qf_ferm_op_add(const QfFermionOperator *left, const QfFermionOperator *right)¶
Adds two operators together.
Example¶
1QfFermionOperator *one = qf_ferm_op_one(); 2QfFermionOperator *zero = qf_ferm_op_zero(); 3 4QfFermionOperator *result = qf_ferm_op_add(one, zero); 5 6assert(qf_ferm_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.
-
QfFermionOperator *qf_ferm_op_mul(const QfFermionOperator *op, const QkComplex64 *scalar)¶
Multiplies an operator by a scalar.
Example¶
1QfFermionOperator *one = qf_ferm_op_one(); 2QkComplex64 coeff = {2.0, 0.0}; 3QfFermionOperator *result = qf_ferm_op_mul(one, &coeff); 4 5QfFermionOperator *expected = qf_ferm_op_zero(); 6bool actions[0] = {}; 7uint32_t modes[0] = {}; 8qf_ferm_op_add_term(expected, 0, actions, modes, &coeff); 9 10assert(qf_ferm_op_equal(result, expected));
- Parameters:
op – A pointer to the operator.
scalar – A pointer to the scalar.
- Returns:
A pointer to the resulting operator.
-
QfFermionOperator *qf_ferm_op_compose(const QfFermionOperator *left, const QfFermionOperator *right)¶
Composes two operators with each other.
Example¶
1QfFermionOperator *one = qf_ferm_op_one(); 2QfFermionOperator *zero = qf_ferm_op_zero(); 3 4QfFermionOperator *result = qf_ferm_op_compose(one, zero); 5 6assert(qf_ferm_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.
-
QfFermionOperator *qf_ferm_op_adjoint(const QfFermionOperator *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 and flip between creation and annihilation
the coefficients are complex conjugated
Example¶
1QfFermionOperator *op = qf_ferm_op_zero(); 2bool actions[0] = {}; 3uint32_t modes[0] = {}; 4QkComplex64 coeff = {0.0, 1.0}; 5qf_ferm_op_add_term(op, 0, actions, modes, &coeff); 6 7QfFermionOperator *adjoint = qf_ferm_op_adjoint(op); 8 9QfFermionOperator *expected = qf_ferm_op_zero(); 10QkComplex64 coeff_adj = {0.0, -1.0}; 11qf_ferm_op_add_term(expected, 0, actions, modes, &coeff_adj); 12 13assert(qf_ferm_op_equal(adjoint, expected));
- Parameters:
op – A pointer to the operator.
- Returns:
A pointer to the created operator.
-
void qf_ferm_op_ichop(QfFermionOperator *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_ferm_op_simplify()instead!Example¶
1QfFermionOperator *op = qf_ferm_op_zero(); 2bool actions[0] = {}; 3uint32_t modes[0] = {}; 4QkComplex64 coeff = {1e-8}; 5qf_ferm_op_add_term(op, 0, actions, modes, &coeff); 6 7qf_ferm_op_ichop(op, 1e-6); 8 9QfFermionOperator *expected = qf_ferm_op_zero(); 10 11assert(qf_ferm_op_equal(op, expected));
- Parameters:
op – A pointer to the operator.
atol – The absolute tolerance for coefficient truncation.
-
QfFermionOperator *qf_ferm_op_simplify(const QfFermionOperator *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_ferm_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_ferm_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_actions = 0; 3bool actions[0] = {}; 4uint32_t modes[0] = {}; 5QkComplex64 coeffs[100000]; 6uint32_t boundaries[100001]; 7for (int i = 0; i < 100000; i++) { 8 coeffs[i].re = 1e-5; 9 coeffs[i].im = 0.0; 10 boundaries[i] = 0; 11} 12boundaries[100000] = 0; 13QfFermionOperator *op = qf_ferm_op_new(num_terms, num_actions, coeffs, 14 actions, modes, boundaries); 15 16QfFermionOperator *canon = qf_ferm_op_simplify(op, 1e-4); 17 18QfFermionOperator *one = qf_ferm_op_one(); 19bool canon_is_equal = qf_ferm_op_equiv(canon, one, 1e-6); 20 21qf_ferm_op_ichop(op, 1e-4); 22 23QfFermionOperator *zero = qf_ferm_op_zero(); 24bool ichop_is_equal = qf_ferm_op_equiv(op, zero, 1e-6);
- Parameters:
op – A pointer to the fermionic operator to be simplified.
atol – The absolute tolerance for coefficient truncation.
- Returns:
An equivalent but simplified operator.
-
QfFermionOperator *qf_ferm_op_normal_ordered(const QfFermionOperator *op)¶
Returns an equivalent operator with normal ordered terms.
The normal order of an operator term is defined such that all creation actions before all annihilation actions and the modes of actions within each group descend lexicographically (e.g.
+_1 +_0 -_1 -_0).Example¶
1QfFermionOperator *op = qf_ferm_op_zero(); 2bool actions[4] = {false, true, false, true}; 3uint32_t modes[4] = {1, 1, 0, 0}; 4QkComplex64 coeff = {1.0, 0.0}; 5qf_ferm_op_add_term(op, 4, actions, modes, &coeff); 6 7QfFermionOperator *normal_ordered = qf_ferm_op_normal_ordered(op); 8 9uint64_t num_terms = 4; 10uint64_t num_actions = 8; 11bool actions_exp[8] = {true, false, true, false, true, true, false, false}; 12uint32_t modes_exp[8] = {0, 0, 1, 1, 1, 0, 1, 0}; 13QkComplex64 coeffs_exp[4] = { 14 {1.0, 0.0}, {-1.0, 0.0}, {-1.0, 0.0}, {-1.0, 0.0}}; 15uint32_t boundaries_exp[5] = {0, 0, 2, 4, 8}; 16QfFermionOperator *expected = 17 qf_ferm_op_new(num_terms, num_actions, coeffs_exp, actions_exp, 18 modes_exp, boundaries_exp); 19 20assert(qf_ferm_op_equal(normal_ordered, expected));
Note
When a term is being reordered, the anti-commutation relations have to be taken into account, \(a_i a^\dagger_j = \delta_{ij} - a^\dagger_j a^i\), implying that the number of terms may change.
- Parameters:
op – A pointer to the operator.
- Returns:
A pointer to the created operator.
-
bool qf_ferm_op_is_hermitian(const QfFermionOperator *op, double atol)¶
Checks whether an operator is Hermitian.
Example¶
1QfFermionOperator *op = qf_ferm_op_zero(); 2bool actions1[2] = {true, false}; 3uint32_t modes1[2] = {0, 1}; 4QkComplex64 coeff1 = {0.0, 1.00001}; 5qf_ferm_op_add_term(op, 2, actions1, modes1, &coeff1); 6bool actions2[2] = {true, false}; 7uint32_t modes2[2] = {1, 0}; 8QkComplex64 coeff2 = {0.0, -1}; 9qf_ferm_op_add_term(op, 2, actions2, modes2, &coeff1); 10 11assert(qf_ferm_op_is_hermitian(op, 1e-4)); 12assert(!qf_ferm_op_is_hermitian(op, 1e-8));
Note
This check is implemented using
qf_ferm_op_equiv()on theqf_ferm_op_normal_ordered()difference ofopand itsqf_ferm_op_adjoint()andqf_ferm_op_zero().- Parameters:
op – A pointer to the fermionic operator to be checked.
atol – The absolute tolerance upto which coefficients are considered equal.
- Returns:
Whether the provided operator is Hermitian.
-
uint32_t qf_ferm_op_many_body_order(const QfFermionOperator *op)¶
Checks the many-body order of an operator.
Example¶
1QfFermionOperator *op = qf_ferm_op_zero(); 2bool actions[4] = {true, false, true, false}; 3uint32_t modes[4] = {0, 1, 2, 3}; 4QkComplex64 coeff = {1.0, 0.0}; 5qf_ferm_op_add_term(op, 4, actions, modes, &coeff); 6 7assert(qf_ferm_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 fermionic operator to be checked.
- Returns:
The many-body order of the operator.
-
bool qf_ferm_op_conserves_particle_number(const QfFermionOperator *op)¶
Checks whether an operator is particle-number conserving.
Example¶
1QfFermionOperator *op = qf_ferm_op_zero(); 2bool actions1[2] = {true, false}; 3uint32_t modes1[2] = {0, 1}; 4QkComplex64 coeff1 = {0.0, 1.00001}; 5qf_ferm_op_add_term(op, 2, actions1, modes1, &coeff1); 6bool actions2[2] = {true, false}; 7uint32_t modes2[2] = {1, 0}; 8QkComplex64 coeff2 = {0.0, -1}; 9qf_ferm_op_add_term(op, 2, actions2, modes2, &coeff2); 10 11assert(qf_ferm_op_is_hermitian(op, 1e-4)); 12assert(!qf_ferm_op_is_hermitian(op, 1e-8));
- Parameters:
op – A pointer to the fermionic operator to be checked.
- Returns:
Whether the provided operator is particle-number conserving.
-
bool qf_ferm_op_equal(const QfFermionOperator *left, const QfFermionOperator *right)¶
Compare two operators for equality.
Example¶
1QfFermionOperator *one = qf_ferm_op_one(); 2QfFermionOperator *zero = qf_ferm_op_zero(); 3 4assert(qf_ferm_op_equal(one, one)); 5assert(!qf_ferm_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_ferm_op_equiv(const QfFermionOperator *left, const QfFermionOperator *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 differenceother - selfare below the specified thresholdatol.Example¶
1QfFermionOperator *zero = qf_ferm_op_zero(); 2 3QfFermionOperator *op = qf_ferm_op_zero(); 4bool actions[0] = {}; 5uint32_t modes[0] = {}; 6QkComplex64 coeff = {1e-7, 0.0}; 7qf_ferm_op_add_term(op, 0, actions, modes, &coeff); 8 9assert(qf_ferm_op_equiv(op, zero, 1e-6)); 10assert(!qf_ferm_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_ferm_op_len(const QfFermionOperator *op)¶
Returns the length (or number of terms) of the provided operator.
Example¶
1QfFermionOperator *op = qf_ferm_op_zero(); 2bool actions[4] = {true, false, true, false}; 3uint32_t modes[4] = {0, 1, 2, 3}; 4QkComplex64 coeff = {1.0, 0.0}; 5qf_ferm_op_add_term(op, 4, actions, modes, &coeff); 6 7assert(qf_ferm_op_len(op) == 1);
- Parameters:
op – A pointer to the fermionic operator.
- Returns:
The length (or number of terms) of the operator.