QNET icon indicating copy to clipboard operation
QNET copied to clipboard

Implement scalar algebra

Open goerz opened this issue 6 years ago • 2 comments

Following up on #63: After giving it some thought, I think we do need to implement a scalar algebra, mostly to properly handle Braket (and BraOpKet, which is a class we should have). Fully identifying scalars in the various algebras is going to be quite useful, as we can freely commute scalars to bring expressions into some desired form for a simplification. Also, there's a bunch of expressions that commonly occur in perturbation theory (brakets in the denominator) that we currently can't express at all.

Algebraic Elements

  • Scalar: base class for all scalars, with subclasses:
    • ValueScalar: wrapper around what we currently consider a scalars. It stores the wrapped value in val attribute (instance of sympy.Basic, int, np.float64,...). Further subclassing into SymbolicScalar to wrap around SymPy expressions and NumericScalar for everything else is possible, but probably not necessary.
    • ExpressionScalar: base class for e.g. Braket and BraOpKet

Singletons

The Singletons for the neutral elements are Zero and One, and are instances of ValueScalar.

Operations

The Operation subclasses in the algebra would be:

  • ScalarPlus, e.g. ⟨ϕ|Ψ⟩ + 1
  • ScalarTimes, e.g. 2⟨ϕ|Ψ⟩
  • ~~ScalarQuotient, e.g. 1/⟨ϕ|Ψ⟩~~ (better expressed by ScalarPower)
  • ScalarPower, e.g. √⟨ϕ|Ψ⟩ or ⟨ϕ|Ψ⟩ⁿ
  • ~~ScalarAbs, e.g. |⟨ϕ|Ψ⟩|~~
  • A ScalarConjugate class for ⟨ϕ|Ψ⟩⁺ probably can be avoided, if we can guarantee that for any scalar, scalar.conjugate() can be translated into its operands.

Notes

  • Any operation between two ValueScalar instances automatically combines the values: ValueScalar(α) + ValueScalar(β) = ValueScalar(α+β). All the numeric magic methods should be defined appropriately.
  • Operations involving an ExpressionScalar (like the examples above) stay instances of the appropriate Operation subclass
  • ScalarTimesExpression subclasses will require that the coefficient is a Scalar instance. They will do an automatic conversion in their __init__ routine: if not isinstance(coeff, Scalar): ValueScalar.create(coeff)
  • Other classes that currently take scalars as parameters, e.g. the amplitude for the CoherentStateKet likewise convert to Scalar in __init__. These conversions must happen in __init__, not in create.
  • For ValueScalar instance, we definitely want hash(scalar) == hash(scalar.val) and scalar == scalar.val). This will be safe because of the conversions happening in the __init__ routines.
  • The current SCALAR_TYPES list is obsolete
  • Scalar instances have a space attribute for an associated Hilbert space. Instances of ValueScalar are associated with TrivialSpace. ~~For Braket, the Hilbert space would be the Hilbert space on which the expression is the inner product. We probably don't really need to enforce that ScalarTimesExpression has a scalar and an expression in compatible spaces (although we could, as a "sanity check"). Mostly, the Hilbert space attribute will be used for printing, and for ordering the operands of ScalarTimes (factors associated with the same Hilbert space should be grouped together, making it easier to apply "binary" simplification rules to them)~~
  • When printing ScalarTimesKet, we'd probably want to be a little bit fancy and print ValueScalar factors left of the Ket, and ExpressionScalar factor to the right, to print something like α|Ψ⟩⟨ϕ|Ψ⟩ instead of α⟨ϕ|Ψ⟩|Ψ⟩. For the associated Bra, all factors should be printed on the left.

goerz avatar Apr 26 '18 22:04 goerz

The printing of scalars isn't fully implemented yet (specifically the "advanced" printing for ScalarTimesKet and ScalarPower)

goerz avatar May 21 '18 22:05 goerz

Once the printing for ScalarPower is fully implemented, it should be used in the doctest for sqrt

goerz avatar Jun 14 '18 18:06 goerz