qalgebra.core.abstract_algebra module¶
Base classes for all Expressions and Operations.
The abstract algebra package provides the foundation for
symbolic algebra of quantum objects or circuits. All symbolic objects are
an instance of Expression
. Algebraic combinations of atomic
expressions are instances of Operation
. In this way, any symbolic
expression is a tree of operations, with children of each node defined through
the Operation.operands
attribute, and the leaves being atomic
expressions.
See Expressions and Operations for design details and usage.
Summary¶
Classes:
Base class for all QAlgebra Expressions. |
|
Base class for “operations” |
Functions:
Substitute symbols or (sub-)expressions with the given replacements. |
__all__
: Expression
, Operation
, substitute
Reference¶
-
class
qalgebra.core.abstract_algebra.
Expression
(*args, **kwargs)[source]¶ Bases:
object
Base class for all QAlgebra Expressions.
Expressions should generally be instantiated using the
create()
class method, which takes into account the algebraic properties of the Expression and and applies simplifications. It also uses memoization to cache all known (sub-)expression. This is possible because expressions are intended to be immutable. Any changes to an expression should be made through e.g.substitute()
orapply_rule()
, which returns a new modified expression.Every expression has a well-defined list of positional and keyword arguments that uniquely determine the expression and that may be accessed through the
args
andkwargs
property. That is,expr.__class__(*expr.args, **expr.kwargs)
will return and object identical to expr.
- Class Attributes
instance_caching (bool) – Flag to indicate whether the
create()
class method should cache the instantiation of instances. If True, repeated calls tocreate()
with the same arguments return instantly, instead of re-evaluating all simplifications and rules.simplifications (list) – List of callable simplifications that
create()
will use to process its positional and keyword arguments. Each callable must take three parameters (the class, the list args of positional arguments given tocreate()
and a dictionary kwargs of keyword arguments given tocreate()
) and return either a tuple of new args and kwargs (which are then handed to the next callable), or anExpression
(which is directly returned as the result of the call tocreate()
). The built-in available simplification callables are inalgebraic_properties
-
simplifications
= []¶
-
instance_caching
= True¶
-
classmethod
create
(*args, **kwargs)[source]¶ Instantiate while applying automatic simplifications.
Instead of directly instantiating cls, it is recommended to use
create()
, which applies simplifications to the args and keyword arguments according to the simplifications class attribute, and returns an appropriate object (which may or may not be an instance of the original cls).Two simplifications of particular importance are
match_replace()
andmatch_replace_binary()
which apply rule-based simplifications.The
temporary_rules()
context manager may be used to allow temporary modification of the automatic simplifications thatcreate()
uses, in particular the rules formatch_replace()
andmatch_replace_binary()
. Inside the managed context, the simplifications class attribute may be modified and rules can be managed withadd_rule()
anddel_rules()
.
-
classmethod
add_rule
(name, pattern, replacement, attr=None)[source]¶ Add an algebraic rule for
create()
to the class.- Parameters
name (str) – Name of the rule. This is used for debug logging to allow an analysis of which rules where applied when creating an expression. The name can be arbitrary, but it must be unique. Built-in rules have names
'Rxxx'
wherex
is a digitpattern (Pattern) – A pattern constructed by
pattern_head()
to match aProtoExpr
replacement (callable) – callable that takes the wildcard names defined in pattern as keyword arguments and returns an evaluated expression.
attr (None or str) – Name of the class attribute to which to add the rule. If None, one of
'_rules'
,'_binary_rules'
is automatically chosen
- Raises
TypeError – if name is not a
str
or pattern is not aPattern
instanceValueError – if pattern is not set up to match a
ProtoExpr
; if there there is already a rule with the same name; if replacement is not a callable or does not take all the wildcard names in pattern as argumentsAttributeError – If invalid attr
Note
The “automatic” rules added by this method are applied before expressions are instantiated (against a corresponding
ProtoExpr
). In contrast,apply_rules()
/apply_rule()
are applied to fully instantiated objects.The
temporary_rules()
context manager may be used to create a context in which rules may be defined locally.
-
classmethod
show_rules
(*names, attr=None)[source]¶ Print algebraic rules used by
create
.Print a summary of the algebraic rules with the given names, or all rules if not names a given.
- Parameters
names (str) – Names of rules to show
attr (None or str) – Name of the class attribute from which to get the rules. Cf.
add_rule()
.
- Raises
AttributeError – If invalid attr
-
classmethod
del_rules
(*names, attr=None)[source]¶ Delete algebraic rules used by
create()
Remove the rules with the given names, or all rules if no names are given
- Parameters
names (str) – Names of rules to delete
attr (None or str) – Name of the class attribute from which to delete the rules. Cf.
add_rule()
.
- Raises
KeyError – If any rules in names does not exist
AttributeError – If invalid attr
-
abstract property
args
¶ The tuple of positional arguments for the instantiation of the Expression
-
property
kwargs
¶ The dictionary of keyword-only arguments for the instantiation of the Expression
-
property
minimal_kwargs
¶ A “minimal” dictionary of keyword-only arguments, i.e. a subset of kwargs that may exclude default options
-
substitute
(var_map)[source]¶ Substitute sub-expressions
- Parameters
var_map (dict) – Dictionary with entries of the form
{expr: substitution}
-
doit
(classes=None, recursive=True, **kwargs)[source]¶ Rewrite (sub-)expressions in a more explicit form
Return a modified expression that is more explicit than the original expression. The definition of “more explicit” is decided by the relevant subclass, e.g. a
Commutator
is written out according to its definition.- Parameters
classes (None or list) – an optional list of classes. If given, only (sub-)expressions that an instance of one of the classes in the list will be rewritten.
recursive (bool) – If True, also rewrite any sub-expressions of any rewritten expression. Note that
doit()
always recurses into sub-expressions of expressions not affected by it.kwargs – Any remaining keyword arguments may be used by the
doit()
method of a particular expression.
Example
Consider the following expression:
>>> from sympy import IndexedBase >>> i = IdxSym('i'); N = symbols('N') >>> Asym, Csym = symbols('A, C', cls=IndexedBase) >>> A = lambda i: OperatorSymbol(StrLabel(Asym[i]), hs=0) >>> B = OperatorSymbol('B', hs=0) >>> C = lambda i: OperatorSymbol(StrLabel(Csym[i]), hs=0) >>> def show(expr): ... print(unicode(expr, show_hs_label=False)) >>> expr = Sum(i, 1, 3)(Commutator(A(i), B) + C(i)) / N >>> show(expr) 1/N (∑_{i=1}^{3} (Ĉ_i + [Â_i, B̂]))
Calling
doit()
without parameters rewrites both the indexed sum and the commutator:>>> show(expr.doit()) 1/N (Ĉ₁ + Ĉ₂ + Ĉ₃ + Â₁ B̂ + Â₂ B̂ + Â₃ B̂ - B̂ Â₁ - B̂ Â₂ - B̂ Â₃)
A non-recursive call only expands the sum, as it does not recurse into the expanded summands:
>>> show(expr.doit(recursive=False)) 1/N (Ĉ₁ + Ĉ₂ + Ĉ₃ + [Â₁, B̂] + [Â₂, B̂] + [Â₃, B̂])
We can selectively expand only the sum or only the commutator:
>>> show(expr.doit(classes=[IndexedSum])) 1/N (Ĉ₁ + Ĉ₂ + Ĉ₃ + [Â₁, B̂] + [Â₂, B̂] + [Â₃, B̂]) >>> show(expr.doit(classes=[Commutator])) 1/N (∑_{i=1}^{3} (Ĉ_i - B̂ Â_i + Â_i B̂))
Also we can pass a keyword argument that expands the sum only to the 2nd term, as documented in
Commutator.doit()
>>> show(expr.doit(classes=[IndexedSum], max_terms=2)) 1/N (Ĉ₁ + Ĉ₂ + [Â₁, B̂] + [Â₂, B̂])
-
apply
(func, *args, **kwargs)[source]¶ Apply func to expression.
Equivalent to
func(self, *args, **kwargs)
. This method exists for easy chaining:>>> A, B, C, D = ( ... OperatorSymbol(s, hs=1) for s in ('A', 'B', 'C', 'D')) >>> expr = ( ... Commutator(A * B, C * D) ... .apply(lambda expr: expr**2) ... .apply(expand_commutators_leibniz, expand_expr=False) ... .substitute({A: IdentityOperator}))
-
apply_rules
(rules, recursive=True)[source]¶ Rebuild the expression while applying a list of rules.
The rules are applied against the instantiated expression, and any sub-expressions if recursive is True. Rule application is best though of as a pattern-based substitution. This is different from the automatic rules that
create()
uses (seeadd_rule()
), which are applied before expressions are instantiated.- Parameters
rules (list or OrderedDict) – List of rules or dictionary mapping names to rules, where each rule is a tuple (
Pattern
, replacement callable), cf.apply_rule()
recursive (bool) – If true (default), apply rules to all arguments and keyword arguments of the expression. Otherwise, only the expression itself will be re-instantiated.
If rules is a dictionary, the keys (rules names) are used only for debug logging, to allow an analysis of which rules lead to the final form of an expression.
-
apply_rule
(pattern, replacement, recursive=True)[source]¶ Apply a single rules to the expression
This is equivalent to
apply_rules()
withrules=[(pattern, replacement)]
- Parameters
pattern (Pattern) – A pattern containing one or more wildcards
replacement (callable) – A callable that takes the wildcard names in pattern as keyword arguments, and returns a replacement for any expression that pattern matches.
Example
Consider the following Heisenberg Hamiltonian:
>>> tls = SpinSpace(label='s', spin='1/2') >>> i, j, n = symbols('i, j, n', cls=IdxSym) >>> J = symbols('J', cls=sympy.IndexedBase) >>> def Sig(i): ... return OperatorSymbol( ... StrLabel(sympy.Indexed('sigma', i)), hs=tls) >>> H = - Sum(i, tls)(Sum(j, tls)( ... J[i, j] * Sig(i) * Sig(j))) >>> unicode(H) '- (∑_{i,j ∈ ℌₛ} J_ij σ̂_i^(s) σ̂_j^(s))'
We can transform this into a classical Hamiltonian by replacing the operators with scalars:
>>> H_classical = H.apply_rule( ... pattern(OperatorSymbol, wc('label', head=StrLabel)), ... lambda label: label.expr * IdentityOperator) >>> unicode(H_classical) '- (∑_{i,j ∈ ℌₛ} J_ij σ_i σ_j)'
-
rebuild
()[source]¶ Recursively re-instantiate the expression.
This is generally used within a managed context such as
temporary_rules()
.
-
property
free_symbols
¶ Set of free SymPy symbols contained within the expression.
-
property
bound_symbols
¶ Set of bound SymPy symbols in the expression.
-
property
all_symbols
¶ Combination of
free_symbols
andbound_symbols
.
-
class
qalgebra.core.abstract_algebra.
Operation
(*operands, **kwargs)[source]¶ Bases:
qalgebra.core.abstract_algebra.Expression
Base class for “operations”
Operations are Expressions that act algebraically on other expressions (their “operands”).
Operations differ from more general Expressions by the convention that the arguments of the Operator are exactly the operands (which must be members of the algebra!) Any other parameters (non-operands) that may be required must be given as keyword-arguments.
-
property
operands
¶ Tuple of operands of the operation
-
property
args
¶ Alias for operands
-
property