QNET
QNET copied to clipboard
Implement scalar algebra
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 inval
attribute (instance ofsympy.Basic
,int
,np.float64
,...). Further subclassing intoSymbolicScalar
to wrap around SymPy expressions andNumericScalar
for everything else is possible, but probably not necessary. -
ExpressionScalar
: base class for e.g.Braket
andBraOpKet
-
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 byScalarPower
) -
ScalarPower
, e.g.√⟨ϕ|Ψ⟩
or⟨ϕ|Ψ⟩ⁿ
- ~~
ScalarAbs
, e.g.|⟨ϕ|Ψ⟩|
~~ - A
ScalarConjugate
class for⟨ϕ|Ψ⟩⁺
probably can be avoided, if we can guarantee that for anyscalar
,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 appropriateOperation
subclass -
ScalarTimesExpression
subclasses will require that the coefficient is aScalar
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 toScalar
in__init__
. These conversions must happen in__init__
, not increate
. - For
ValueScalar
instance, we definitely wanthash(scalar) == hash(scalar.val)
andscalar == 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 aspace
attribute for an associated Hilbert space. Instances ofValueScalar
are associated withTrivialSpace
. ~~ForBraket
, the Hilbert space would be the Hilbert space on which the expression is the inner product. We probably don't really need to enforce thatScalarTimesExpression
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 ofScalarTimes
(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 printValueScalar
factors left of theKet
, andExpressionScalar
factor to the right, to print something likeα|Ψ⟩⟨ϕ|Ψ⟩
instead ofα⟨ϕ|Ψ⟩|Ψ⟩
. For the associatedBra
, all factors should be printed on the left.
The printing of scalars isn't fully implemented yet (specifically the "advanced" printing for ScalarTimesKet
and ScalarPower
)
Once the printing for ScalarPower
is fully implemented, it should be used in the doctest for sqrt