Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions LocalPreferences.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[GraphDynamicalSystems]
dispatch_doctor_mode = "debug"
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "GraphDynamicalSystems"
uuid = "13529e2e-ed53-56b1-bd6f-420b01fca819"
authors = ["Reuben Gardos Reid <[email protected]>"]
version = "0.0.1"
version = "0.0.2"

[deps]
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
Expand Down
112 changes: 90 additions & 22 deletions src/qualitative_networks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import SciMLBase

using AbstractTrees: Leaves
using DynamicalSystemsBase: ArbitrarySteppable, current_parameters, initial_state
using HerbConstraints: DomainRuleNode, Forbidden, Ordered, VarNode, addconstraint!
using HerbConstraints: DomainRuleNode, Forbidden, Ordered, Unique, VarNode, addconstraint!
using HerbCore: AbstractGrammar, RuleNode, get_rule
using HerbGrammar: @csgrammar, add_rule!, rulenode2expr
using HerbSearch: rand
Expand All @@ -22,24 +22,34 @@ const base_qn_grammar = @csgrammar begin
Val = Floor(Val)
end

const default_qn_constants = [2]
const default_qn_constants = [0, 1, 2]

"""
$(TYPEDSIGNATURES)

Builds a grammar based on the base QN grammar adding `entity_names` and `constants`
to the grammar.

Four constraints are currently included
The following constraints are currently included

1. removing symmetry due to commutativity of `+`/`*`/`min`/`max`
2. forbidding same arguments of two argument functions
3. forbidding trivial inputs (consts and entity values) to `floor`/`ceil`
4. forbidding `ceil(floor(_))` and `floor(ceil(_))`
3. forbidding constant arguments to 2-argument functions
4. forbidding constant arguments to 1-argument functions
5. using each of the entities only once per function
6. forbidding adding or subtracting zero
7. forbidding multiplication and division by 1 or 0
8. forcing the first operator inside `ceil` and `floor` to be `÷`
9. forbidding `max(□, X)` and `min(□, X)` where X is either the max or min
constant in the grammar.

"""
function build_qn_grammar(entity_names, constants = default_qn_constants)
g = deepcopy(GraphDynamicalSystems.base_qn_grammar)
function build_qn_grammar(
entity_names,
constants = default_qn_constants;
unique_constr = true,
)
g = deepcopy(base_qn_grammar)

for e in entity_names
add_rule!(g, :(Val = $e))
Expand All @@ -57,40 +67,98 @@ function build_qn_grammar(entity_names, constants = default_qn_constants)
template_tree = DomainRuleNode(domain, [VarNode(:a), VarNode(:b)])
order = [:a, :b]

addconstraint!(g, Ordered(template_tree, order))
addconstraint!(g, Ordered(deepcopy(template_tree), order))

# Forbid same arguments for 2-argument functions
domain = BitVector(zeros(length(g.rules)))
@. domain[length(g.childtypes)==2] = true
template_tree = DomainRuleNode(domain, [VarNode(:a), VarNode(:a)])

addconstraint!(g, Forbidden(template_tree))
addconstraint!(g, Forbidden(deepcopy(template_tree)))

# Forbid Ceil and Floor from including an entity or constant directly
domain = BitVector(zeros(length(g.rules)))
n_original_rules = length(GraphDynamicalSystems.base_qn_grammar.rules)
domain[[n_original_rules+1:length(g.rules)...]] .= true
# Forbid constant arguments for 2-argument functions
domain = falses(length(g.rules))
@. domain[length(g.childtypes)==2] = true
consts_domain = falses(length(g.rules))
consts_domain[findall(x -> x isa Int, g.rules)] .= true
consts_domain_rn = DomainRuleNode(consts_domain)
template_tree = DomainRuleNode(domain, [consts_domain_rn, consts_domain_rn])

entities_consts = DomainRuleNode(domain)
addconstraint!(g, Forbidden(deepcopy(template_tree)))

domain = BitVector(zeros(length(g.rules)))
domain[[7, 8]] .= true
# Forbid constant arguments for 1-argument functions
domain = falses(length(g.rules))
@. domain[[7, 8]] = true
consts_domain = falses(length(g.rules))
consts_domain[findall(x -> x isa Int, g.rules)] .= true
consts_domain_rn = DomainRuleNode(consts_domain)
template_tree = DomainRuleNode(domain, [consts_domain_rn])

addconstraint!(g, Forbidden(deepcopy(template_tree)))

n_original_rules = length(base_qn_grammar.rules)

# Only use each of the entities once per function
n_consts = length(constants)
entities = n_original_rules+1:length(g.rules)-n_consts

if unique_constr
addconstraint!.((g,), Unique.(entities))
end

# Forbid □ + 0, □ - 0
plus_or_minus = falses(length(g.rules))
plus_or_minus[[1, 2]] .= true
zero_rule = findfirst(==(0), g.rules)
if !isnothing(zero_rule)
template_tree = DomainRuleNode(plus_or_minus, [VarNode(:a), RuleNode(zero_rule)])

template_tree = DomainRuleNode(domain, [entities_consts])
addconstraint!(g, Forbidden(deepcopy(template_tree)))

addconstraint!(g, Forbidden(template_tree))
# Both orderings, but only for plus. Allow 0 - □
plus_or_minus[2] = false
template_tree = DomainRuleNode(plus_or_minus, [RuleNode(zero_rule), VarNode(:a)])
addconstraint!(g, Forbidden(deepcopy(template_tree)))
end

# Forbid □ * 1, □ / 1, □ * 0, □ / 0
mult_or_div = falses(length(g.rules))
mult_or_div[[3, 4]] .= true
one_zero_domain = falses(length(g.rules))
one_zero_domain[findfirst(==(1), g.rules)] = true
if !isnothing(findfirst(==(0), g.rules))
one_zero_domain[findfirst(==(0), g.rules)] = true
end

template_tree =
DomainRuleNode(mult_or_div, [VarNode(:a), DomainRuleNode(one_zero_domain)])

# Forbid ceil(floor(x)) and vice-versa
addconstraint!(g, Forbidden(deepcopy(template_tree)))

# Forbid ceil(X) and floor(X) unless X = □ ÷ □
ceil_or_floor = BitVector(zeros(length(g.rules)))
ceil_or_floor[[7, 8]] .= true
template_tree =
DomainRuleNode(ceil_or_floor, [DomainRuleNode(ceil_or_floor, [VarNode(:a)])])
all_except_div = trues(length(g.rules))
all_except_div[3] = false
template_tree = DomainRuleNode(ceil_or_floor, [DomainRuleNode(all_except_div)])

addconstraint!(g, Forbidden(deepcopy(template_tree)))

addconstraint!(g, Forbidden(template_tree))
# Forbid max(□, X) and min(□, X) where X is either the largest or smallest constant in the grammar
min_max_rules = falses(length(g.rules))
min_max_rules[[5, 6]] .= true
(min_const, max_const) = extrema(filter(x -> isa(x, Int), g.rules))
extrema_domain = falses(length(g.rules))
extrema_domain[findall(x -> x == min_const || x == max_const, g.rules)] .= true
rule_extrema_consts = DomainRuleNode(extrema_domain)
template_tree = DomainRuleNode(min_max_rules, [VarNode(:a), rule_extrema_consts])

addconstraint!(g, Forbidden(deepcopy(template_tree)))

return g
end


struct Entity{I}
target_function::Any
# _f::Any
Expand Down
7 changes: 1 addition & 6 deletions test/qn_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,11 @@ end

@testitem "QN Grammar Creation" begin
entities = [:a, :b, :c]
constants = [i for i = 1:10]
constants = [i for i = 0:10]
g = build_qn_grammar(entities, constants)

@test issubset(Set(entities), Set(g.rules))
@test issubset(Set(constants), Set(g.rules))

g2 = build_qn_grammar(Symbol[], Integer[])

@test isempty(intersect(Set(g2.rules), Set(entities)))
@test isempty(intersect(Set(g2.rules), Set(constants)))
end

@testitem "QN Sampling" setup = [RandomSetup, ExampleQN] begin
Expand Down
Loading