The internals of TransmissionChannelAnalysis.jl all resolve around the type Q, which is the internal representation of a transmission condition using the variables of the systems form; thus \(x_i\) and not\(y_{i,t}\).
We denote with Q(b), where b is a Boolean statement, a transmission question.
Important: Each string in s: should be a Boolean statement involving variables x``followed by a number, i.e.x1,x2`, etc. Additionally, each Boolean statement should contain only AND (&) and NOT (!) statements.
Important: Users should only use the Q(i::Int) constructor. All other constructors are for internal use only. Misuse of the other constructors easily leads to mistakes.
Fields
vars::Vector{String}: Contains the variables. These will be Boolean statements containing only AND and NOT.
multiplier::Vector{Number}: Multiplier in front of Q(b).
Arguments
s::Union{String, Vector{String}}: String representation of transmission condition.
m::Union{Number, Vector{Number}}: Multipliers for transmission conditions.
i::Int: Variable number.
```{julia}ge
# Defining all variables in one gox = [Q("x$i") for i =1:10]q = (x[1] | x[2]) & !x[3]# Alternatively variables can be defined separaterlyx1 =Q("x1")x2 =Q("x2")x3 =Q("x3")q = (x1 | x2) & !x3# The following are also validq =Q("x1 & !x3", 1)q =Q(["x1", "x2", "x1 & x2"], [1, 1, -1])# The following is NOT valid but does not yet throw an error or warningq =Q("x1 | x2") # DO NOT DO THIS!
The basic idea is to start with a singleton statement, such as \(x_i\), \(\neg x_i\) or sometimes even just \(\text{TRUE}\). Each of these singleton – or atomic – statements refers to the simplest transmission channel in which \(x_i\) must be on the path, \(x_i\) cannot be on the path, or in which case the condition is always true. Such singleton statement can be created in the following way.
# Q is not exported# x1 must be on the pathx1 = TransmissionChannelAnalysis.Q(1)# x1 cannot be on the pathnot_x2 = TransmissionChannelAnalysis.Q("!x2")# TRUET = TransmissionChannelAnalysis.Q("")
More complex transmission conditions are then simply a combination of these singleton statements. More precisely, a transmission condition q1::Q can be combined with another transmission condition q2::Q using any of AND (&), OR (|) and NOT (!). Throughout, parentheses can be used to change the precedence. In the following we will describe how such combinations are internally implemented.
Combining two conditions using AND (&)
Suppose we wanted to combine two conditions q1::Q and q2::Q using an AND. Thus, suppose we wanted to do
q1 & q2
This is achieved by extending Base.&. However, key in the internal implementation is the following assumption.
Note 1: Assumption 1
The transmission condition q::Q consists of only conjunctions (&) and negations (!).
Under Assumption 1q1::Q and q2::Q both consists of only conjunctions and disjunctions. This immediately implies that their conjunction will also only consist of conjunctions and negations. We can thus simply “concatenate” the two conditions. This is basically what we internally do, with the caveat that the conditions are represented using String and that, for algorithimic purposes, we prefer the variables to be ordered in the statement.
The detailed internal implementation is given by
&
Base.&(q1::Q, q2::Q)
Combine two transmission conditions using AND.
string_and
string_and(s1::String, s2::String)
Combine two strings using “&”.
Makes sure that only unique variables occur. Also sorts the string such that negated variables only come after all non-negated variables, and higher-variables come before lower variables.
While concatenating the two conditions, we need to watch out that no contradictions are being introduced. Contradictions are any statements along the lines \(x_i \land \neg x_i\) which would always result in false. This is taken care of by the following functions.
Check whether there is a contradiction of the form x1 & !x1.
Arguments
var_and::Union{Vector{Int}, Vector{Vector{Int}}}: AND variable numbers obtained from get_varnums_and_multiplier.
var_not::Union{Vector{Int}, Vector{Vector{Int}}}: NOT variable numbers obtained from get_varnums_and_multiplier
Returns
Bool indicating whether there are any contradictions.
Vector{Bool} indicating which elements yielded a contradiction.
Notes
This is used in remove_contradictions to remove contradicting terms. This speeds up simplification of terms, since the total number of terms can often be reduced.
remove_contradictions
remove_contradictions(q::Q)
Remove contradicting terms.
A terms is deemed contradicting if it includes some “xi & !xi”. This would result in the entire Boolean statement to be false, and thus in the effect of this terms to be zero.
Arguments
q::Q: A transmission condition. See also Q and make_condition.
Returns
If TransmissionChannelAnalysis.REMOVE_CONTRADICTIONS[] == false, then q will simply be returned again.
If TransmissionChannelAnalysis.REMOVE_CONTRADICTIONS[] == true, then
If all terms are contradicting, then Q("", 0) will be retuned, which has a transmission effect of zero.
If some terms are non-contradicting, then a transmission condition consisting of only the non-contradicting terms will be returned.
Exa```{julia}es
TransmissionChannelAnalysis.REMOVE_CONTRADICTIONS[] =trueq = TransmissionChannelAnalysis.Q("x1", 1)remove_contradictions(q) # will return q again since no contradictions existq = TransmissionChannelAnalysis.Q("x1 & !x1", 1)remove_contradictions(q) # Will return Q("", 0)q = TransmissionChannelAnalysis.Q(["x1 & !x1", "x1 & x2"], [1, 1])remove_contradictions(q) # Will return Q("x1 & x2", 1)
As stated in the overview of the documentation, removal of contradictions is governed by REMOVE_CONTRADICTION and the utility function set_remove_contradictions. Not removing contradictions does not lead to a mistakes in the computation, but can result in longer computations. Contrary, removing contradictions can lead to longer compile times – it takes longer to compile the transmission conditions. Thus, a trade-off between compile and computing time exists.
Combining two conditions using OR (|)
Let q1::Q and q2::Q be again two transmission conditions. Suppose their internal Boolean conditions are given by \(b\) and \(b'\) respectively, with both satisfying Assumption 1. The rules for manipulating transmission conditions then imply \[
Q(b \lor b') = Q(b) + Q(b') - Q(b \land b').
\] Implementation of OR (|) therefore simply uses AND and the ability for Q to represent multiple terms with different multipliers. The precise implementation of OR can be found in the following function.
|
Base.|(q1::Q, q2::Q)
Combine two transmission conditions using OR.
Negating a condition using NOT (!)
Suppose a transmission condition is given by q::Q which internally represents the Boolean condition \(b\). By the rules for manipulating transmission conditions we then know that \[
Q(\neg b) = Q(T) - Q(b),
\] where \(T\) represents the Boolean condition that is always true. This can easily be represented using Q through its ability to represent multiple terms and multipliers. Specifically, we simply replace the original transmission condition with a new one that consists one one additional term, representing true, and switch the sign of all original terms of the condition. The following function implements this behaviour.
!
Base.!(q1::Q)
Return NOT the transmission condition if the condition involves more than one variables. If the condition only involves one variables, then “!x1” is returned where “1” is replaced by the respective variable number.
Note: The decision not to simplify terms of the form “!x1” was made because calculations usign the second calculation method in Wegner et al (2024) are faster than having to simplify “!x1” type of terms and using the first calculation method.
Parsing transmission conditions using make_condition
Rather than letting the user create all variables manually via x1 =Q(1) etc., we provide the function make_condition. This function simply matches all variables in a string with the regex x\d+, creates teh variables using the above way, and then evaluates the entire condition to create the final condition. Due to the operator overloading above, the final result will be a valid and correct transmission condtion. However, key to this correctness is that all variables math the provided regex pattern. Thus, all variables must be of the form x1, x2, etc.
For user friendliness, we also provide a version of make_condition that takes variables of the dynamic form, i.e. \(y_{i,t}\). However, all that this function does is to translate all variables into variables of the systems form and then calls the method explained above.
In any case, make_conditions should be seen as a utility function with the main funcionality being dependent on correct implementation of Q, &, |, and ! as explained above.
Make a transmission condition, i.e. Q(b), out of a string.
Transmission channels are described using Boolean statements involving the variables in the dynamic model. make_condition allows for specifying these Boolean conditions as a string which is then converted to an internal representation allowing the computation of transmission channels.
Two ways of specifying the Boolean conditions exist:
make_condition(s::String) takes the Boolean condition in the systems form of Wegner et al (2024), i.e. variables must start with x followed by a number. For example, given a three variable VAR(1), y_{1,t} -> x_1, y_{2, t} -> x_2, y_{3, t} -> x_3, y_{1, t+1} -> x_4, y_{2, t+1} -> x_5, … Boolean statements then involve expressions in the x variables and define which paths can be taken. Each path involved in the transmission mechanism must satisfy the Boolean statement.
make_condition(s_y::String, order::AbstractVector{<:Int}) does the same as the first method, however the Boolean condition can be specified using the variables of the dynamic systems, i.e. y. Variables must then be specified using y_{i,t} where i is the variable number and t is the period. At all times t >= 0 with 0 denoting the contemporaneous horizon.
Arguments
s::String: A Boolean statement given as a string. Variables must start with x for them to be valid variables.
s_y::String: A Boolean statement given as a string. Variabls must have the form y_{i,t} where i is the variable number and t >= 0 is the time period. t=0 corresponds to the contemporaneous horizon.
order::AbstractVector{<:Int}: The variable ordering defined by the transmission matrix.