diff --git a/Project.toml b/Project.toml index 78a9be9..a2b86cd 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Constraints" uuid = "30f324ab-b02d-43f0-b619-e131c61659f7" authors = ["Jean-François Baffier"] -version = "0.1.5" +version = "0.1.6" [deps] CompositionalNetworks = "4b67e4b5-442d-4ef5-b760-3f5df3a57537" @@ -9,9 +9,9 @@ ConstraintDomains = "5800fd60-8556-4464-8d61-84ebf7a0bedb" Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" [compat] -ConstraintDomains = "0.1.1" -CompositionalNetworks = "0.1.2" -Dictionaries = "0.3.6" +CompositionalNetworks = "0.1.3" +ConstraintDomains = "0.1.2" +Dictionaries = "0.3" julia = "1.5, 1.6" [extras] diff --git a/docs/Project.toml b/docs/Project.toml index d87c885..8889d0e 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,3 +1,5 @@ [deps] +CompositionalNetworks = "4b67e4b5-442d-4ef5-b760-3f5df3a57537" +ConstraintDomains = "5800fd60-8556-4464-8d61-84ebf7a0bedb" Constraints = "30f324ab-b02d-43f0-b619-e131c61659f7" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" diff --git a/docs/make.jl b/docs/make.jl index 1371d8c..bb2c1ff 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,8 +1,10 @@ using Constraints +using CompositionalNetworks +using ConstraintDomains using Documenter makedocs(; - modules=[Constraints], + modules=[Constraints, ConstraintDomains, CompositionalNetworks], authors="Jean-François Baffier", repo="https://github.com/JuliaConstraints/Constraints.jl/blob/{commit}{path}#L{line}", sitename="Constraints.jl", @@ -13,8 +15,15 @@ makedocs(; ), pages=[ "Home" => "index.md", - "Library" => "library.md", - "Contributing" => "contributing.md", + "Learning (ICN)" => "learning.md", + "Dependencies" => [ + "ConstraintDomains.jl" => "domain.md", + "CompositionalNetworks.jl" => "icn.md", + ], + "Library" => [ + "Public" => "public.md", + "Internal" => "internal.md" + ], ], ) diff --git a/docs/src/domain.md b/docs/src/domain.md new file mode 100644 index 0000000..64ae2b4 --- /dev/null +++ b/docs/src/domain.md @@ -0,0 +1,7 @@ +# ConstraintDomains.jl + +Currently only discrete domains are supported using the following function. + +```@docs +ConstraintDomains.domain +``` \ No newline at end of file diff --git a/docs/src/icn.md b/docs/src/icn.md new file mode 100644 index 0000000..3ae214f --- /dev/null +++ b/docs/src/icn.md @@ -0,0 +1,58 @@ +# CompositionalNetworks.jl + +```@contents +Pages = ["public.md"] +Depth = 5 +``` + +`CompositionalNetworks.jl`, a Julia package for Interpretable Compositional Networks (ICN), a variant of neural networks, allowing the user to get interpretable results, unlike regular artificial neural networks. + +The current state of our ICN focuses on the composition of error functions for `LocalSearchSolvers.jl`, but produces results independently of it and export it to either/both Julia functions or/and human readable output. + +### How does it work? + +The package comes with a basic ICN for learning global constraints. The ICN is composed of 4 layers: `transformation`, `arithmetic`, `aggregation`, and `comparison`. Each contains several operations that can be composed in various ways. +Given a `concept` (a predicate over the variables' domains), a metric (`hamming` by default), and the variables' domains, we learn the binary weights of the ICN. + +## Installation + +```julia +] add CompositionalNetworks +``` + +As the package is in a beta version, some changes in the syntax and features are likely to occur. However, those changes should be minimal between minor versions. Please update with caution. + +## Quickstart + +```julia +# 4 variables in 1:4 +doms = [domain([1,2,3,4]) for i in 1:4] + +# allunique concept (that is used to define the :all_different constraint) +err = explore_learn_compose(allunique, domains=doms) +# > interpretation: identity ∘ count_positive ∘ sum ∘ count_eq_left + +# test our new error function +@assert err([1,2,3,3], dom_size = 4) > 0.0 + +# export an all_different function to file "current/path/test_dummy.jl" +compose_to_file!(icn, "all_different", "test_dummy.jl") +``` + +The output file should produces a function that can be used as follows (assuming the maximum domain size is `7`) + +```julia +import CompositionalNetworks + +all_different([1,2,3,4,5,6,7]; dom_size = 7) +# > 0.0 (which means true, no errors) +``` + +Please see `JuliaConstraints/Constraints.jl/learn.jl` for an extensive example of ICN learning and compositions. + +## Public interface + +```@autodocs +Modules = [CompositionalNetworks] +Private = false +``` diff --git a/docs/src/index.md b/docs/src/index.md index 07409d8..e321f45 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -2,5 +2,43 @@ CurrentModule = Constraints ``` -# Constraints for JuliaConstraints front packages +# Constraints.jl +A back-end pacage for JuliaConstraints front packages, such as `LocalSearchSolvers.jl`. + +It provides the following features: +- A dictionary to store usual constraint: `usual_contraint`, which contains the following entries + - `:all_different` + - `:dist_different` + - `:eq`, `:all_equal`, `:all_equal_param` + - `:ordered` + - `:always_true` (mainly for testing default `Constraint()` constructor) +- For each constraint `c`, the following properties + - arguments length + - concept (predicate the variables compliance with `c`) + - error (a function that evaluate how much `c` is violated) + - parameters length + - known symmetries of `c` +- A learning function using `CompositionalNetworks.jl`. If no error function is given when instanciating `c`, it will check the existence of a composition related to `c` and set the error to it. + +Follow the list of the constraints currently stored in `usual_constraint`. Note that if the constraint is named `_my_constraint`, it can be accessed as `usual_contraint[:my_constraint]`. + +```@docs +Constraints._all_different +Constraints._all_equal +Constraints._all_equal_param +Constraints._dist_different +Constraints._eq +Constraints._ordered +``` + +## Contributing + +Contributions to this package are more than welcome and can be arbitrarily, and not exhaustively, split as follows: +- Adding new constraints and symmetries +- Adding new ICNs to learn error of existing constraints +- Creating other compositional networks which target other kind of constraints +- Just making stuff better, faster, user-friendlier, etc. + +### Contact +Do not hesitate to contact me (@azzaare) or other members of JuliaConstraints on GitHub (file an issue), the julialang discourse forum, the julialang slack channel, the julialang zulip server, or the Human of Julia (HoJ) discord server. \ No newline at end of file diff --git a/docs/src/internal.md b/docs/src/internal.md new file mode 100644 index 0000000..2b6de61 --- /dev/null +++ b/docs/src/internal.md @@ -0,0 +1,11 @@ +# Internal + +```@contents +Pages = ["internal.md"] +Depth = 5 +``` + +```@autodocs +Modules = [Constraints] +Public = false +``` diff --git a/docs/src/contributing.md b/docs/src/learning.md similarity index 100% rename from docs/src/contributing.md rename to docs/src/learning.md diff --git a/docs/src/library.md b/docs/src/library.md deleted file mode 100644 index f836bc9..0000000 --- a/docs/src/library.md +++ /dev/null @@ -1,8 +0,0 @@ -# List of constraints - -```@index -``` - -```@autodocs -Modules = [Constraints] -``` \ No newline at end of file diff --git a/docs/src/public.md b/docs/src/public.md new file mode 100644 index 0000000..1afde1f --- /dev/null +++ b/docs/src/public.md @@ -0,0 +1,11 @@ +# Public + +```@contents +Pages = ["public.md"] +Depth = 5 +``` + +```@autodocs +Modules = [Constraints] +Private = false +``` diff --git a/src/Constraints.jl b/src/Constraints.jl index 6b5b36c..f26b1f2 100644 --- a/src/Constraints.jl +++ b/src/Constraints.jl @@ -7,7 +7,7 @@ import CompositionalNetworks: hamming, explore_learn_compose, compose_to_file! import ConstraintDomains: domain # Exports -export Constraint, usual_constraints +export Constraint, usual_constraints, usual_symmetries export args_length, concept, error_f, params_length, symmetries # Includes internals diff --git a/src/compositions/_icn_all_different.jl b/src/compositions/_icn_all_different.jl index b808ccb..df05dd9 100644 --- a/src/compositions/_icn_all_different.jl +++ b/src/compositions/_icn_all_different.jl @@ -1,3 +1,3 @@ function _icn_all_different(x; param=nothing, dom_size) - fill(x, 1) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_count_eq_left]) |> CompositionalNetworks._ar_prod |> CompositionalNetworks._ag_count_positive |> (y -> CompositionalNetworks._co_identity(y; param=param, dom_size=dom_size, nvars=length(x))) + fill(x, 1) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_count_eq_right]) |> CompositionalNetworks._ar_sum |> CompositionalNetworks._ag_count_positive |> (y -> CompositionalNetworks._co_identity(y; param=param, dom_size=dom_size, nvars=length(x))) end diff --git a/src/compositions/_icn_all_equal.jl b/src/compositions/_icn_all_equal.jl index e744da1..1fde1fc 100644 --- a/src/compositions/_icn_all_equal.jl +++ b/src/compositions/_icn_all_equal.jl @@ -1,3 +1,3 @@ function _icn_all_equal(x; param=nothing, dom_size) - fill(x, 1) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_identity]) |> CompositionalNetworks._ar_prod |> CompositionalNetworks._ag_count_positive |> (y -> CompositionalNetworks._co_euclidian(y; param=param, dom_size=dom_size, nvars=length(x))) + fill(x, 1) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_identity]) |> CompositionalNetworks._ar_sum |> CompositionalNetworks._ag_count_positive |> (y -> CompositionalNetworks._co_euclidian(y; param=param, dom_size=dom_size, nvars=length(x))) end diff --git a/src/compositions/_icn_all_equal_param.jl b/src/compositions/_icn_all_equal_param.jl index 3e309f0..d94197d 100644 --- a/src/compositions/_icn_all_equal_param.jl +++ b/src/compositions/_icn_all_equal_param.jl @@ -1,3 +1,3 @@ function _icn_all_equal_param(x; param=nothing, dom_size) - fill(x, 1) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_count_g_param]) |> CompositionalNetworks._ar_sum |> CompositionalNetworks._ag_sum |> (y -> CompositionalNetworks._co_vars_minus_val(y; param=param, dom_size=dom_size, nvars=length(x))) + fill(x, 5) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_val_minus_param, CompositionalNetworks._tr_count_g_param, CompositionalNetworks._tr_count_l_right, CompositionalNetworks._tr_count_lesser, CompositionalNetworks._tr_count_eq]) |> CompositionalNetworks._ar_sum |> CompositionalNetworks._ag_count_positive |> (y -> CompositionalNetworks._co_identity(y; param=param, dom_size=dom_size, nvars=length(x))) end diff --git a/src/compositions/_icn_dist_different.jl b/src/compositions/_icn_dist_different.jl index 6741974..eab0b0a 100644 --- a/src/compositions/_icn_dist_different.jl +++ b/src/compositions/_icn_dist_different.jl @@ -1,3 +1,3 @@ function _icn_dist_different(x; param=nothing, dom_size) - fill(x, 4) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_count_l_right, CompositionalNetworks._tr_count_lesser, CompositionalNetworks._tr_count_greater, CompositionalNetworks._tr_count_eq_left]) |> CompositionalNetworks._ar_sum |> CompositionalNetworks._ag_count_positive |> (y -> CompositionalNetworks._co_vars_minus_val(y; param=param, dom_size=dom_size, nvars=length(x))) + fill(x, 4) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_count_l_left, CompositionalNetworks._tr_count_lesser, CompositionalNetworks._tr_count_greater, CompositionalNetworks._tr_count_eq_right]) |> CompositionalNetworks._ar_sum |> CompositionalNetworks._ag_count_positive |> (y -> CompositionalNetworks._co_abs_diff_val_vars(y; param=param, dom_size=dom_size, nvars=length(x))) end diff --git a/src/compositions/_icn_eq.jl b/src/compositions/_icn_eq.jl index 601c775..9a40539 100644 --- a/src/compositions/_icn_eq.jl +++ b/src/compositions/_icn_eq.jl @@ -1,3 +1,3 @@ function _icn_eq(x; param=nothing, dom_size) - fill(x, 1) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_count_greater]) |> CompositionalNetworks._ar_sum |> CompositionalNetworks._ag_sum |> (y -> CompositionalNetworks._co_identity(y; param=param, dom_size=dom_size, nvars=length(x))) + fill(x, 1) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_count_lesser]) |> CompositionalNetworks._ar_sum |> CompositionalNetworks._ag_sum |> (y -> CompositionalNetworks._co_identity(y; param=param, dom_size=dom_size, nvars=length(x))) end diff --git a/src/compositions/_icn_ordered.jl b/src/compositions/_icn_ordered.jl index 73ea8f3..786cdbf 100644 --- a/src/compositions/_icn_ordered.jl +++ b/src/compositions/_icn_ordered.jl @@ -1,3 +1,3 @@ function _icn_ordered(x; param=nothing, dom_size) - fill(x, 1) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_count_l_right]) |> CompositionalNetworks._ar_sum |> CompositionalNetworks._ag_count_positive |> (y -> CompositionalNetworks._co_identity(y; param=param, dom_size=dom_size, nvars=length(x))) + fill(x, 1) .|> map(f -> (y -> f(y; param=param)), [CompositionalNetworks._tr_contiguous_vals_minus]) |> CompositionalNetworks._ar_sum |> CompositionalNetworks._ag_count_positive |> (y -> CompositionalNetworks._co_identity(y; param=param, dom_size=dom_size, nvars=length(x))) end diff --git a/src/learn.jl b/src/learn.jl index 0a9b7ff..1bc0cdd 100644 --- a/src/learn.jl +++ b/src/learn.jl @@ -23,28 +23,16 @@ function learn_from_icn() config = Dict( :local_iter => 100, - :global_iter => 1, + :global_iter => 10, :search => :complete, :metric => hamming, :population => 400, ) path = joinpath(dirname(pathof(Constraints)),"compositions") - @info "path" path for t in targets @info "Starting learning for $(t.first)" - @info "param" get(t.second, :param, nothing) - err = explore_learn_compose( - concept(usual_constraints[t.first]); - domains=t.second[:domains], - param=get(t.second, :param, nothing), - local_iter=config[:local_iter], - global_iter=config[:global_iter], - search=config[:search], - metric=config[:metric], - popSize=config[:population], - ) name = "_icn_$(t.first)" compose_to_file!( concept(usual_constraints[t.first]),