I’m writing a module where a user may create and analyze arbitrary “compositions” (expressions) of so-called CrystalOperators
.
Right now, the manual workflow to analyze an arbitrary composition is quite laborious. I am pretty sure it is possible to completely automate the procedure via metaprogramming/macros. I tried many things but unfortunately I always run into problems which I’m not able to solve. Hopefully, you can tell me if and how such an automation is possible.
Workflow example
I give an example of a workflow of a potential user.
A user defines instances of CrystalOperators:
x_1::CrystalOperator, x_2::CrystalOperator, x_3::CrystalOperator
and for example other variables such as z::Int
He/she wants to analyze a “composition” or expression of these variables, for example f = x_1*x_2+z*inv(x_3)
. To this end, he has to carry out the following steps manually. (I want this to be automated):
- CrystalOperators are mathematical functions defined with respect to a “lattice”. In the first step we need to find a (least) common sublattice and convert all Crystaloperators accordingly. ( You may think of this step as a conversion to the same datatype x::Float64 + y::Int is computed via x::Float64 + y::Float64. I need to store x::Float64 and y::Float64 to proceed further. ):
co_vec = [x_1, x_2, x_3] # Create and Array of the CrystylOperators within the expression f.
# Find the least common sublattice A:
A = nothing
for x in co_vec
if A == nothing
A = x.C.L.A
else
A = lcm(A, x.C.L.A)
end
end
# Rewrite all Operators with respect to the least common sublattice A:
x_1 = wrtLattice(x_1, A)
x_2 = wrtLattice(x_2, A)
x_3 = wrtLattice(x_3, A)
- We need to make sure that this Expression f makes sense. (As an analogy you may think of these crystaloperators as matrices. Computing
x_1*x_2+z*inv(x_3)
makes only sense if the corresponding number of rows and columns are equal.)
try
x_1*x_2+z*inv(x_3)
catch e
@show e
end
- If the above test was succesful, we may proceed with step 3. We define a function based on the expression f ( we basically replace each appearance of a CrystalOperator x with symbol(x,k) ).
sym_f = (k) -> symbol(x_1,k)*symbol(x_2, k)+z*inv(symbol(x_3, k))
- Finally, we are able to analyze our operator composition. Oftentimes we are interested in the eigenvalues of sym_f(k) (which is nothing else than a (square) matrix.) for many different values of k:
kvec = 0:0.1:1 # this
eigs = [eigvals(sym_f(k)) for k in kvec]
How to automate this?
Is it possible (how?) to automate these steps? It is important that steps 1 and 2 are only executed once as the computation can be very complex. I would like to have a macro or function which executes steps 1-3 and spits out the function sym_f:
sym_f = @create_sym(x_1*x_2+z*inv(x_3))
The problem is, that I need to access and manipulate the variables of the current workspace.
Ideally, I would like to store copies of the variables and functions such as “sym_f” within a struct:
struct OperatorComposition
sym_f::Expr
mydict::Dict
end
Then I would like to have a macro which creates such an OperatorComposition via
oc = @createOperatorComposition x_1*x_2+z*inv(x_3)
such that
oc.dict = Dict(:x_1 => x_1, :x_2 => x_2, :x_3 => x_3)` # i.e., oc.dict[:x_i] is the output of step 1 (wrtLattice(x_1, A)
and
oc.sym_f = (k) -> symbol(Ref(oc.mydict[:x_1]),k)*symbol(Ref(oc.mydict[:x_2]), k)+z*inv(symbol(Ref(oc.mydict[:x_3]), k))
Please let me know if anything is unclear. I’m grateful for any advice.