2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
isempty(operations::PossibleOperations)
|
2023-08-21 12:54:45 +02:00
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
Return whether `operations` is empty, i.e. all of its fields are empty.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function isempty(operations::PossibleOperations)
|
2023-10-12 17:51:03 +02:00
|
|
|
return isempty(operations.nodeFusions) && isempty(operations.nodeReductions) && isempty(operations.nodeSplits)
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
length(operations::PossibleOperations)
|
|
|
|
|
|
|
|
Return a named tuple with the number of each of the operation types as a named tuple. The fields are named the same as the [`PossibleOperations`](@ref)'.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function length(operations::PossibleOperations)
|
2023-08-25 10:48:22 +02:00
|
|
|
return (
|
|
|
|
nodeFusions = length(operations.nodeFusions),
|
|
|
|
nodeReductions = length(operations.nodeReductions),
|
|
|
|
nodeSplits = length(operations.nodeSplits),
|
|
|
|
)
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
delete!(operations::PossibleOperations, op::NodeFusion)
|
|
|
|
|
|
|
|
Delete the given node fusion from the possible operations.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function delete!(operations::PossibleOperations, op::NodeFusion)
|
2023-08-25 10:48:22 +02:00
|
|
|
delete!(operations.nodeFusions, op)
|
|
|
|
return operations
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
delete!(operations::PossibleOperations, op::NodeReduction)
|
|
|
|
|
|
|
|
Delete the given node reduction from the possible operations.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function delete!(operations::PossibleOperations, op::NodeReduction)
|
2023-08-25 10:48:22 +02:00
|
|
|
delete!(operations.nodeReductions, op)
|
|
|
|
return operations
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
delete!(operations::PossibleOperations, op::NodeSplit)
|
|
|
|
|
|
|
|
Delete the given node split from the possible operations.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function delete!(operations::PossibleOperations, op::NodeSplit)
|
2023-08-25 10:48:22 +02:00
|
|
|
delete!(operations.nodeSplits, op)
|
|
|
|
return operations
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
can_fuse(n1::ComputeTaskNode, n2::DataTaskNode, n3::ComputeTaskNode)
|
2023-08-21 12:54:45 +02:00
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
Return whether the given nodes can be fused. See [`NodeFusion`](@ref) for the requirements.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function can_fuse(n1::ComputeTaskNode, n2::DataTaskNode, n3::ComputeTaskNode)
|
2023-08-25 10:48:22 +02:00
|
|
|
if !is_child(n1, n2) || !is_child(n2, n3)
|
|
|
|
# the checks are redundant but maybe a good sanity check
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2023-11-22 13:51:54 +01:00
|
|
|
if length(parents(n2)) != 1 || length(children(n2)) != 1 || length(parents(n1)) != 1
|
2023-08-25 10:48:22 +02:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
can_reduce(n1::Node, n2::Node)
|
|
|
|
|
|
|
|
Return whether the given two nodes can be reduced. See [`NodeReduction`](@ref) for the requirements.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function can_reduce(n1::Node, n2::Node)
|
2023-11-22 13:51:54 +01:00
|
|
|
return false
|
|
|
|
end
|
2023-08-25 10:48:22 +02:00
|
|
|
|
2023-11-22 13:51:54 +01:00
|
|
|
function can_reduce(
|
|
|
|
n1::NodeType,
|
|
|
|
n2::NodeType,
|
|
|
|
) where {TaskType <: AbstractTask, NodeType <: Union{DataTaskNode{TaskType}, ComputeTaskNode{TaskType}}}
|
|
|
|
n1_length = length(children(n1))
|
|
|
|
n2_length = length(children(n2))
|
2023-08-25 10:48:22 +02:00
|
|
|
|
|
|
|
if (n1_length != n2_length)
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
# this seems to be the most common case so do this first
|
|
|
|
# doing it manually is a lot faster than using the sets for a general solution
|
|
|
|
if (n1_length == 2)
|
2023-11-22 13:51:54 +01:00
|
|
|
if (children(n1)[1] != children(n2)[1])
|
|
|
|
if (children(n1)[1] != children(n2)[2])
|
2023-08-25 10:48:22 +02:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
# 1_1 == 2_2
|
2023-11-22 13:51:54 +01:00
|
|
|
if (children(n1)[2] != children(n2)[1])
|
2023-08-25 10:48:22 +02:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
# 1_1 == 2_1
|
2023-11-22 13:51:54 +01:00
|
|
|
if (children(n1)[2] != children(n2)[2])
|
2023-08-21 12:54:45 +02:00
|
|
|
return false
|
2023-08-25 10:48:22 +02:00
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
# this is simple
|
|
|
|
if (n1_length == 1)
|
2023-11-22 13:51:54 +01:00
|
|
|
return children(n1)[1] == children(n2)[1]
|
2023-08-25 10:48:22 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
# this takes a long time
|
2023-11-22 13:51:54 +01:00
|
|
|
return Set(children(n1)) == Set(children(n2))
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
can_split(n1::Node)
|
|
|
|
|
|
|
|
Return whether the given node can be split. See [`NodeSplit`](@ref) for the requirements.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function can_split(n::Node)
|
2023-08-25 10:48:22 +02:00
|
|
|
return length(parents(n)) > 1
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
==(op1::Operation, op2::Operation)
|
|
|
|
|
|
|
|
Fallback implementation of operation equality. Return false. Actual comparisons are done by the overloads of same type operation comparisons.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function ==(op1::Operation, op2::Operation)
|
2023-08-25 10:48:22 +02:00
|
|
|
return false
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
==(op1::NodeFusion, op2::NodeFusion)
|
|
|
|
|
|
|
|
Equality comparison between two node fusions. Two node fusions are considered equal if they have the same inputs.
|
|
|
|
"""
|
2023-11-22 13:51:54 +01:00
|
|
|
function ==(
|
|
|
|
op1::NodeFusion{ComputeTaskType1, DataTaskType, ComputeTaskType2},
|
|
|
|
op2::NodeFusion{ComputeTaskType1, DataTaskType, ComputeTaskType2},
|
|
|
|
) where {
|
|
|
|
ComputeTaskType1 <: AbstractComputeTask,
|
|
|
|
DataTaskType <: AbstractDataTask,
|
|
|
|
ComputeTaskType2 <: AbstractComputeTask,
|
|
|
|
}
|
2023-08-25 10:48:22 +02:00
|
|
|
# there can only be one node fusion on a given data task, so if the data task is the same, the fusion is the same
|
|
|
|
return op1.input[2] == op2.input[2]
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
==(op1::NodeReduction, op2::NodeReduction)
|
|
|
|
|
|
|
|
Equality comparison between two node reductions. Two node reductions are considered equal when they have the same inputs.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function ==(op1::NodeReduction, op2::NodeReduction)
|
2023-08-25 10:48:22 +02:00
|
|
|
# node reductions are equal exactly if their first input is the same
|
|
|
|
return op1.input[1].id == op2.input[1].id
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|
|
|
|
|
2023-08-29 12:57:46 +02:00
|
|
|
"""
|
|
|
|
==(op1::NodeSplit, op2::NodeSplit)
|
|
|
|
|
|
|
|
Equality comparison between two node splits. Two node splits are considered equal if they have the same input node.
|
|
|
|
"""
|
2023-08-21 12:54:45 +02:00
|
|
|
function ==(op1::NodeSplit, op2::NodeSplit)
|
2023-08-25 10:48:22 +02:00
|
|
|
return op1.input == op2.input
|
2023-08-21 12:54:45 +02:00
|
|
|
end
|