Skip to content

Commit

Permalink
Merge pull request YangLab-um#22 from ftavella/update-network-utilities
Browse files Browse the repository at this point in the history
Update network utilities and input handling - New functionality
  • Loading branch information
biphy authored Sep 15, 2023
2 parents 76d968d + 3476019 commit 98d096f
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 6 deletions.
7 changes: 6 additions & 1 deletion src/BiologicalOscillations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ export pin_hit_rate
export gene_regulatory_network, grn_parameters, grn_timescale, grn_parameter_sets
export grn_equilibration_times, find_grn_oscillations
# Network utilities
export network_permutations, is_same_network, is_directed_cycle_graph, is_same_set_of_networks, all_network_additions, unique_network_additions, unique_negative_feedback_networks, unique_cycle_addition
export network_permutations, is_same_network, all_network_additions, unique_network_additions
export is_directed_cycle_graph, is_same_set_of_networks, unique_cycle_addition
export unique_negative_feedback_networks, count_inputs_by_coherence, is_negative_feedback_network
export connectivity_to_binary, find_all_binary_circular_permutations, binary_to_connectivity
export calculate_node_coherence
# User input handling
export is_valid_connectivity, connectivity_string_to_matrix
# Default hyperparameters
export DEFAULT_PIN_HYPERPARAMETERS, DEFAULT_PIN_PARAMETER_LIMITS, DEFAULT_PIN_SAMPLING_SCALES
export DEFAULT_GRN_HYPERPARAMETERS, DEFAULT_GRN_PARAMETER_LIMITS, DEFAULT_GRN_SAMPLING_SCALES
Expand Down
90 changes: 88 additions & 2 deletions src/network_utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ end
- `result::Bool`: True if any of the permutations of connectivity 1 is equal to any of the permutations of connectivity2. False otherwise.
"""
function is_same_network(connectivity1::AbstractMatrix, connectivity2::AbstractMatrix)
if connectivity1 == connectivity2
return true
end
# If the sum of edges is different, then the networks are different
if sum(connectivity1) != sum(connectivity2)
return false
end
permutations1 = network_permutations(connectivity1)
permutations2 = network_permutations(connectivity2)
# Compare permutations
Expand Down Expand Up @@ -165,7 +172,8 @@ function unique_network_additions(connectivity::AbstractMatrix, number_of_edges:
all_additions = all_network_additions(connectivity, number_of_edges)
# Remove duplicates
for addition in all_additions
if !any(is_same_network(addition, c) for c in connectivity_vector)
comparison = [is_same_network(addition, c) for c in connectivity_vector]
if !any(comparison)
push!(connectivity_vector, addition)
end
end
Expand Down Expand Up @@ -227,6 +235,33 @@ end


"""
count_inputs_by_coherence(connectivity::AbstractMatrix)
Returns the total number of coherent and incoherent inputs for a given network.
# Arguments (Required)
- `connectivity::AbstractMatrix`: Connectivity matrix of a network
# Returns
- `input_counts::DataFrame`: DataFrame containing the number of coherent and incoherent inputs for a given network
"""
function count_inputs_by_coherence(connectivity::AbstractMatrix)
# Initialize dataframe
input_counts = DataFrame(coherent = 0, incoherent = 0)
# Go through each row
for row in eachrow(connectivity)
node_coherence = calculate_node_coherence(row)
if node_coherence == "coherent"
input_counts.coherent .+= 1
elseif node_coherence == "incoherent"
input_counts.incoherent .+= 1
end
end
return input_counts
end


"""
connectivity_to_binary(connectivity::AbstractMatrix)
Returns a binary representation of a circular network.
Expand Down Expand Up @@ -273,6 +308,30 @@ end


"""
is_negative_feedback_network(connectivity::AbstractMatrix)
Returns true if the network is a negative-feedback-only network. False otherwise.
# Arguments (Required)
- `connectivity::AbstractMatrix`: Connectivity matrix of a network
# Returns
- `result::Bool`: True if the network is a negative-feedback-only network. False otherwise.
"""
function is_negative_feedback_network(connectivity::AbstractMatrix)
non_zero_edges = connectivity[connectivity .!= 0]
# If the number of edges is larger than the number of nodes, then it is not a negative-feedback-only network
if sum(abs.(non_zero_edges)) > size(connectivity, 1)
return false
# If the product of the edges is non-negative, then it is not a negative-feedback-only network
elseif prod(non_zero_edges) >= 0
return false
else
return true
end
end

"""
find_all_binary_circular_permutations(feedback_loop_in_binary::String)
Returns a vector containing all unique circular permutations of a given binary representation.
Expand Down Expand Up @@ -312,6 +371,33 @@ function binary_to_connectivity(feedback_loop_in_binary::String)
end


"""
calculate_node_coherence(node_inputs::AbstractVector)
Returns the coherence of a node given its inputs. A coherent node has all inputs with the same sign. An incoherent node has at least one input with a different sign.
# Arguments (Required)
- `node_inputs::AbstractVector`: Vector containing the inputs of a node
# Returns
- `coherence::String`: Coherence of a node. Either "coherent", "incoherent" or "unknown"
"""
function calculate_node_coherence(node_inputs::AbstractArray)
coherence = "unknown"

n_positive = sum(node_inputs .== 1)
n_negative = sum(node_inputs .== -1)

if n_positive == 0 && n_negative > 1 || n_positive > 1 && n_negative == 0
coherence = "coherent"
elseif n_positive > 0 && n_negative > 0
coherence = "incoherent"
end

return coherence
end


"""
unique_cycle_addition(connectivity::AbstractMatrix)
Expand Down Expand Up @@ -344,4 +430,4 @@ function unique_cycle_addition(connectivity::AbstractMatrix)
end

return connectivity_vector
end
end
23 changes: 22 additions & 1 deletion src/user_input_handling.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Check if connectivity is a 2-dimensional square matrix filled with 0, 1, and -1 values.
# Arguments
# Arguments (Required)
- `connectivity::AbstractMatrix`: A matrix.
# Returns
Expand All @@ -25,4 +25,25 @@ function is_valid_connectivity(connectivity::AbstractMatrix)
result = true
end
return [result, errmsg]
end


"""
connectivity_string_to_matrix(connectivity_string::AbstractString)
Convert connectivity string to a 2-dimensional square matrix filled with 0, 1, and -1 values.
# Arguments (Required)
- `connectivity_string::AbstractString`: A string representing a connectivity matrix.
# Returns
- `AbstractMatrix`: A 2-dimensional square matrix filled with 0, 1, and -1 values.
"""
function connectivity_string_to_matrix(connectivity_string::AbstractString)
connectivity_values = replace(connectivity_string, ";" => "", "[" => "", "]" => "")
connectivity_array = [parse(Int, i) for i in split(connectivity_values)]
nodes = convert(Int64, sqrt(length(connectivity_array)))
connectivity_array = reshape(connectivity_array, nodes, nodes)
connectivity_array = connectivity_array'
return connectivity_array
end
110 changes: 108 additions & 2 deletions test/network_utilities_tests.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
using BiologicalOscillations

# Test that the correct network permutations are generated
# Test `network_permutations`
## 2 nodes
true_permutations = [
[0 1; 2 0],
[0 2; 1 0],
]
calculated_permutations = network_permutations([0 1; 2 0])
@test true_permutations == calculated_permutations
## 3 nodes
true_permutations = [
[0 0 1; 2 0 0; 0 3 0],
[0 1 0; 0 0 3; 2 0 0],
Expand Down Expand Up @@ -105,6 +113,104 @@ for network in true_unique_6_nodes
@test any([is_same_network(network, calculated_network) for calculated_network in calculated_unique_6_nodes])
end

# Test the count_inputs_by_coherence function
connectivity = [0 0 0 0 0 -1; 1 0 0 0 0 0; 0 -1 0 0 0 0; 0 0 -1 0 0 0; 0 0 0 -1 0 0; 0 0 0 0 -1 0]
input_counts = count_inputs_by_coherence(connectivity)
@test input_counts.coherent[1] == 0
@test input_counts.incoherent[1] == 0

connectivity = [0 1 1;1 0 0;0 -1 1]
input_counts = count_inputs_by_coherence(connectivity)
@test input_counts.coherent[1] == 1
@test input_counts.incoherent[1] == 1

connectivity = [-1 1 1;1 1 1;-1 1 1]
input_counts = count_inputs_by_coherence(connectivity)
@test input_counts.coherent[1] == 1
@test input_counts.incoherent[1] == 2

# Test is_negative_feedback_network
nf_networks = [
[0 0 -1; -1 0 0; 0 -1 0],
[0 0 1; 1 0 0; 0 -1 0],
[0 0 0 -1; 1 0 0 0; 0 -1 0 0; 0 0 -1 0],
[0 0 0 -1; 1 0 0 0; 0 1 0 0; 0 0 1 0],
[0 0 0 0 -1; -1 0 0 0 0; 0 -1 0 0 0; 0 0 -1 0 0; 0 0 0 -1 0],
[0 0 0 0 -1; 1 0 0 0 0; 0 1 0 0 0; 0 0 -1 0 0; 0 0 0 -1 0],
[0 0 0 0 -1; 1 0 0 0 0; 0 -1 0 0 0; 0 0 1 0 0; 0 0 0 -1 0],
[0 0 0 0 -1; 1 0 0 0 0; 0 1 0 0 0; 0 0 1 0 0; 0 0 0 1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 -1 0 0 0 0; 0 0 -1 0 0 0; 0 0 0 -1 0 0; 0 0 0 0 -1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 1 0 0 0 0; 0 0 1 0 0 0; 0 0 0 -1 0 0; 0 0 0 0 -1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 1 0 0 0 0; 0 0 -1 0 0 0; 0 0 0 1 0 0; 0 0 0 0 -1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 1 0 0 0 0; 0 0 -1 0 0 0; 0 0 0 -1 0 0; 0 0 0 0 1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 -1 0 0 0 0; 0 0 1 0 0 0; 0 0 0 -1 0 0; 0 0 0 0 1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 1 0 0 0 0; 0 0 1 0 0 0; 0 0 0 1 0 0; 0 0 0 0 1 0],
[0 0 0 0 0 0 -1; -1 0 0 0 0 0 0; 0 -1 0 0 0 0 0; 0 0 -1 0 0 0 0; 0 0 0 -1 0 0 0; 0 0 0 0 -1 0 0; 0 0 0 0 0 -1 0],
[0 0 0 0 0 0 -1; 1 0 0 0 0 0 0; 0 1 0 0 0 0 0; 0 0 -1 0 0 0 0; 0 0 0 -1 0 0 0; 0 0 0 0 -1 0 0; 0 0 0 0 0 -1 0],
[0 0 0 0 0 0 -1; 1 0 0 0 0 0 0; 0 -1 0 0 0 0 0; 0 0 1 0 0 0 0; 0 0 0 -1 0 0 0; 0 0 0 0 -1 0 0; 0 0 0 0 0 -1 0],
[0 0 0 0 0 0 -1; 1 0 0 0 0 0 0; 0 -1 0 0 0 0 0; 0 0 -1 0 0 0 0; 0 0 0 1 0 0 0; 0 0 0 0 -1 0 0; 0 0 0 0 0 -1 0],
[0 0 0 0 0 0 -1; 1 0 0 0 0 0 0; 0 1 0 0 0 0 0; 0 0 1 0 0 0 0; 0 0 0 1 0 0 0; 0 0 0 0 -1 0 0; 0 0 0 0 0 -1 0],
[0 0 0 0 0 0 -1; 1 0 0 0 0 0 0; 0 -1 0 0 0 0 0; 0 0 1 0 0 0 0; 0 0 0 1 0 0 0; 0 0 0 0 1 0 0; 0 0 0 0 0 -1 0],
[0 0 0 0 0 0 -1; 1 0 0 0 0 0 0; 0 -1 0 0 0 0 0; 0 0 -1 0 0 0 0; 0 0 0 1 0 0 0; 0 0 0 0 1 0 0; 0 0 0 0 0 1 0],
[0 0 0 0 0 0 -1; 1 0 0 0 0 0 0; 0 1 0 0 0 0 0; 0 0 -1 0 0 0 0; 0 0 0 1 0 0 0; 0 0 0 0 1 0 0; 0 0 0 0 0 -1 0],
[0 0 0 0 0 0 -1; 1 0 0 0 0 0 0; 0 1 0 0 0 0 0; 0 0 -1 0 0 0 0; 0 0 0 1 0 0 0; 0 0 0 0 -1 0 0; 0 0 0 0 0 1 0],
[0 0 0 0 0 0 -1; 1 0 0 0 0 0 0; 0 1 0 0 0 0 0; 0 0 1 0 0 0 0; 0 0 0 1 0 0 0; 0 0 0 0 1 0 0; 0 0 0 0 0 1 0],
]

non_nf_networks = [
[0 0 1; -1 0 0; 0 -1 0],
[0 0 1; 1 0 0; 0 1 0],
[0 0 0 -1; 1 0 0 1; 0 -1 0 0; 0 0 -1 0],
[0 0 0 -1; 1 0 1 0; 1 1 0 0; 0 0 1 0],
[0 0 0 0 -1; -1 0 0 0 0; 0 0 0 0 0; 0 0 -1 0 0; 0 0 0 -1 0],
[0 0 0 0 -1; 1 0 0 0 0; 0 -1 0 0 0; 0 0 -1 0 0; 0 0 0 -1 0],
[0 0 0 0 -1; 1 0 0 0 0; 0 1 0 0 0; 0 0 1 0 0; 0 0 0 -1 0],
[0 0 0 0 -1; 1 0 0 0 0; 0 -1 0 0 0; 0 0 1 0 0; 0 0 0 1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 1 0 0 0 0; 0 0 -1 0 0 0; 0 0 0 -1 0 0; 0 0 0 0 -1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 -1 0 0 0 0; 0 0 1 0 0 0; 0 0 0 -1 0 0; 0 0 0 0 -1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 1 0 1 0 0; 0 0 -1 0 0 0; 0 0 0 1 0 0; 0 0 0 0 -1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 1 0 0 1 0; 0 0 -1 0 0 0; 0 0 0 -1 0 0; 0 0 0 0 1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 -1 0 0 -1 0; 0 0 1 0 0 0; 0 0 0 -1 0 0; 0 0 0 0 1 0],
[0 0 0 0 0 -1; 1 0 0 0 0 0; 0 -1 0 0 0 0; 0 0 1 0 0 0; 0 0 0 1 0 0; 0 0 0 0 1 0],
]

for network in nf_networks
@test is_negative_feedback_network(network) == true
end

for network in non_nf_networks
@test is_negative_feedback_network(network) == false
end

# Test node_coherence
node_inputs = [0 0 0 0 0 0]
node_coherence = calculate_node_coherence(node_inputs)
@test node_coherence == "unknown"

node_inputs = [0 0 1 -1 0 0]
node_coherence = calculate_node_coherence(node_inputs)
@test node_coherence == "incoherent"

node_inputs = [0 0 1 -1 0 1]
node_coherence = calculate_node_coherence(node_inputs)
@test node_coherence == "incoherent"

node_inputs = [1 0 1 -1 0 1]
node_coherence = calculate_node_coherence(node_inputs)
@test node_coherence == "incoherent"

node_inputs = [1 0 1 1 0 1]
node_coherence = calculate_node_coherence(node_inputs)
@test node_coherence == "coherent"

node_inputs = [1 -1 1 1 -1 1]
node_coherence = calculate_node_coherence(node_inputs)
@test node_coherence == "incoherent"

node_inputs = [0 0 1 0 0 0]
node_coherence = calculate_node_coherence(node_inputs)
@test node_coherence == "unknown"

# Test is_directed_cycle_graph function
@test is_directed_cycle_graph([0 1 0; 0 0 1; 1 0 0]) # forward cycle
@test is_directed_cycle_graph([0 0 1; 1 0 0; 0 1 0]) # backward cycle
Expand Down Expand Up @@ -168,4 +274,4 @@ p2 = [0 0 0 0 -1; -1 0 0 0 0; 0 -1 0 0 0; 0 0 1 0 0; 0 0 0 1 0]
@test is_same_set_of_networks(unique_network_additions(t2, 1), unique_cycle_addition(t2))
@test is_same_set_of_networks(unique_network_additions(s1, 1), unique_cycle_addition(s1))
@test is_same_set_of_networks(unique_network_additions(s3, 1), unique_cycle_addition(s3))
@test is_same_set_of_networks(unique_network_additions(p2, 1), unique_cycle_addition(p2))
@test is_same_set_of_networks(unique_network_additions(p2, 1), unique_cycle_addition(p2))
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ using Test

@testset "Protein Interaction Networks" begin include("protein_interaction_network/pin_tests.jl") end
@testset "Gene Regulatory Networks" begin include("gene_regulatory_network/grn_tests.jl") end
@testset "User Input Handling" begin include("user_input_handling_tests.jl") end
@testset "Feature Calculation" begin include("feature_calculation_tests.jl") end
@testset "Network Utilities" begin include("network_utilities_tests.jl") end
@testset "Model Simulation" begin include("simulation_tests.jl") end
Expand Down
18 changes: 18 additions & 0 deletions test/user_input_handling_tests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using BiologicalOscillations

# Test is_valid_connectivity
valid_connectivity = [0 0 1; 1 0 0; 0 -1 0]
result, errmsg = is_valid_connectivity(valid_connectivity)
@test result == true
@test errmsg == ""

invalid_connectivity = [0 0 1; 1 0 0; 0 -1 2]
result, errmsg = is_valid_connectivity(invalid_connectivity)
@test result == false
@test errmsg == "Only -1, 0, and 1 are allowed as connectivity values"

# Test connectivity_string_to_matrix
connectivity_string = "[0 0 1; 1 0 0; 0 -1 0]"
valid_connectivity = [0 0 1; 1 0 0; 0 -1 0]
connectivity_matrix = connectivity_string_to_matrix(connectivity_string)
@test connectivity_matrix == valid_connectivity

0 comments on commit 98d096f

Please sign in to comment.