qnet.algebra.core.algebraic_properties module

Summary

Functions:

accept_bras Accept operands that are all bras, and turn that into to bra of the operation applied to all corresponding kets
assoc Associatively expand out nested arguments of the flat class.
assoc_indexed Flatten nested indexed structures while pulling out possible prefactors
basis_ket_zero_outside_hs For BasisKet.create(ind, hs) with an integer label ind, return a ZeroKet if ind is outside of the range of the underlying Hilbert space
check_cdims Check that all operands (ops) have equal channel dimension.
collect_scalar_summands Collect ValueScalar and ScalarExpression summands
collect_summands Collect summands that occur multiple times into a single summand
commutator_order Apply anti-commutative property of the commutator to apply a standard ordering of the commutator arguments
convert_to_scalars Convert any entry in ops that is not a Scalar instance into a ScalarValue instance
convert_to_spaces For all operands that are merely of type str or int, substitute LocalSpace objects with corresponding labels: For a string, just itself, for an int, a string version of that int.
delegate_to_method Create a simplification rule that delegates the instantiation to the method mtd of the operand (if defined)
derivative_via_diff Implementation of the QuantumDerivative.create() interface via the use of QuantumExpression._diff().
disjunct_hs_zero Return ZeroOperator if all the operators in ops have a disjunct Hilbert space, or an unchanged ops, kwargs otherwise
empty_trivial A ProductSpace of zero Hilbert spaces should yield the TrivialSpace
filter_cid Remove occurrences of the circuit_identity() cid(n) for any n.
filter_neutral Remove occurrences of a neutral element from the argument/operand list, if that list has at least two elements.
idem Remove duplicate arguments and order them via the cls’s order_key key object/function.
implied_local_space Return a simplification that converts the positional argument arg_index from (str, int) to a subclass of LocalSpace, as well as any keyword argument with one of the given keys.
indexed_sum_over_const Execute an indexed sum over a term that does not depend on the summation indices
indexed_sum_over_kronecker Execute sums over KroneckerDelta prefactors
match_replace Match and replace a full operand specification to a function that provides a replacement for the whole expression or raises a CannotSimplify exception.
match_replace_binary Similar to func:match_replace, but for arbitrary length operations, such that each two pairs of subsequent operands are matched pairwise.
orderby Re-order arguments via the class’s order_key key object/function.
scalars_to_op Convert any scalar \(\alpha\) in ops into an operator $alpha identity$

Reference

qnet.algebra.core.algebraic_properties.assoc(cls, ops, kwargs)[source]

Associatively expand out nested arguments of the flat class. E.g.:

>>> class Plus(Operation):
...     simplifications = [assoc, ]
>>> Plus.create(1,Plus(2,3))
Plus(1, 2, 3)
qnet.algebra.core.algebraic_properties.assoc_indexed(cls, ops, kwargs)[source]

Flatten nested indexed structures while pulling out possible prefactors

For example, for an IndexedSum:

\[\sum_j \left( a \sum_i \dots \right) = a \sum_{j, i} \dots\]
qnet.algebra.core.algebraic_properties.idem(cls, ops, kwargs)[source]

Remove duplicate arguments and order them via the cls’s order_key key object/function. E.g.:

>>> class Set(Operation):
...     order_key = lambda val: val
...     simplifications = [idem, ]
>>> Set.create(1,2,3,1,3)
Set(1, 2, 3)
qnet.algebra.core.algebraic_properties.orderby(cls, ops, kwargs)[source]

Re-order arguments via the class’s order_key key object/function. Use this for commutative operations: E.g.:

>>> class Times(Operation):
...     order_key = lambda val: val
...     simplifications = [orderby, ]
>>> Times.create(2,1)
Times(1, 2)
qnet.algebra.core.algebraic_properties.filter_neutral(cls, ops, kwargs)[source]

Remove occurrences of a neutral element from the argument/operand list, if that list has at least two elements. To use this, one must also specify a neutral element, which can be anything that allows for an equality check with each argument. E.g.:

>>> class X(Operation):
...     _neutral_element = 1
...     simplifications = [filter_neutral, ]
>>> X.create(2,1,3,1)
X(2, 3)
qnet.algebra.core.algebraic_properties.collect_summands(cls, ops, kwargs)[source]

Collect summands that occur multiple times into a single summand

Also filters out zero-summands.

Example

>>> A, B, C = (OperatorSymbol(s, hs=0) for s in ('A', 'B', 'C'))
>>> collect_summands(
...     OperatorPlus, (A, B, C, ZeroOperator, 2 * A, B, -C) , {})
((3 * A^(0), 2 * B^(0)), {})
>>> collect_summands(OperatorPlus, (A, -A), {})
ZeroOperator
>>> collect_summands(OperatorPlus, (B, A, -B), {})
A^(0)
qnet.algebra.core.algebraic_properties.collect_scalar_summands(cls, ops, kwargs)[source]

Collect ValueScalar and ScalarExpression summands

Example

>>> srepr(collect_scalar_summands(Scalar, (1, 2, 3), {}))
'ScalarValue(6)'
>>> collect_scalar_summands(Scalar, (1, 1, -1), {})
One
>>> collect_scalar_summands(Scalar, (1, -1), {})
Zero
>>> Psi = KetSymbol("Psi", hs=0)
>>> Phi = KetSymbol("Phi", hs=0)
>>> braket = BraKet.create(Psi, Phi)
>>> collect_scalar_summands(Scalar, (1, braket, -1), {})
<Psi|Phi>^(0)
>>> collect_scalar_summands(Scalar, (1, 2 * braket, 2, 2 * braket), {})
((3, 4 * <Psi|Phi>^(0)), {})
>>> collect_scalar_summands(Scalar, (2 * braket, -braket, -braket), {})
Zero
qnet.algebra.core.algebraic_properties.match_replace(cls, ops, kwargs)[source]

Match and replace a full operand specification to a function that provides a replacement for the whole expression or raises a CannotSimplify exception. E.g.

First define an operation:

>>> class Invert(Operation):
...     _rules = OrderedDict()
...     simplifications = [match_replace, ]

Then some _rules:

>>> A = wc("A")
>>> A_float = wc("A", head=float)
>>> Invert_A = pattern(Invert, A)
>>> Invert._rules.update([
...     ('r1', (pattern_head(Invert_A), lambda A: A)),
...     ('r2', (pattern_head(A_float), lambda A: 1./A)),
... ])

Check rule application:

>>> print(srepr(Invert.create("hallo")))  # matches no rule
Invert('hallo')
>>> Invert.create(Invert("hallo"))        # matches first rule
'hallo'
>>> Invert.create(.2)                     # matches second rule
5.0

A pattern can also have the same wildcard appear twice:

>>> class X(Operation):
...     _rules = {
...         'r1': (pattern_head(A, A), lambda A: A),
...     }
...     simplifications = [match_replace, ]
>>> X.create(1,2)
X(1, 2)
>>> X.create(1,1)
1
qnet.algebra.core.algebraic_properties.match_replace_binary(cls, ops, kwargs)[source]

Similar to func:match_replace, but for arbitrary length operations, such that each two pairs of subsequent operands are matched pairwise.

>>> A = wc("A")
>>> class FilterDupes(Operation):
...     _binary_rules = {
...          'filter_dupes': (pattern_head(A,A), lambda A: A)}
...     simplifications = [match_replace_binary, assoc]
...     _neutral_element = 0
>>> FilterDupes.create(1,2,3,4)         # No duplicates
FilterDupes(1, 2, 3, 4)
>>> FilterDupes.create(1,2,2,3,4)       # Some duplicates
FilterDupes(1, 2, 3, 4)

Note that this only works for subsequent duplicate entries:

>>> FilterDupes.create(1,2,3,2,4)       # No *subsequent* duplicates
FilterDupes(1, 2, 3, 2, 4)

Any operation that uses binary reduction must be associative and define a neutral element. The binary rules must be compatible with associativity, i.e. there is no specific order in which the rules are applied to pairs of operands.

qnet.algebra.core.algebraic_properties.check_cdims(cls, ops, kwargs)[source]

Check that all operands (ops) have equal channel dimension.

qnet.algebra.core.algebraic_properties.filter_cid(cls, ops, kwargs)[source]

Remove occurrences of the circuit_identity() cid(n) for any n. Cf. filter_neutral()

qnet.algebra.core.algebraic_properties.convert_to_spaces(cls, ops, kwargs)[source]

For all operands that are merely of type str or int, substitute LocalSpace objects with corresponding labels: For a string, just itself, for an int, a string version of that int.

qnet.algebra.core.algebraic_properties.empty_trivial(cls, ops, kwargs)[source]

A ProductSpace of zero Hilbert spaces should yield the TrivialSpace

qnet.algebra.core.algebraic_properties.implied_local_space(*, arg_index=None, keys=None)[source]

Return a simplification that converts the positional argument arg_index from (str, int) to a subclass of LocalSpace, as well as any keyword argument with one of the given keys.

The exact type of the resulting Hilbert space is determined by the default_hs_cls argument of init_algebra().

In many cases, we have implied_local_space() (in create) in addition to a conversion in __init__, so that match_replace() etc can rely on the relevant arguments being a HilbertSpace instance.

qnet.algebra.core.algebraic_properties.delegate_to_method(mtd)[source]

Create a simplification rule that delegates the instantiation to the method mtd of the operand (if defined)

qnet.algebra.core.algebraic_properties.scalars_to_op(cls, ops, kwargs)[source]

Convert any scalar \(\alpha\) in ops into an operator $alpha identity$

qnet.algebra.core.algebraic_properties.convert_to_scalars(cls, ops, kwargs)[source]

Convert any entry in ops that is not a Scalar instance into a ScalarValue instance

qnet.algebra.core.algebraic_properties.disjunct_hs_zero(cls, ops, kwargs)[source]

Return ZeroOperator if all the operators in ops have a disjunct Hilbert space, or an unchanged ops, kwargs otherwise

qnet.algebra.core.algebraic_properties.commutator_order(cls, ops, kwargs)[source]

Apply anti-commutative property of the commutator to apply a standard ordering of the commutator arguments

qnet.algebra.core.algebraic_properties.accept_bras(cls, ops, kwargs)[source]

Accept operands that are all bras, and turn that into to bra of the operation applied to all corresponding kets

qnet.algebra.core.algebraic_properties.basis_ket_zero_outside_hs(cls, ops, kwargs)[source]

For BasisKet.create(ind, hs) with an integer label ind, return a ZeroKet if ind is outside of the range of the underlying Hilbert space

qnet.algebra.core.algebraic_properties.indexed_sum_over_const(cls, ops, kwargs)[source]

Execute an indexed sum over a term that does not depend on the summation indices

\[\sum_{j=1}^{N} a = N a\]
>>> a = symbols('a')
>>> i, j  = (IdxSym(s) for s in ('i', 'j'))
>>> unicode(Sum(i, 1, 2)(a))
'2 a'
>>> unicode(Sum(j, 1, 2)(Sum(i, 1, 2)(a * i)))
'∑_{i=1}^{2} 2 i a'
qnet.algebra.core.algebraic_properties.indexed_sum_over_kronecker(cls, ops, kwargs)[source]

Execute sums over KroneckerDelta prefactors

qnet.algebra.core.algebraic_properties.derivative_via_diff(cls, ops, kwargs)[source]

Implementation of the QuantumDerivative.create() interface via the use of QuantumExpression._diff().

Thus, by having QuantumExpression.diff() delegate to QuantumDerivative.create(), instead of QuantumExpression._diff() directly, we get automatic caching of derivatives