123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- """ A module for mapping operators to their corresponding eigenstates
- and vice versa
- It contains a global dictionary with eigenstate-operator pairings.
- If a new state-operator pair is created, this dictionary should be
- updated as well.
- It also contains functions operators_to_state and state_to_operators
- for mapping between the two. These can handle both classes and
- instances of operators and states. See the individual function
- descriptions for details.
- TODO List:
- - Update the dictionary with a complete list of state-operator pairs
- """
- from sympy.physics.quantum.cartesian import (XOp, YOp, ZOp, XKet, PxOp, PxKet,
- PositionKet3D)
- from sympy.physics.quantum.operator import Operator
- from sympy.physics.quantum.state import StateBase, BraBase, Ket
- from sympy.physics.quantum.spin import (JxOp, JyOp, JzOp, J2Op, JxKet, JyKet,
- JzKet)
- __all__ = [
- 'operators_to_state',
- 'state_to_operators'
- ]
- #state_mapping stores the mappings between states and their associated
- #operators or tuples of operators. This should be updated when new
- #classes are written! Entries are of the form PxKet : PxOp or
- #something like 3DKet : (ROp, ThetaOp, PhiOp)
- #frozenset is used so that the reverse mapping can be made
- #(regular sets are not hashable because they are mutable
- state_mapping = { JxKet: frozenset((J2Op, JxOp)),
- JyKet: frozenset((J2Op, JyOp)),
- JzKet: frozenset((J2Op, JzOp)),
- Ket: Operator,
- PositionKet3D: frozenset((XOp, YOp, ZOp)),
- PxKet: PxOp,
- XKet: XOp }
- op_mapping = {v: k for k, v in state_mapping.items()}
- def operators_to_state(operators, **options):
- """ Returns the eigenstate of the given operator or set of operators
- A global function for mapping operator classes to their associated
- states. It takes either an Operator or a set of operators and
- returns the state associated with these.
- This function can handle both instances of a given operator or
- just the class itself (i.e. both XOp() and XOp)
- There are multiple use cases to consider:
- 1) A class or set of classes is passed: First, we try to
- instantiate default instances for these operators. If this fails,
- then the class is simply returned. If we succeed in instantiating
- default instances, then we try to call state._operators_to_state
- on the operator instances. If this fails, the class is returned.
- Otherwise, the instance returned by _operators_to_state is returned.
- 2) An instance or set of instances is passed: In this case,
- state._operators_to_state is called on the instances passed. If
- this fails, a state class is returned. If the method returns an
- instance, that instance is returned.
- In both cases, if the operator class or set does not exist in the
- state_mapping dictionary, None is returned.
- Parameters
- ==========
- arg: Operator or set
- The class or instance of the operator or set of operators
- to be mapped to a state
- Examples
- ========
- >>> from sympy.physics.quantum.cartesian import XOp, PxOp
- >>> from sympy.physics.quantum.operatorset import operators_to_state
- >>> from sympy.physics.quantum.operator import Operator
- >>> operators_to_state(XOp)
- |x>
- >>> operators_to_state(XOp())
- |x>
- >>> operators_to_state(PxOp)
- |px>
- >>> operators_to_state(PxOp())
- |px>
- >>> operators_to_state(Operator)
- |psi>
- >>> operators_to_state(Operator())
- |psi>
- """
- if not (isinstance(operators, Operator)
- or isinstance(operators, set) or issubclass(operators, Operator)):
- raise NotImplementedError("Argument is not an Operator or a set!")
- if isinstance(operators, set):
- for s in operators:
- if not (isinstance(s, Operator)
- or issubclass(s, Operator)):
- raise NotImplementedError("Set is not all Operators!")
- ops = frozenset(operators)
- if ops in op_mapping: # ops is a list of classes in this case
- #Try to get an object from default instances of the
- #operators...if this fails, return the class
- try:
- op_instances = [op() for op in ops]
- ret = _get_state(op_mapping[ops], set(op_instances), **options)
- except NotImplementedError:
- ret = op_mapping[ops]
- return ret
- else:
- tmp = [type(o) for o in ops]
- classes = frozenset(tmp)
- if classes in op_mapping:
- ret = _get_state(op_mapping[classes], ops, **options)
- else:
- ret = None
- return ret
- else:
- if operators in op_mapping:
- try:
- op_instance = operators()
- ret = _get_state(op_mapping[operators], op_instance, **options)
- except NotImplementedError:
- ret = op_mapping[operators]
- return ret
- elif type(operators) in op_mapping:
- return _get_state(op_mapping[type(operators)], operators, **options)
- else:
- return None
- def state_to_operators(state, **options):
- """ Returns the operator or set of operators corresponding to the
- given eigenstate
- A global function for mapping state classes to their associated
- operators or sets of operators. It takes either a state class
- or instance.
- This function can handle both instances of a given state or just
- the class itself (i.e. both XKet() and XKet)
- There are multiple use cases to consider:
- 1) A state class is passed: In this case, we first try
- instantiating a default instance of the class. If this succeeds,
- then we try to call state._state_to_operators on that instance.
- If the creation of the default instance or if the calling of
- _state_to_operators fails, then either an operator class or set of
- operator classes is returned. Otherwise, the appropriate
- operator instances are returned.
- 2) A state instance is returned: Here, state._state_to_operators
- is called for the instance. If this fails, then a class or set of
- operator classes is returned. Otherwise, the instances are returned.
- In either case, if the state's class does not exist in
- state_mapping, None is returned.
- Parameters
- ==========
- arg: StateBase class or instance (or subclasses)
- The class or instance of the state to be mapped to an
- operator or set of operators
- Examples
- ========
- >>> from sympy.physics.quantum.cartesian import XKet, PxKet, XBra, PxBra
- >>> from sympy.physics.quantum.operatorset import state_to_operators
- >>> from sympy.physics.quantum.state import Ket, Bra
- >>> state_to_operators(XKet)
- X
- >>> state_to_operators(XKet())
- X
- >>> state_to_operators(PxKet)
- Px
- >>> state_to_operators(PxKet())
- Px
- >>> state_to_operators(PxBra)
- Px
- >>> state_to_operators(XBra)
- X
- >>> state_to_operators(Ket)
- O
- >>> state_to_operators(Bra)
- O
- """
- if not (isinstance(state, StateBase) or issubclass(state, StateBase)):
- raise NotImplementedError("Argument is not a state!")
- if state in state_mapping: # state is a class
- state_inst = _make_default(state)
- try:
- ret = _get_ops(state_inst,
- _make_set(state_mapping[state]), **options)
- except (NotImplementedError, TypeError):
- ret = state_mapping[state]
- elif type(state) in state_mapping:
- ret = _get_ops(state,
- _make_set(state_mapping[type(state)]), **options)
- elif isinstance(state, BraBase) and state.dual_class() in state_mapping:
- ret = _get_ops(state,
- _make_set(state_mapping[state.dual_class()]))
- elif issubclass(state, BraBase) and state.dual_class() in state_mapping:
- state_inst = _make_default(state)
- try:
- ret = _get_ops(state_inst,
- _make_set(state_mapping[state.dual_class()]))
- except (NotImplementedError, TypeError):
- ret = state_mapping[state.dual_class()]
- else:
- ret = None
- return _make_set(ret)
- def _make_default(expr):
- # XXX: Catching TypeError like this is a bad way of distinguishing between
- # classes and instances. The logic using this function should be rewritten
- # somehow.
- try:
- ret = expr()
- except TypeError:
- ret = expr
- return ret
- def _get_state(state_class, ops, **options):
- # Try to get a state instance from the operator INSTANCES.
- # If this fails, get the class
- try:
- ret = state_class._operators_to_state(ops, **options)
- except NotImplementedError:
- ret = _make_default(state_class)
- return ret
- def _get_ops(state_inst, op_classes, **options):
- # Try to get operator instances from the state INSTANCE.
- # If this fails, just return the classes
- try:
- ret = state_inst._state_to_operators(op_classes, **options)
- except NotImplementedError:
- if isinstance(op_classes, (set, tuple, frozenset)):
- ret = tuple(_make_default(x) for x in op_classes)
- else:
- ret = _make_default(op_classes)
- if isinstance(ret, set) and len(ret) == 1:
- return ret[0]
- return ret
- def _make_set(ops):
- if isinstance(ops, (tuple, list, frozenset)):
- return set(ops)
- else:
- return ops
|