From 7d7eee65bbf995464ba7297e0c05b49e6bba7e7d Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Tue, 11 Apr 2023 01:10:14 +0000 Subject: [PATCH 01/77] CompatHelper: add new compat entry for StatsBase at version 0.33, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index e5d0cfc..7b618ce 100644 --- a/Project.toml +++ b/Project.toml @@ -21,6 +21,7 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" [compat] +StatsBase = "0.33" julia = "1" [extras] From 77e003bc3e4ba7b6064f16cceb8dae850e1f370c Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Tue, 2 May 2023 01:11:50 +0000 Subject: [PATCH 02/77] CompatHelper: add new compat entry for StatsBase at version 0.34, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index e5d0cfc..6f95bb8 100644 --- a/Project.toml +++ b/Project.toml @@ -21,6 +21,7 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" [compat] +StatsBase = "0.34" julia = "1" [extras] From ea39431307833ea8ecac454749833fa53dc6d5e3 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 12 May 2023 17:20:10 +0200 Subject: [PATCH 03/77] Fix toml dependencies --- Project.toml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 7b618ce..56ba62e 100644 --- a/Project.toml +++ b/Project.toml @@ -21,8 +21,19 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" [compat] -StatsBase = "0.33" julia = "1" +BenchmarkTools = "1" +ComputedFieldTypes = "1" +DataStructures = "0.18" +FillArrays = "1" +FunctionWrappers = "1" +ProgressMeter = "1" +Reexport = "1" +Revise = "3" +SoleBase = "0.9" +SoleLogics = "0.2" +StatsBase = "0.33" +Suppressor = "0.2" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From 22957816092b20071ef097c17030dd3124086474 Mon Sep 17 00:00:00 2001 From: mauro-milella Date: Mon, 22 May 2023 16:19:36 +0200 Subject: [PATCH 04/77] installation utility script updated --- src/install.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/install.jl b/src/install.jl index 14b4539..05ffa78 100644 --- a/src/install.jl +++ b/src/install.jl @@ -25,6 +25,6 @@ end install("SoleBase", "https://github.com/aclai-lab/SoleBase.jl", "dev") install("SoleData", "https://github.com/aclai-lab/SoleData.jl", "dev") -install("SoleLogics", "https://github.com/aclai-lab/SoleLogics.jl", "algebras/giopaglia") +install("SoleLogics", "https://github.com/aclai-lab/SoleLogics.jl", "dev-v0.9") Pkg.instantiate() From 76d94760071cff78246993e3d95c58f0cbf0661a Mon Sep 17 00:00:00 2001 From: mauro-milella Date: Thu, 25 May 2023 21:52:07 +0200 Subject: [PATCH 05/77] comma removed from docstring --- src/dimensional-datasets/parse-dimensional-condition.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dimensional-datasets/parse-dimensional-condition.jl b/src/dimensional-datasets/parse-dimensional-condition.jl index 474a320..fff0df5 100644 --- a/src/dimensional-datasets/parse-dimensional-condition.jl +++ b/src/dimensional-datasets/parse-dimensional-condition.jl @@ -80,7 +80,7 @@ parsing of the attribute name; please, refer to `attribute_name` for their behav julia> syntaxstring(SoleModels.parsecondition("min[V1] <= 32")) "min[V1] <= 32.0" -julia> syntaxstring(parseformulatree("min[V1] <= 15 ∧ max[V1] >= 85"; proposition_parser=(x)->parsecondition(x; featvaltype = Int64,))) +julia> syntaxstring(parseformulatree("min[V1] <= 15 ∧ max[V1] >= 85"; proposition_parser=(x)->parsecondition(x; featvaltype = Int64))) "min[V1] <= 15 ∧ max[V1] >= 85" ``` From 8dbcfd9f9518ad2b77ea1bbca495c17fa05c9e74 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 30 May 2023 17:47:19 +0200 Subject: [PATCH 06/77] Broken --- src/SoleModels.jl | 75 ++-- src/conditional-data/conditional-alphabets.jl | 118 ------ src/conditional-data/conditional-datasets.jl | 89 ---- src/conditional-data/conditions.jl | 156 ------- src/conditional-data/featured-datasets.jl | 72 ---- src/conditional-data/main.jl | 69 ---- .../multi-frame-conditional-datasets.jl | 94 ----- .../datasets => datasets/base}/check.jl | 10 +- src/datasets/base/conditions.jl | 35 ++ .../base}/features.jl | 37 +- .../base}/generic-supporting-datasets.jl | 4 +- .../base}/generic-supports.jl | 4 +- src/datasets/base/logiset-interface.jl | 123 ++++++ src/datasets/base/logiset.jl | 273 +++++++++++++ src/datasets/base/main.jl | 51 +++ src/datasets/base/multiframe-logiset.jl | 94 +++++ src/datasets/base/representatives.jl | 37 ++ src/datasets/base/supported-logiset.jl | 58 +++ .../datasets/dimensional-fwds.jl | 36 +- .../datasets/dimensional-logiset.jl} | 98 ++--- .../datasets}/dimensional-supports.jl | 10 +- .../dimensional-datasets/datasets/main.jl | 30 +- .../datasets/passive-dimensional-dataset.jl | 2 +- .../dimensional-features.jl | 6 +- .../dimensional-ontologies.jl | 0 .../dimensional-datasets/gamma-access.jl | 53 +++ .../dimensional-datasets/main.jl | 4 +- .../dimensional-datasets/ontology.jl | 0 .../parse-dimensional-condition.jl | 24 +- .../representatives/Full0DFrame.jl | 0 .../representatives/Full1DFrame+IA.jl | 0 .../representatives/Full1DFrame+RCC.jl | 0 .../representatives/Full1DFrame.jl | 2 +- .../representatives/Full2DFrame.jl | 0 .../scalar-datasets/active-scalar-logiset.jl} | 68 ++-- .../canonical-scalar-conditions.jl} | 0 .../scalar-datasets}/gamma-access.jl | 77 +--- src/datasets/scalar-datasets/main.jl | 30 ++ .../one-step-featured-supporting-dataset.jl} | 21 +- .../scalar-datasets}/random.jl | 14 +- .../scalar-datasets}/representatives.jl | 6 +- .../scalar-datasets/scalar-conditions.jl | 247 ++++++++++++ .../scalar-datasets}/test-operators.jl | 0 .../datasets/featured-dataset.jl | 380 ------------------ .../datasets/supported-featured-dataset.jl | 192 --------- src/{conditional-data => utils}/minify.jl | 0 test/datasets.jl | 31 +- 47 files changed, 1269 insertions(+), 1461 deletions(-) delete mode 100644 src/conditional-data/conditional-alphabets.jl delete mode 100644 src/conditional-data/conditional-datasets.jl delete mode 100644 src/conditional-data/conditions.jl delete mode 100644 src/conditional-data/featured-datasets.jl delete mode 100644 src/conditional-data/main.jl delete mode 100644 src/conditional-data/multi-frame-conditional-datasets.jl rename src/{dimensional-datasets/datasets => datasets/base}/check.jl (96%) create mode 100644 src/datasets/base/conditions.jl rename src/{conditional-data => datasets/base}/features.jl (74%) rename src/{dimensional-datasets/datasets => datasets/base}/generic-supporting-datasets.jl (97%) rename src/{dimensional-datasets/datasets/one-step-featured-supporting-dataset => datasets/base}/generic-supports.jl (95%) create mode 100644 src/datasets/base/logiset-interface.jl create mode 100644 src/datasets/base/logiset.jl create mode 100644 src/datasets/base/main.jl create mode 100644 src/datasets/base/multiframe-logiset.jl create mode 100644 src/datasets/base/representatives.jl create mode 100644 src/datasets/base/supported-logiset.jl rename src/{ => datasets}/dimensional-datasets/datasets/dimensional-fwds.jl (92%) rename src/{dimensional-datasets/datasets/dimensional-featured-dataset.jl => datasets/dimensional-datasets/datasets/dimensional-logiset.jl} (74%) rename src/{dimensional-datasets/datasets/one-step-featured-supporting-dataset => datasets/dimensional-datasets/datasets}/dimensional-supports.jl (96%) rename src/{ => datasets}/dimensional-datasets/datasets/main.jl (74%) rename src/{ => datasets}/dimensional-datasets/datasets/passive-dimensional-dataset.jl (96%) rename src/{ => datasets}/dimensional-datasets/dimensional-features.jl (98%) rename src/{ => datasets}/dimensional-datasets/dimensional-ontologies.jl (100%) create mode 100644 src/datasets/dimensional-datasets/gamma-access.jl rename src/{ => datasets}/dimensional-datasets/main.jl (95%) rename src/{ => datasets}/dimensional-datasets/ontology.jl (100%) rename src/{ => datasets}/dimensional-datasets/parse-dimensional-condition.jl (92%) rename src/{ => datasets}/dimensional-datasets/representatives/Full0DFrame.jl (100%) rename src/{ => datasets}/dimensional-datasets/representatives/Full1DFrame+IA.jl (100%) rename src/{ => datasets}/dimensional-datasets/representatives/Full1DFrame+RCC.jl (100%) rename src/{ => datasets}/dimensional-datasets/representatives/Full1DFrame.jl (89%) rename src/{ => datasets}/dimensional-datasets/representatives/Full2DFrame.jl (100%) rename src/{conditional-data/active-featured-dataset.jl => datasets/scalar-datasets/active-scalar-logiset.jl} (62%) rename src/{conditional-data/canonical-conditions.jl => datasets/scalar-datasets/canonical-scalar-conditions.jl} (100%) rename src/{dimensional-datasets => datasets/scalar-datasets}/gamma-access.jl (54%) create mode 100644 src/datasets/scalar-datasets/main.jl rename src/{dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl => datasets/scalar-datasets/one-step-featured-supporting-dataset.jl} (94%) rename src/{conditional-data => datasets/scalar-datasets}/random.jl (85%) rename src/{conditional-data => datasets/scalar-datasets}/representatives.jl (95%) create mode 100644 src/datasets/scalar-datasets/scalar-conditions.jl rename src/{conditional-data => datasets/scalar-datasets}/test-operators.jl (100%) delete mode 100644 src/dimensional-datasets/datasets/featured-dataset.jl delete mode 100644 src/dimensional-datasets/datasets/supported-featured-dataset.jl rename src/{conditional-data => utils}/minify.jl (100%) diff --git a/src/SoleModels.jl b/src/SoleModels.jl index acc1986..bfca502 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -48,59 +48,68 @@ export rulemetrics include("models/rule-evaluation.jl") -# TODO avoid? +export minify, isminifiable + +# Minification interface for lossless data compression +include("utils/minify.jl") + export AbstractFeature, - DimensionalFeature, AbstractUnivariateFeature, - UnivariateNamedFeature, - UnivariateFeature, NamedFeature, ExternalFWDFeature export propositions export computefeature -include("conditional-data/main.jl") +# Definitions for logical datasets (i.e., logisets) +include("datasets/base/main.jl") + +# include("datasets/scalar-datasets/main.jl") + +# export nsamples, nframes, frames, nfeatures -export nsamples, nframes, frames, nfeatures +# export get_ontology, +# get_interval_ontology -export get_ontology, - get_interval_ontology +# export DimensionalLogiset, Logiset, SupportedScalarLogiset -export DimensionalFeaturedDataset, FeaturedDataset, SupportedFeaturedDataset +# export parsecondition -export parsecondition +# export UnivariateMin, UnivariateMax, +# UnivariateSoftMin, UnivariateSoftMax, +# MultivariateFeature -export UnivariateMin, UnivariateMax, - UnivariateSoftMin, UnivariateSoftMax, - MultivariateFeature +# export DimensionalFeature, AbstractUnivariateFeature, +# UnivariateNamedFeature, +# UnivariateFeature -include("dimensional-datasets/main.jl") +# include("datasets/dimensional-datasets/main.jl") -using .DimensionalDatasets: parsecondition +# using .DimensionalDatasets: parsecondition -using .DimensionalDatasets: nfeatures, nrelations, - # - relations, - # - GenericModalDataset, - AbstractActiveFeaturedDataset, - DimensionalFeaturedDataset, - FeaturedDataset, - SupportedFeaturedDataset +# using .DimensionalDatasets: nfeatures, nrelations, +# # +# relations, +# # +# GenericModalDataset, +# AbstractLogiset, +# AbstractActiveScalarLogiset, +# DimensionalLogiset, +# Logiset, +# SupportedScalarLogiset -using .DimensionalDatasets: AbstractWorld, AbstractRelation -using .DimensionalDatasets: AbstractWorldSet, WorldSet -using .DimensionalDatasets: FullDimensionalFrame +# using .DimensionalDatasets: AbstractWorld, AbstractRelation +# using .DimensionalDatasets: AbstractWorldSet, WorldSet +# using .DimensionalDatasets: FullDimensionalFrame -using .DimensionalDatasets: Ontology, worldtype +# using .DimensionalDatasets: Ontology, worldtype -using .DimensionalDatasets: get_ontology, - get_interval_ontology +# using .DimensionalDatasets: get_ontology, +# get_interval_ontology -using .DimensionalDatasets: OneWorld, OneWorldOntology +# using .DimensionalDatasets: OneWorld, OneWorldOntology -using .DimensionalDatasets: Interval, Interval2D +# using .DimensionalDatasets: Interval, Interval2D -using .DimensionalDatasets: IARelations +# using .DimensionalDatasets: IARelations end diff --git a/src/conditional-data/conditional-alphabets.jl b/src/conditional-data/conditional-alphabets.jl deleted file mode 100644 index c91b428..0000000 --- a/src/conditional-data/conditional-alphabets.jl +++ /dev/null @@ -1,118 +0,0 @@ - -""" - abstract type AbstractConditionalAlphabet{C<:FeatCondition} <: AbstractAlphabet{C} end - -Abstract type for alphabets of conditions. - -See also -[`FeatCondition`](@ref), -[`FeatMetaCondition`](@ref), -[`AbstractAlphabet`](@ref). -""" -abstract type AbstractConditionalAlphabet{C<:FeatCondition} <: AbstractAlphabet{C} end - -""" - struct UnboundedExplicitConditionalAlphabet{C<:FeatCondition} <: AbstractConditionalAlphabet{C} - metaconditions::Vector{<:FeatMetaCondition} - end - -An infinite alphabet of conditions induced from a finite set of metaconditions. -For example, if `metaconditions = [FeatMetaCondition(UnivariateMin(1), ≥)]`, -the alphabet represents the (infinite) set: \${min(V1) ≥ a, a ∈ ℝ}\$. - -See also -[`BoundedExplicitConditionalAlphabet`](@ref), -[`FeatCondition`](@ref), -[`FeatMetaCondition`](@ref), -[`AbstractAlphabet`](@ref). -""" -struct UnboundedExplicitConditionalAlphabet{C<:FeatCondition} <: AbstractConditionalAlphabet{C} - metaconditions::Vector{<:FeatMetaCondition} - - function UnboundedExplicitConditionalAlphabet{C}( - metaconditions::Vector{<:FeatMetaCondition} - ) where {C<:FeatCondition} - new{C}(metaconditions) - end - - function UnboundedExplicitConditionalAlphabet( - features :: AbstractVector{C}, - test_operators :: AbstractVector, - ) where {C<:FeatCondition} - metaconditions = - [FeatMetaCondition(f, t) for f in features for t in test_operators] - UnboundedExplicitConditionalAlphabet{C}(metaconditions) - end -end - -Base.isfinite(::Type{<:UnboundedExplicitConditionalAlphabet}) = false - -function Base.in(p::Proposition{<:FeatCondition}, a::UnboundedExplicitConditionalAlphabet) - fc = atom(p) - idx = findfirst(mc->mc == metacond(fc), a.metaconditions) - return !isnothing(idx) -end - -""" - struct BoundedExplicitConditionalAlphabet{C<:FeatCondition} <: AbstractConditionalAlphabet{C} - grouped_featconditions::Vector{Tuple{<:FeatMetaCondition,Vector}} - end - -A finite alphabet of conditions, grouped by (a finite set of) metaconditions. - -See also -[`UnboundedExplicitConditionalAlphabet`](@ref), -[`FeatCondition`](@ref), -[`FeatMetaCondition`](@ref), -[`AbstractAlphabet`](@ref). -""" -# Finite alphabet of conditions induced from a set of metaconditions -struct BoundedExplicitConditionalAlphabet{C<:FeatCondition} <: AbstractConditionalAlphabet{C} - grouped_featconditions::Vector{<:Tuple{FeatMetaCondition,Vector}} - - function BoundedExplicitConditionalAlphabet{C}( - grouped_featconditions::Vector{<:Tuple{FeatMetaCondition,Vector}} - ) where {C<:FeatCondition} - new{C}(grouped_featconditions) - end - - function BoundedExplicitConditionalAlphabet{C}( - metaconditions::Vector{<:FeatMetaCondition}, - thresholds::Vector{<:Vector}, - ) where {C<:FeatCondition} - length(metaconditions) != length(thresholds) && - error("Can't instantiate BoundedExplicitConditionalAlphabet with mismatching" * - " number of `metaconditions` and `thresholds`" * - " ($(metaconditions) != $(thresholds)).") - grouped_featconditions = collect(zip(metaconditions, thresholds)) - BoundedExplicitConditionalAlphabet{C}(grouped_featconditions) - end - - function BoundedExplicitConditionalAlphabet( - features :: AbstractVector{C}, - test_operators :: AbstractVector, - thresholds :: Vector - ) where {C<:FeatCondition} - metaconditions = - [FeatMetaCondition(f, t) for f in features for t in test_operators] - BoundedExplicitConditionalAlphabet{C}(metaconditions, thresholds) - end -end - -function propositions(a::BoundedExplicitConditionalAlphabet) - Iterators.flatten( - map( - ((mc,thresholds),)->map( - threshold->Proposition(FeatCondition(mc, threshold)), - thresholds), - a.grouped_featconditions - ) - ) |> collect -end - -function Base.in(p::Proposition{<:FeatCondition}, a::BoundedExplicitConditionalAlphabet) - fc = atom(p) - grouped_featconditions = a.grouped_featconditions - idx = findfirst(((mc,thresholds),)->mc == metacond(fc), grouped_featconditions) - return !isnothing(idx) && Base.in(threshold(fc), last(grouped_featconditions[idx])) -end diff --git a/src/conditional-data/conditional-datasets.jl b/src/conditional-data/conditional-datasets.jl deleted file mode 100644 index b174afc..0000000 --- a/src/conditional-data/conditional-datasets.jl +++ /dev/null @@ -1,89 +0,0 @@ -using SoleLogics: AbstractKripkeStructure, AbstractInterpretationSet, AbstractFrame -import SoleLogics: alphabet, frame, check -import SoleLogics: accessibles, allworlds, nworlds, initialworld -import SoleLogics: worldtype, frametype -export check, accessibles, allworlds, representatives - -""" - abstract type AbstractConditionalDataset{ - W<:AbstractWorld, - A<:AbstractCondition, - T<:TruthValue, - FR<:AbstractFrame{W,T}, - } <: AbstractInterpretationSet{AbstractKripkeStructure{W,A,T,FR}} end - -Abstract type for conditional datasets, that is, -symbolic learning datasets where each instance is a Kripke model -where conditions (see [`AbstractCondition`](@ref)), and logical formulas -with conditional letters can be checked on worlds. - -See also -[`AbstractInterpretationSet`](@ref), -[`AbstractCondition`](@ref). -""" -abstract type AbstractConditionalDataset{ - W<:AbstractWorld, - A<:AbstractCondition, - T<:TruthValue, - FR<:AbstractFrame{W,T}, -} <: AbstractInterpretationSet{AbstractKripkeStructure{W,A,T,FR}} end - -representatives(X::AbstractConditionalDataset, i_sample, args...) = representatives(frame(X, i_sample), args...) - -# TODO initialworld is at model-level, not at frame-level? -function initialworld( - X::AbstractConditionalDataset{W,A,T}, - i_sample -) where {W<:AbstractWorld,A<:AbstractCondition,T<:TruthValue} - error("Please, provide method initialworld(::$(typeof(X)), i_sample::$(typeof(i_sample))).") -end - -function check( - p::Proposition{A}, - X::AbstractConditionalDataset{W,AA,T}, - i_sample, - w::W, -)::T where {W<:AbstractWorld,AA<:AbstractCondition,T<:TruthValue,A<:AA} - error("Please, provide method check(p::$(typeof(p)), X::$(typeof(X)), i_sample::$(typeof(i_sample)), w::$(typeof(w))).") -end - -function check( - f::AbstractFormula, - X::AbstractConditionalDataset{W,A,T}, - i_sample, - w::W, -)::T where {W<:AbstractWorld,A<:AbstractCondition,T<:TruthValue} - error("Please, provide method check(f::$(typeof(f)), X::$(typeof(X)), i_sample::$(typeof(i_sample)), w::$(typeof(w))).") -end - -function display_structure(X::AbstractConditionalDataset; kwargs...) - error("Please, provide method display_structure(X::$(typeof(X)); kwargs...).") -end - - -""" - abstract type AbstractActiveConditionalDataset{ - W<:AbstractWorld, - A<:AbstractCondition, - T<:TruthValue, - FR<:AbstractFrame{W,T}, - } <: AbstractConditionalDataset{W,A,T,FR} end - -Abstract type for active conditional datasets, that is, -conditional datasets that can be used in machine learning algorithms -(e.g., they have an alphabet, can enumerate propositions and learn formulas from). - -See also -[`AbstractConditionalDataset`](@ref), -[`AbstractCondition`](@ref). -""" -abstract type AbstractActiveConditionalDataset{ - W<:AbstractWorld, - A<:AbstractCondition, - T<:TruthValue, - FR<:AbstractFrame{W,T}, -} <: AbstractConditionalDataset{W,A,T,FR} end - -function alphabet(X::AbstractActiveConditionalDataset) - error("Please, provide method alphabet(::$(typeof(X))).") -end diff --git a/src/conditional-data/conditions.jl b/src/conditional-data/conditions.jl deleted file mode 100644 index f67c97a..0000000 --- a/src/conditional-data/conditions.jl +++ /dev/null @@ -1,156 +0,0 @@ - -using SoleLogics: AbstractAlphabet -using Random -import SoleLogics: negation, propositions - -import Base: isequal, hash, in, isfinite, length - -""" - abstract type AbstractCondition end - -Abstract type for representing conditions that can be interpreted and evaluated -on worlds of instances of a conditional dataset. In logical contexts, -these are wrapped into `Proposition`s. - -See also -[`Proposition`](@ref), -[`syntaxstring`](@ref), -[`FeatMetaCondition`](@ref), -[`FeatCondition`](@ref). -""" -abstract type AbstractCondition end # TODO parametric? - -function syntaxstring(c::AbstractCondition; kwargs...) - error("Please, provide method syntaxstring(::$(typeof(c)); kwargs...)." * - " Note that this value must be unique.") -end - -function Base.show(io::IO, c::AbstractCondition) - # print(io, "Feature of type $(typeof(c))\n\t-> $(syntaxstring(c))") - print(io, "$(typeof(c)): $(syntaxstring(c))") - # print(io, "$(syntaxstring(c))") -end - -Base.isequal(a::AbstractCondition, b::AbstractCondition) = syntaxstring(a) == syntaxstring(b) # nameof(x) == nameof(feature) -Base.hash(a::AbstractCondition) = Base.hash(syntaxstring(a)) - -############################################################################################ - -""" - struct FeatMetaCondition{F<:AbstractFeature,O<:TestOperator} <: AbstractCondition - feature::F - test_operator::O - end - -A metacondition representing a scalar comparison method. -A feature is a scalar function that can be computed on a world -of an instance of a conditional dataset. -A test operator is a binary mathematical relation, comparing the computed feature value -and an external threshold value (see `FeatCondition`). A metacondition can also be used -for representing the infinite set of conditions that arise with a free threshold -(see `UnboundedExplicitConditionalAlphabet`): \${min(V1) ≥ a, a ∈ ℝ}\$. - -See also -[`AbstractCondition`](@ref), -[`negation`](@ref), -[`FeatCondition`](@ref). -""" -struct FeatMetaCondition{F<:AbstractFeature,O<:TestOperator} <: AbstractCondition - - # Feature: a scalar function that can be computed on a world - feature::F - - # Test operator (e.g. ≥) - test_operator::O - -end - -feature(m::FeatMetaCondition) = m.feature -test_operator(m::FeatMetaCondition) = m.test_operator - -negation(m::FeatMetaCondition) = FeatMetaCondition(feature(m), inverse_test_operator(test_operator(m))) - -syntaxstring(m::FeatMetaCondition; kwargs...) = - "$(_syntaxstring_metacondition(m; kwargs...)) ⍰" - -function _syntaxstring_metacondition( - m::FeatMetaCondition; - use_feature_abbreviations::Bool = false, - kwargs..., -) - if use_feature_abbreviations - _st_featop_abbr(feature(m), test_operator(m); kwargs...) - else - _st_featop_name(feature(m), test_operator(m); kwargs...) - end -end - -_st_featop_name(feature::AbstractFeature, test_operator::TestOperator; kwargs...) = "$(syntaxstring(feature; kwargs...)) $(test_operator)" - -# Abbreviations - -_st_featop_abbr(feature::AbstractFeature, test_operator::TestOperator; kwargs...) = _st_featop_name(feature, test_operator; kwargs...) - -############################################################################################ - -""" - struct FeatCondition{U,M<:FeatMetaCondition} <: AbstractCondition - metacond::M - a::U - end - -A scalar condition comparing a computed feature value (see `FeatMetaCondition`) -and a threshold value `a`. -It can be evaluated on a world -of an instance of a conditional dataset. - -Example: \$min(V1) ≥ 10\$, which translates to -"Within this world, the minimum of variable 1 is greater or equal than 10." - -See also -[`AbstractCondition`](@ref), -[`negation`](@ref), -[`FeatMetaCondition`](@ref). -""" -struct FeatCondition{U,M<:FeatMetaCondition} <: AbstractCondition - - # Metacondition - metacond::M - - # Threshold value - threshold::U - - function FeatCondition( - metacond :: M, - threshold :: U - ) where {M<:FeatMetaCondition,U} - new{U,M}(metacond, threshold) - end - - function FeatCondition( - condition :: FeatCondition{U,M}, - threshold :: U - ) where {M<:FeatMetaCondition,U} - new{U,M}(condition.metacond, threshold) - end - - function FeatCondition( - feature :: AbstractFeature, - test_operator :: TestOperator, - threshold :: U - ) where {U} - metacond = FeatMetaCondition(feature, test_operator) - FeatCondition(metacond, threshold) - end -end - -metacond(c::FeatCondition) = c.metacond -threshold(c::FeatCondition) = c.threshold - -feature(c::FeatCondition) = feature(metacond(c)) -test_operator(c::FeatCondition) = test_operator(metacond(c)) - -negation(c::FeatCondition) = FeatCondition(negation(metacond(c)), threshold(c)) - -syntaxstring(m::FeatCondition; threshold_decimals = nothing, kwargs...) = - "$(_syntaxstring_metacondition(metacond(m); kwargs...)) $((isnothing(threshold_decimals) ? threshold(m) : round(threshold(m); digits=threshold_decimals)))" diff --git a/src/conditional-data/featured-datasets.jl b/src/conditional-data/featured-datasets.jl deleted file mode 100644 index 4d0cac4..0000000 --- a/src/conditional-data/featured-datasets.jl +++ /dev/null @@ -1,72 +0,0 @@ -# Featured datasets: - -# const AbstractFeaturedDataset{ -# V<:Number, -# W<:AbstractWorld, -# FR<:AbstractFrame{W,Bool}, -# FT<:AbstractFeature{V} -# } = AbstractConditionalDataset{W,AbstractCondition,Bool,FR} - -# function featvalue( -# X::AbstractFeaturedDataset{W}, -# i_sample, -# w::W, -# f::AbstractFeature, -# ) where {W<:AbstractWorld} -# error("Please, provide method featvalue(::$(typeof(X)), i_sample::$(typeof(i_sample)), w::$(typeof(w)), w::$(typeof(f))).") -# end - -# function check( -# p::Proposition{<:FeatCondition}, -# X::AbstractFeaturedDataset{W}, -# i_sample, -# w::W, -# ) where {W<:AbstractWorld} -# cond = atom(p) -# featval = featvalue(X, i_sample, w, feature(cond)) -# apply_test_operator(test_operator(cond), featval, threshold(cond)) -# end - - -# # forma passiva implicita del dataset (simile a ontological dataset) -# struct ImplicitConditionalDataset{N,U,W<:AbstractWorld,C<:AbstractCondition,FR,FRS<:AbstractFrameSet{FR},M<:AbstractKripkeStructure{W,C,T,FR}} <: PassiveFeaturedDataset{M} end -# domain::AbstractArray{N,U} # TODO questo non dovrebbe essere necessariamente dimensionale! C'è un altro Layer qui in mezzo. -# frameset::FRS -# end - -# # forma passiva esplicita (= tabella proposizionale) -# struct UniformFullDimensionalFeaturedConditionalDataset{N,U,W<:AbstractWorld,... TODO, MDA} <: PassiveFeaturedDataset{M} end -# domain::MDA -# features::Vector{AbstractFeature{U}} -# end - -# # TODO funzioni che tipo convertono da ImplicitConditionalDataset a UniformFullDimensionalFeaturedConditionalDataset (e viceversa?). - -# # forma attiva = pronta per essere learnata -# struct ConditionalDataset{ -# W<:AbstractWorld, -# T<:TruthValue, -# M<:AbstractKripkeStructure{W,T}, -# C<:AbstractCondition, # Nota che le non sono! Quando checcki formule, devi avere vere condizioni. -# PCD<:PassiveFeaturedDataset{U,W,C}, -# AL<:AbstractConditionalAlphabet{C}, # Però questo alfabeto può essere implementato come un vettore di MetaCondition's, che induce un alfabeto infinito di AbstractCondition's -# } <: AbstractActiveConditionalDataset{M} -# cd:PCD -# alphabet::AL -# end - -# check(ms::ConditionalDataset{W, T, M, C}, args...) = check(ms.cd, args...) # TODO scrivere in forma estesa oppure col forward, e indica che le lettere e formule devono avere atomi di tipo C. -# accessibles(ms::ConditionalDataset{W, T, M, C}, args...) = accessibles(ms.cd, args...) # TODO scrivere in forma estesa oppure col forward, e indica che le lettere e formule devono avere atomi di tipo C. - - -# # TODO from here onwards - -# { -# ConditionalDatasetWithMemo <: AbstractActiveConditionalDataset che wrappa: -# dataset::ConditionalDataset -# H::ConditionalDatasetMemoStructure -# end - -# abstract ConditionalDatasetMemoStructure -# } - diff --git a/src/conditional-data/main.jl b/src/conditional-data/main.jl deleted file mode 100644 index bc9b280..0000000 --- a/src/conditional-data/main.jl +++ /dev/null @@ -1,69 +0,0 @@ -using SoleLogics: OneWorld, Interval, Interval2D -using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame -using SoleLogics: X, Y, Z -using SoleLogics: AbstractWorld, IdentityRel -import SoleLogics: syntaxstring -import SoleLogics: frame - -import SoleData: nsamples, nfeatures -import SoleData: nframes, frames, hasnans, _slice_dataset -# import SoleData: frame # TODO - -# Minification interface for lossless data compression -include("minify.jl") - -# Scalar features to be computed on worlds of dataset instances -include("features.jl") - -export inverse_test_operator, dual_test_operator, - apply_test_operator, - TestOperator - -# Test operators to be used for comparing features and threshold values -include("test-operators.jl") - -# Scalar conditions on the features, to be wrapped in Proposition's -include("conditions.jl") - -# Alphabets of conditions on the features, to be used in logical datasets -include("conditional-alphabets.jl") - -export MixedFeature, CanonicalFeature, canonical_geq, canonical_leq - -export canonical_geq_95, canonical_geq_90, canonical_geq_85, canonical_geq_80, canonical_geq_75, canonical_geq_70, canonical_geq_60, - canonical_leq_95, canonical_leq_90, canonical_leq_85, canonical_leq_80, canonical_leq_75, canonical_leq_70, canonical_leq_60 - -# Types for representing common associations between features and operators -include("canonical-conditions.jl") # TODO fix - -const MixedFeature = Union{AbstractFeature,CanonicalFeature,Function,Tuple{TestOperator,Function},Tuple{TestOperator,AbstractFeature}} - -# Representative accessibles, for optimized model checking -include("representatives.jl") - -# Datasets where the instances are Kripke models with conditional alphabets -include("conditional-datasets.jl") - -include("active-featured-dataset.jl") - -export nframes, frames, frame, - display_structure, - MultiFrameConditionalDataset, - worldtypes - -# # -# # TODO figure out which convert function works best: -# convert(::Type{<:MultiFrameConditionalDataset{T}}, X::MD) where {T,MD<:AbstractConditionalDataset{T}} = MultiFrameConditionalDataset{MD}([X]) -# convert(::Type{<:MultiFrameConditionalDataset}, X::AbstractConditionalDataset) = MultiFrameConditionalDataset([X]) - -# Multi-frame version of conditional datasets, for representing multimodal datasets -include("multi-frame-conditional-datasets.jl") # TODO define interface - -const ActiveMultiFrameConditionalDataset{T} = MultiFrameConditionalDataset{<:AbstractActiveFeaturedDataset{<:T}} - -# TODO decide how to name this. -getframe = frame - -# include("featured-datasets.jl") TODO? - -include("random.jl") diff --git a/src/conditional-data/multi-frame-conditional-datasets.jl b/src/conditional-data/multi-frame-conditional-datasets.jl deleted file mode 100644 index 47202c6..0000000 --- a/src/conditional-data/multi-frame-conditional-datasets.jl +++ /dev/null @@ -1,94 +0,0 @@ -""" - struct MultiFrameConditionalDataset{MD<:AbstractConditionalDataset} - frames :: Vector{<:MD} - end - -A multi-frame conditional dataset. This structure is useful for representing -multimodal datasets in logical terms. - -See also -[`AbstractConditionalDataset`](@ref), -[`minify`](@ref). -""" -struct MultiFrameConditionalDataset{MD<:AbstractConditionalDataset} - - frames :: Vector{<:MD} - - function MultiFrameConditionalDataset{MD}(X::MultiFrameConditionalDataset{MD}) where {MD<:AbstractConditionalDataset} - MultiFrameConditionalDataset{MD}(X.frames) - end - function MultiFrameConditionalDataset{MD}(Xs::AbstractVector) where {MD<:AbstractConditionalDataset} - Xs = collect(Xs) - @assert length(Xs) > 0 && length(unique(nsamples.(Xs))) == 1 "Can't create an empty MultiFrameConditionalDataset or with mismatching number of samples (nframes: $(length(Xs)), frame_sizes: $(nsamples.(Xs)))." - new{MD}(Xs) - end - function MultiFrameConditionalDataset{MD}() where {MD<:AbstractConditionalDataset} - new{MD}(MD[]) - end - function MultiFrameConditionalDataset{MD}(X::MD) where {MD<:AbstractConditionalDataset} - MultiFrameConditionalDataset{MD}(MD[X]) - end - function MultiFrameConditionalDataset(Xs::AbstractVector{<:MD}) where {MD<:AbstractConditionalDataset} - MultiFrameConditionalDataset{MD}(Xs) - end - function MultiFrameConditionalDataset(X::MD) where {MD<:AbstractConditionalDataset} - MultiFrameConditionalDataset{MD}(X) - end -end - -frames(X::MultiFrameConditionalDataset) = X.frames - -Base.iterate(X::MultiFrameConditionalDataset, state=1) = state > length(X) ? nothing : (get_instance(X, state), state+1) -Base.length(X::MultiFrameConditionalDataset) = nsamples(X) -Base.push!(X::MultiFrameConditionalDataset, f::AbstractConditionalDataset) = push!(frames(X), f) - -Base.size(X::MultiFrameConditionalDataset) = map(size, frames(X)) - -frame(X::MultiFrameConditionalDataset, i_frame::Integer) = nframes(X) > 0 ? frames(X)[i_frame] : error("MultiFrameConditionalDataset has no frame!") -nframes(X::MultiFrameConditionalDataset) = length(frames(X)) -nsamples(X::MultiFrameConditionalDataset) = nsamples(frame(X, 1)) - -# max_channel_size(X::MultiFrameConditionalDataset) = map(max_channel_size, frames(X)) # TODO: figure if this is useless or not. Note: channel_size doesn't make sense at this point. -nfeatures(X::MultiFrameConditionalDataset) = map(nfeatures, frames(X)) # Note: used for safety checks in fit_tree.jl -# nrelations(X::MultiFrameConditionalDataset) = map(nrelations, frames(X)) # TODO: figure if this is useless or not -nfeatures(X::MultiFrameConditionalDataset, i_frame::Integer) = nfeatures(frame(X, i_frame)) -nrelations(X::MultiFrameConditionalDataset, i_frame::Integer) = nrelations(frame(X, i_frame)) -worldtype(X::MultiFrameConditionalDataset, i_frame::Integer) = worldtype(frame(X, i_frame)) -worldtypes(X::MultiFrameConditionalDataset) = Vector{Type{<:AbstractWorld}}(worldtype.(frames(X))) - -get_instance(X::MultiFrameConditionalDataset, i_frame::Integer, idx_i::Integer, args...) = get_instance(frame(X, i_frame), idx_i, args...) -# get_instance(X::MultiFrameConditionalDataset, idx_i::Integer, args...) = get_instance(frame(X, i), idx_i, args...) # TODO should slice across the frames! - -_slice_dataset(X::MultiFrameConditionalDataset{MD}, inds::AbstractVector{<:Integer}, args...; kwargs...) where {MD<:AbstractConditionalDataset} = - MultiFrameConditionalDataset{MD}(Vector{MD}(map(frame->_slice_dataset(frame, inds, args...; kwargs...), frames(X)))) - -function display_structure(Xs::MultiFrameConditionalDataset; indent_str = "") - out = "$(typeof(Xs))" # * "\t\t\t$(Base.summarysize(Xs) / 1024 / 1024 |> x->round(x, digits=2)) MBs" - for (i_frame, X) in enumerate(frames(Xs)) - if i_frame == nframes(Xs) - out *= "\n$(indent_str)└ " - else - out *= "\n$(indent_str)├ " - end - out *= "[$(i_frame)] " - # \t\t\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t(worldtype: $(worldtype(X)))" - out *= display_structure(X; indent_str = indent_str * (i_frame == nframes(Xs) ? " " : "│ ")) * "\n" - end - out -end - -hasnans(Xs::MultiFrameConditionalDataset) = any(hasnans.(frames(Xs))) - -isminifiable(::MultiFrameConditionalDataset) = true - -function minify(Xs::MultiFrameConditionalDataset) - if !any(map(isminifiable, frames(Xs))) - if !all(map(isminifiable, frames(Xs))) - @error "Cannot perform minification with frames of types $(typeof.(frames(Xs))). Please use a minifiable format (e.g., SupportedFeaturedDataset)." - else - @warn "Cannot perform minification on some of the frames provided. Please use a minifiable format (e.g., SupportedFeaturedDataset) ($(typeof.(frames(Xs))) were used instead)." - end - end - Xs, backmap = zip([!isminifiable(X) ? minify(X) : (X, identity) for X in frames(Xs)]...) - Xs, backmap -end diff --git a/src/dimensional-datasets/datasets/check.jl b/src/datasets/base/check.jl similarity index 96% rename from src/dimensional-datasets/datasets/check.jl rename to src/datasets/base/check.jl index c261cd2..9d236c2 100644 --- a/src/dimensional-datasets/datasets/check.jl +++ b/src/datasets/base/check.jl @@ -1,8 +1,8 @@ @inline function check( - p::Proposition{<:FeatCondition}, - X::AbstractConditionalDataset{W}, + p::Proposition{<:ScalarCondition}, + X::AbstractScalarLogiset{W}, i_sample::Integer, w::W, ) where {W<:AbstractWorld} @@ -22,7 +22,7 @@ hasformula(memo_structure::AbstractDict{SyntaxTree}, φ::AbstractFormula) = hask function check( φ::SoleLogics.AbstractFormula, - X::AbstractConditionalDataset{W,<:AbstractCondition,<:Number,FR}, + X::AbstractLogiset{W,<:AbstractFeature,<:Number,FR}, i_sample::Integer; initialworld::Union{Nothing,W,AbstractVector{<:W}} = SoleLogics.initialworld(X, i_sample), # use_memo::Union{Nothing,AbstractVector{<:AbstractDict{<:F,<:T}}} = nothing, @@ -42,7 +42,7 @@ function check( end # forget_list = Vector{SoleLogics.FNode}() - # hasmemo(::AbstractActiveFeaturedDataset) = false + # hasmemo(::AbstractLogiset) = false # hasmemo(X)TODO # φ = normalize(φ; profile = :modelchecking) # TODO normalize formula and/or use a dedicate memoization structure that normalizes functions @@ -100,7 +100,7 @@ end # function compute_chained_threshold( # φ::SoleLogics.AbstractFormula, -# X::SupportedFeaturedDataset{V,W,FR}, +# X::SupportedScalarLogiset{V,W,FR}, # i_sample; # use_memo::Union{Nothing,AbstractVector{<:AbstractDict{F,T}}} = nothing, # ) where {V<:Number,W<:AbstractWorld,T<:Bool,FR<:AbstractMultiModalFrame{W,T},F<:SoleLogics.AbstractFormula} diff --git a/src/datasets/base/conditions.jl b/src/datasets/base/conditions.jl new file mode 100644 index 0000000..8789513 --- /dev/null +++ b/src/datasets/base/conditions.jl @@ -0,0 +1,35 @@ + +using SoleLogics: AbstractAlphabet +using Random +import SoleLogics: negation, propositions + +import Base: isequal, hash, in, isfinite, length + +""" + abstract type AbstractCondition{F<:AbstractFeature} end + +Abstract type for representing conditions that can be interpreted and evaluated +on worlds of instances of a logical dataset. In logical contexts, +these are wrapped into `Proposition`s. + +See also +[`Proposition`](@ref), +[`syntaxstring`](@ref), +[`ScalarMetaCondition`](@ref), +[`ScalarCondition`](@ref). +""" +abstract type AbstractCondition{F<:AbstractFeature} end + +function syntaxstring(c::AbstractCondition; kwargs...) + error("Please, provide method syntaxstring(::$(typeof(c)); kwargs...)." * + " Note that this value must be unique.") +end + +function Base.show(io::IO, c::AbstractCondition) + # print(io, "Feature of type $(typeof(c))\n\t-> $(syntaxstring(c))") + print(io, "$(typeof(c)): $(syntaxstring(c))") + # print(io, "$(syntaxstring(c))") +end + +Base.isequal(a::AbstractCondition, b::AbstractCondition) = syntaxstring(a) == syntaxstring(b) # nameof(x) == nameof(feature) +Base.hash(a::AbstractCondition) = Base.hash(syntaxstring(a)) diff --git a/src/conditional-data/features.jl b/src/datasets/base/features.jl similarity index 74% rename from src/conditional-data/features.jl rename to src/datasets/base/features.jl index 7d71fbc..4e2a260 100644 --- a/src/conditional-data/features.jl +++ b/src/datasets/base/features.jl @@ -1,18 +1,15 @@ import Base: isequal, hash, show import SoleLogics: syntaxstring -############################################################################################ -############################################################################################ -############################################################################################ - """ - abstract type AbstractFeature{U<:Real} end + abstract type AbstractFeature{U} end -Abstract type for features, representing a scalar functions that can be computed on a world. +Abstract type for features of worlds of +[Kripke structures](https://en.wikipedia.org/wiki/Kripke_structure_(model_checking). See also [`featvaltype`](@ref), [`computefeature`](@ref), [`AbstractWorld`](@ref). """ -abstract type AbstractFeature{U<:Real} end +abstract type AbstractFeature{U} end """ featvaltype(::Type{<:AbstractFeature{U}}) where {U} = U @@ -28,7 +25,7 @@ featvaltype(::AbstractFeature{U}) where {U} = U """ computefeature(f::AbstractFeature{U}, channel; kwargs...)::U where {U} -Compute a feature on a channel of an instance. +Compute a feature on a channel (i.e., world reading) of an instance. See also [`AbstractFeature`](@ref). """ @@ -54,6 +51,26 @@ Base.hash(a::AbstractFeature) = Base.hash(syntaxstring(a)) ############################################################################################ +""" + struct PropositionFeature{U} <: AbstractFeature{U} + name::String + end + +A feature that identifies a propositional letter. + +See also [`AbstractFeature`](@ref). +""" +struct PropositionFeature{U<:Proposition} <: AbstractFeature{U} + proposition::U +end +function computefeature(f::PropositionFeature{U}, channel; kwargs...) where {U} + error("Please, provide method computefeature(::$(typeof(f)), channel::$(typeof(channel)); kwargs...)::U.") +end + +featurename(f::PropositionFeature) = string(f.proposition) + +############################################################################################ + """ struct NamedFeature{U} <: AbstractFeature{U} name::String @@ -79,10 +96,10 @@ featurename(f::NamedFeature) = f.name fwd::Any end -A feature encoded explicitly as (a slice of) an FWD structure (see `AbstractFWD`). +A feature encoded explicitly as (a slice of) an FWD structure (see `AbstractFeatureLookupSet`). See also -[`AbstractFWD`](@ref), [`AbstractFeature`](@ref). +[`AbstractFeatureLookupSet`](@ref), [`AbstractFeature`](@ref). """ struct ExternalFWDFeature{U} <: AbstractFeature{U} name::String diff --git a/src/dimensional-datasets/datasets/generic-supporting-datasets.jl b/src/datasets/base/generic-supporting-datasets.jl similarity index 97% rename from src/dimensional-datasets/datasets/generic-supporting-datasets.jl rename to src/datasets/base/generic-supporting-datasets.jl index 9fb926f..6c94f1d 100644 --- a/src/dimensional-datasets/datasets/generic-supporting-datasets.jl +++ b/src/datasets/base/generic-supporting-datasets.jl @@ -14,7 +14,7 @@ struct GenericSupportingDataset{ end function GenericSupportingDataset( - fd :: FeaturedDataset{V,W,FR}, + fd :: Logiset{V,W,FR}, ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, nsamples(fd)) for i_sample in 1:nsamples(fd) @@ -56,7 +56,7 @@ struct ChainedFeaturedSupportingDataset{ end function ChainedFeaturedSupportingDataset( - fd :: FeaturedDataset{V,W,FR}, + fd :: Logiset{V,W,FR}, ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, nsamples(fd)) for i_sample in 1:nsamples(fd) diff --git a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl b/src/datasets/base/generic-supports.jl similarity index 95% rename from src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl rename to src/datasets/base/generic-supports.jl index 865f762..e048084 100644 --- a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl +++ b/src/datasets/base/generic-supports.jl @@ -16,7 +16,7 @@ struct GenericRelationalSupport{ new{V,W,FR,D}(d) end - function GenericRelationalSupport(fd::FeaturedDataset{V,W,FR}, perform_initialization = false) where {V,W,FR<:AbstractFrame{W,Bool}} + function GenericRelationalSupport(fd::Logiset{V,W,FR}, perform_initialization = false) where {V,W,FR<:AbstractFrame{W,Bool}} _nfeatsnaggrs = nfeatsnaggrs(fd) _fwd_rs = begin if perform_initialization @@ -76,7 +76,7 @@ struct GenericGlobalSupport{V,D<:AbstractArray{V,2}} <: AbstractGlobalSupport{V} GenericGlobalSupport{V,D}(d) end - function GenericGlobalSupport(fd::FeaturedDataset{V}) where {V} + function GenericGlobalSupport(fd::Logiset{V}) where {V} @assert worldtype(fd) != OneWorld "TODO adjust this note: note that you should not use a global support when not using global decisions" _nfeatsnaggrs = nfeatsnaggrs(fd) GenericGlobalSupport{V}(Array{V,2}(undef, nsamples(fd), _nfeatsnaggrs)) diff --git a/src/datasets/base/logiset-interface.jl b/src/datasets/base/logiset-interface.jl new file mode 100644 index 0000000..2080cbe --- /dev/null +++ b/src/datasets/base/logiset-interface.jl @@ -0,0 +1,123 @@ +using SoleLogics: AbstractKripkeStructure, AbstractInterpretationSet, AbstractFrame +using SoleLogics: TruthValue +import SoleLogics: alphabet, frame, check +import SoleLogics: accessibles, allworlds, nworlds, initialworld +import SoleLogics: worldtype, frametype + +""" + abstract type AbstractLogiset{ + W<:AbstractWorld, + F<:AbstractFeature, + T<:TruthValue, + FR<:AbstractFrame{W,T}, + } <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{F},T,FR}} end + +Abstract type for logisets, that is, logical datasets for +symbolic learning where each instance is a +[Kripke structure](https://en.wikipedia.org/wiki/Kripke_structure_(model_checking)) +on features. Conditions (see [`AbstractCondition`](@ref)), and logical formulas +with conditional letters can be checked on (worlds of) instances of the dataset. + +Logisets have an associated alphabet, set of features and set of relations. + +See also +[`AbstractCondition`](@ref), +[`AbstractFeature`](@ref), +[`AbstractKripkeStructure`](@ref), +[`AbstractInterpretationSet`](@ref). +""" +abstract type AbstractLogiset{ + W<:AbstractWorld, + V, + F<:AbstractFeature{V}, + T<:TruthValue, + FR<:AbstractFrame{W,T}, +} <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:F},T,FR}} end + +function evaluatecondition( + X::AbstractLogiset{W,V,F,T}, + i_sample, + w::W, + c::AbstractCondition, +)::T where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue} + error("Please, provide method evaluatecondition(X::$(typeof(X)), i_sample::$(typeof(i_sample)), w::$(typeof(w)), c::$(typeof(c))).") +end + +function check( + f::AbstractFormula, + X::AbstractLogiset{W,V,F,T}, + i_sample, + w::W, +)::T where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue} + # TODO implement once for all. + error("Please, provide method check(f::$(typeof(f)), X::$(typeof(X)), i_sample::$(typeof(i_sample)), w::$(typeof(w))).") +end + +function displaystructure(X::AbstractLogiset; kwargs...)::String + error("Please, provide method displaystructure(X::$(typeof(X)); kwargs...)::String.") +end + +function check( + p::Proposition{A}, + X::AbstractLogiset{W,V,F,T}, + i_sample, + w::W, +)::T where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue,A<:AbstractCondition} + cond = atom(p) + evaluatecondition(X, i_sample, w, cond) +end + +function Base.show(io::IO, X::AbstractLogiset; kwargs...) + println(io, displaystructure(X; kwargs...)) +end + +############################################################################################ + +featvaltype(::Type{<:AbstractLogiset{W,V}}) where {W<:AbstractWorld,V} = V +featvaltype(d::AbstractLogiset) = featvaltype(typeof(d)) + +featuretype(::Type{<:AbstractLogiset{W,V,F}}) where {W<:AbstractWorld,V,F<:AbstractFeature} = F +featuretype(d::AbstractLogiset) = featuretype(typeof(d)) + +function features(X::AbstractLogiset) + return error("Please, provide method features(::$(typeof(X))).") +end + +function featvalue( + X::AbstractLogiset{W}, + i_sample, + w::W, + f::AbstractFeature, +) where {W<:AbstractWorld} + error("Please, provide method featvalue(::$(typeof(X)), i_sample::$(typeof(i_sample)), w::$(typeof(w)), f::$(typeof(f))).") +end + +isminifiable(::AbstractLogiset) = false + +function initialworld( + X::AbstractLogiset{W,V,F,T}, + i_sample +) where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue} + error("Please, provide method initialworld(::$(typeof(X)), i_sample::$(typeof(i_sample))).") +end + +representatives(X::AbstractLogiset, i_sample, args...) = representatives(frame(X, i_sample), args...) + +############################################################################################ +# Helpers +############################################################################################ + +function findfeature(X::AbstractLogiset, feature::AbstractFeature) + id = findfirst(x->(Base.isequal(x, feature)), features(X)) + if isnothing(id) + error("Could not find feature $(feature) in AbstractLogiset of type $(typeof(X)).") + end + id +end +function findrelation(X::AbstractLogiset, relation::AbstractRelation) + id = findfirst(x->x==relation, relations(X)) + if isnothing(id) + error("Could not find relation $(relation) in AbstractLogiset of type $(typeof(X)).") + end + id +end diff --git a/src/datasets/base/logiset.jl b/src/datasets/base/logiset.jl new file mode 100644 index 0000000..0dc8f0f --- /dev/null +++ b/src/datasets/base/logiset.jl @@ -0,0 +1,273 @@ + +""" + abstract type AbstractFeatureLookupSet{V,FR<:AbstractFrame} end + +Abstract type for feature lookup tables. +Structures of this type provide a feature value for each world of each instance +of a logiset. More specifically, if `featstruct isa AbstractFeatureLookupSet`, then +`featstruct[i, w, f]` is the value of feature `f` on world `w` on the `i`-th instance of the +dataset. + +See also +[`featvalue`](@ref). +""" +abstract type AbstractFeatureLookupSet{V,FR<:AbstractFrame} end + +featvaltype(::Type{<:AbstractFeatureLookupSet{V}}) where {V} = V +featvaltype(d::AbstractFeatureLookupSet) = featvaltype(typeof(d)) + +""" + @inline function featvalue( + featstruct :: AbstractFeatureLookupSet{V,FR}, + i_sample :: Integer, + w :: W, + feature :: F, + ) where {V,F<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} + +Return the feature value for `f` at world `w` on the `i`-th instance. + +See also +[`AbstractFeatureLookupSet`](@ref). +""" +function featvalue( + featstruct :: AbstractFeatureLookupSet{V,FR}, + i_sample :: Integer, + w :: W, + feature :: F, +)::V where {V,F<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} + error("Please, provide method featvalue(::$(typeof(featstruct)), i_sample::$(typeof(i_sample)), w::$(typeof(w)), feature::$(typeof(feature)))::$(V).") +end + +""" + @inline function featvalue( + featstruct :: AbstractFeatureLookupSet{V,FR}, + i_sample :: Integer, + w :: W, + i_feature :: Integer, + ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} + +Return the value for the `i_feature`-th at world `w` on the `i`-th instance. + +See also +[`AbstractFeatureLookupSet`](@ref). +""" +function featvalue( + featstruct :: AbstractFeatureLookupSet{V,FR}, + i_sample :: Integer, + w :: W, + i_feature :: Integer, +)::V where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} + error("Please, provide method featvalue(::$(typeof(featstruct)), i_sample::$(typeof(i_sample)), w::$(typeof(w)), i_feature::$(typeof(i_feature)))::$(V).") +end + +@inline Base.getindex(featstruct::AbstractFeatureLookupSet, args...) = featvalue(featstruct, args...) + +############################################################################################ +############################################################################################ +############################################################################################ + +# # The most generic featstruct structure is a matrix of dictionaries of size (nsamples × nfeatures) +# struct GenericFWD{ +# V, +# F<:AbstractFeature{V}, +# W<:AbstractWorld, +# FR<:AbstractFrame{W}, +# D<:AbstractVector{<:AbstractDict{<:W,<:AbstractVector{<:V}}} +# } <: AbstractFeatureLookupSet{V,F,FR} +# d :: D +# nfeatures :: Integer +# end + +# Base.size(featstruct::GenericFWD{V}, args...) where {V} = size(featstruct.d, args...) +# nsamples(featstruct::GenericFWD{V}) where {V} = size(featstruct, 1) +# nfeatures(featstruct::GenericFWD{V}) where {V} = featstruct.d + +# # The matrix is initialized with #undef values +# function fwd_init(::Type{GenericFWD}, X::AbstractLogiset{W,V}) where {W,V} +# d = Array{Dict{W,V}, 2}(undef, nsamples(X)) +# for i in 1:nsamples +# d[i] = Dict{W,Array{V,1}}() +# end +# GenericFWD{V}(d, nfeatures(X)) +# end + +# # A function for initializing individual world slices +# function fwd_init_world_slice(featstruct::GenericFWD{V}, i_sample::Integer, w::AbstractWorld) where {V} +# featstruct.d[i_sample][w] = Array{V,1}(undef, featstruct.nfeatures) +# end + +# # A function for getting a threshold value from the lookup table +# Base.@propagate_inbounds @inline featvalue( +# featstruct :: GenericFWD{V}, +# i_sample :: Integer, +# w :: AbstractWorld, +# i_feature :: Integer) where {V} = featstruct.d[i_sample][w][i_feature] + +# # A function for setting a threshold value in the lookup table +# Base.@propagate_inbounds @inline function fwd_set(featstruct::GenericFWD{V}, w::AbstractWorld, i_sample::Integer, i_feature::Integer, threshold::V) where {V} +# featstruct.d[i_sample][w][i_feature] = threshold +# end + +# # A function for setting threshold values for a single feature (from a feature slice, experimental) +# Base.@propagate_inbounds @inline function fwd_set_feature(featstruct::GenericFWD{V}, i_feature::Integer, fwdslice::Any) where {V} +# throw_n_log("Warning! fwd_set_feature with GenericFWD is not yet implemented!") +# for ((i_sample,w),threshold::V) in read_fwdslice(fwdslice) +# featstruct.d[i_sample][w][i_feature] = threshold +# end +# end + +# # A function for slicing the dataset +# function _slice_dataset(featstruct::GenericFWD{V}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {V} +# GenericFWD{V}(if return_view == Val(true) @view featstruct.d[inds] else featstruct.d[inds] end, featstruct.nfeatures) +# end + +# Others... +# Base.@propagate_inbounds @inline fwdread_channeaoeu(featstruct::GenericFWD{V}, i_sample::Integer, i_feature::Integer) where {V} = TODO +# const GenericFeaturedChannel{V} = TODO +# fwd_channel_interpret_world(fwc::GenericFeaturedChannel{V}, w::AbstractWorld) where {V} = TODO + +# isminifiable(::AbstractFeatureLookupSet) = true + +# function minify(featstruct::AbstractFeatureLookupSet) +# minify(featstruct.d) #TODO improper +# end + +""" +Basic structure for representing active logisets, that is, logical datasets with features. ... TODO +It stores the feature values in a lookup table. + + +""" +struct Logiset{ + V, + W<:AbstractWorld, + FR<:AbstractFrame{W,Bool}, + F<:AbstractFeature{V}, + FWD<:AbstractFeatureLookupSet{V,FR}, +} <: AbstractLogiset{W,V,F,Bool,FR} + + # Feature lookup structure + featstruct :: FWD + + # Features + features :: Vector{F} + + # Accessibility relations + relations :: Vector{<:AbstractRelation} + + # Initial world(s) + initialworld :: Union{Nothing,W,AbstractWorldSet{<:W}} + + function Logiset{V,W,FR,F,FWD}( + featstruct :: FWD, + features :: AbstractVector{F}, + relations :: AbstractVector{<:AbstractRelation}, + ; + allow_no_instances = false, + initialworld = nothing, + ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FWD<:AbstractFeatureLookupSet{V,FR},F<:AbstractFeature{V}} + features = collect(features) + ty = "Logiset{$(V),$(W),$(FR),$(F)}" + @assert allow_no_instances || nsamples(featstruct) > 0 "Can't instantiate $(ty) with no instance. (featstruct's type $(typeof(featstruct)))" + @assert nfeatures(featstruct) == length(features) "Can't instantiate $(ty) with different numbers of instances $(nsamples(featstruct)) and of features $(length(features))." + check_initialworld(Logiset, initialworld, W) + new{ + V, + W, + FR, + F, + FWD, + }( + featstruct, + features, + relations, + initialworld, + ) + end + + function Logiset{V,W,FR}( + featstruct :: FWD, + features :: AbstractVector{<:AbstractFeature}, + args...; + kwargs... + ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FWD<:AbstractFeatureLookupSet{V,FR}} + features = collect(features) + F = Union{typeof.(features)...} + features = Vector{F}(features) + Logiset{V,W,FR,F,FWD}(featstruct, features, args...; kwargs...) + end + + function Logiset{V,W}( + featstruct :: AbstractFeatureLookupSet{V,FR}, + args...; + kwargs... + ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} + Logiset{V,W,FR}(featstruct, args...; kwargs...) + end + + function Logiset( + featstruct :: AbstractFeatureLookupSet{V,FR}, + args...; + kwargs..., + ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} + Logiset{V,W}(featstruct, args...; kwargs...) + end +end + + +@inline function Base.getindex(X::Logiset{V,W}, args...) where {V,W<:AbstractWorld} + Base.getindex(featstruct(X), args...)::V +end + +@inline function featvalue(X::Logiset{V,W}, args...) where {V,W<:AbstractWorld} + featvalue(featstruct(X), args...)::V +end + +Base.size(X::Logiset) = Base.size(featstruct(X)) + +featstruct(X::Logiset) = X.featstruct +relations(X::Logiset) = X.relations +features(X::Logiset) = X.features + +nfeatures(X::Logiset) = length(features(X)) +nrelations(X::Logiset) = length(relations(X)) +nsamples(X::Logiset) = nsamples(featstruct(X)) +worldtype(X::Logiset{V,W}) where {V,W<:AbstractWorld} = W + +frame(X::Logiset, i_sample) = frame(featstruct(X), i_sample) +initialworld(X::Logiset) = X.initialworld +function initialworld(X::Logiset, i_sample) + initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_sample] : initialworld(X) +end + +function _slice_dataset(X::Logiset, inds::AbstractVector{<:Integer}, args...; kwargs...) + Logiset( + _slice_dataset(featstruct(X), inds, args...; kwargs...), + features(X), + relations(X), + initialworld = initialworld(X) + ) +end + +function displaystructure(X::Logiset; indent_str = "") + out = "$(typeof(X))\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" + out *= indent_str * "├ features:\t\t$((length(features(X))))\t$(features(X))\n" + out *= indent_str * "├ relations:\t\t$((length(relations(X))))\t$(relations(X))\n" + out *= indent_str * "├ featstruct:\t\t$(typeof(featstruct(X)))\t$(Base.summarysize(featstruct(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" + out *= indent_str * "└ initialworld(s)\t$(initialworld(X))" + out +end + +hasnans(X::Logiset) = hasnans(featstruct(X)) + +isminifiable(::Logiset) = true + +function minify(X::Logiset) + new_fwd, backmap = minify(featstruct(X)) + X = Logiset( + new_fwd, + features(X), + relations(X), + ) + X, backmap +end diff --git a/src/datasets/base/main.jl b/src/datasets/base/main.jl new file mode 100644 index 0000000..926a261 --- /dev/null +++ b/src/datasets/base/main.jl @@ -0,0 +1,51 @@ +import SoleBase: frame + +using SoleLogics: OneWorld, Interval, Interval2D +using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame +using SoleLogics: X, Y, Z +using SoleLogics: AbstractWorld, IdentityRel +import SoleLogics: syntaxstring + +import SoleData: nsamples, nfeatures +import SoleData: nframes, frames, hasnans, _slice_dataset + +# Features to be computed on worlds of dataset instances +include("features.jl") + +# Conditions on the features, to be wrapped in Proposition's +include("conditions.jl") + +export check, accessibles, allworlds, representatives, initialworld + +# Interface for representative accessibles, for optimized model checking on specific frames +include("representatives.jl") + +# Logical datasets, where the instances are Kripke models with conditional alphabets +include("logiset-interface.jl") + +# Logical dataset based on a lookup +include("logiset.jl") + +# include("supported-logiset.jl") + +# export nframes, frames, frame, +# displaystructure, +# MultiFrameLogiset, +# worldtypes + +# # Multiframe version of logisets, for representing multimodal datasets +# include("multiframe-logiset.jl") # TODO define interface + +# const ActiveMultiFrameLogiset = MultiFrameLogiset{<:AbstractLogiset} +# const ActiveMultiFrameScalarLogiset = MultiFrameScalarLogiset{<:AbstractActiveScalarLogiset} + + + +# include("generic-supporting-datasets.jl") +# include("generic-supports.jl") + + +# # +# # TODO figure out which convert function works best: +# convert(::Type{<:MultiFrameLogiset{T}}, X::MD) where {T,MD<:AbstractLogiset{T}} = MultiFrameLogiset{MD}([X]) +# convert(::Type{<:MultiFrameLogiset}, X::AbstractLogiset) = MultiFrameLogiset([X]) diff --git a/src/datasets/base/multiframe-logiset.jl b/src/datasets/base/multiframe-logiset.jl new file mode 100644 index 0000000..29b0c0c --- /dev/null +++ b/src/datasets/base/multiframe-logiset.jl @@ -0,0 +1,94 @@ +""" + struct MultiFrameLogiset{MD<:AbstractLogiset} + frames :: Vector{<:MD} + end + +A multiframe logical dataset; this structure is useful for representing +multimodal datasets in logical terms. + +See also +[`AbstractLogiset`](@ref), +[`minify`](@ref). +""" +struct MultiFrameLogiset{MD<:AbstractLogiset} + + frames :: Vector{<:MD} + + function MultiFrameLogiset{MD}(X::MultiFrameLogiset{MD}) where {MD<:AbstractLogiset} + MultiFrameLogiset{MD}(X.frames) + end + function MultiFrameLogiset{MD}(Xs::AbstractVector) where {MD<:AbstractLogiset} + Xs = collect(Xs) + @assert length(Xs) > 0 && length(unique(nsamples.(Xs))) == 1 "Can't create an empty MultiFrameLogiset or with mismatching number of samples (nframes: $(length(Xs)), frame_sizes: $(nsamples.(Xs)))." + new{MD}(Xs) + end + function MultiFrameLogiset{MD}() where {MD<:AbstractLogiset} + new{MD}(MD[]) + end + function MultiFrameLogiset{MD}(X::MD) where {MD<:AbstractLogiset} + MultiFrameLogiset{MD}(MD[X]) + end + function MultiFrameLogiset(Xs::AbstractVector{<:MD}) where {MD<:AbstractLogiset} + MultiFrameLogiset{MD}(Xs) + end + function MultiFrameLogiset(X::MD) where {MD<:AbstractLogiset} + MultiFrameLogiset{MD}(X) + end +end + +frames(X::MultiFrameLogiset) = X.frames + +Base.iterate(X::MultiFrameLogiset, state=1) = state > length(X) ? nothing : (get_instance(X, state), state+1) +Base.length(X::MultiFrameLogiset) = nsamples(X) +Base.push!(X::MultiFrameLogiset, f::AbstractLogiset) = push!(frames(X), f) + +Base.size(X::MultiFrameLogiset) = map(size, frames(X)) + +frame(X::MultiFrameLogiset, i_frame::Integer) = nframes(X) > 0 ? frames(X)[i_frame] : error("MultiFrameLogiset has no frame!") +nframes(X::MultiFrameLogiset) = length(frames(X)) +nsamples(X::MultiFrameLogiset) = nsamples(frame(X, 1)) + +# max_channel_size(X::MultiFrameLogiset) = map(max_channel_size, frames(X)) # TODO: figure if this is useless or not. Note: channel_size doesn't make sense at this point. +nfeatures(X::MultiFrameLogiset) = map(nfeatures, frames(X)) # Note: used for safety checks in fit_tree.jl +# nrelations(X::MultiFrameLogiset) = map(nrelations, frames(X)) # TODO: figure if this is useless or not +nfeatures(X::MultiFrameLogiset, i_frame::Integer) = nfeatures(frame(X, i_frame)) +nrelations(X::MultiFrameLogiset, i_frame::Integer) = nrelations(frame(X, i_frame)) +worldtype(X::MultiFrameLogiset, i_frame::Integer) = worldtype(frame(X, i_frame)) +worldtypes(X::MultiFrameLogiset) = Vector{Type{<:AbstractWorld}}(worldtype.(frames(X))) + +get_instance(X::MultiFrameLogiset, i_frame::Integer, idx_i::Integer, args...) = get_instance(frame(X, i_frame), idx_i, args...) +# get_instance(X::MultiFrameLogiset, idx_i::Integer, args...) = get_instance(frame(X, i), idx_i, args...) # TODO should slice across the frames! + +_slice_dataset(X::MultiFrameLogiset{MD}, inds::AbstractVector{<:Integer}, args...; kwargs...) where {MD<:AbstractLogiset} = + MultiFrameLogiset{MD}(Vector{MD}(map(frame->_slice_dataset(frame, inds, args...; kwargs...), frames(X)))) + +function displaystructure(Xs::MultiFrameLogiset; indent_str = "") + out = "$(typeof(Xs))" # * "\t\t\t$(Base.summarysize(Xs) / 1024 / 1024 |> x->round(x, digits=2)) MBs" + for (i_frame, X) in enumerate(frames(Xs)) + if i_frame == nframes(Xs) + out *= "\n$(indent_str)└ " + else + out *= "\n$(indent_str)├ " + end + out *= "[$(i_frame)] " + # \t\t\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t(worldtype: $(worldtype(X)))" + out *= displaystructure(X; indent_str = indent_str * (i_frame == nframes(Xs) ? " " : "│ ")) * "\n" + end + out +end + +hasnans(Xs::MultiFrameLogiset) = any(hasnans.(frames(Xs))) + +isminifiable(::MultiFrameLogiset) = true + +function minify(Xs::MultiFrameLogiset) + if !any(map(isminifiable, frames(Xs))) + if !all(map(isminifiable, frames(Xs))) + @error "Cannot perform minification with frames of types $(typeof.(frames(Xs))). Please use a minifiable format (e.g., SupportedScalarLogiset)." + else + @warn "Cannot perform minification on some of the frames provided. Please use a minifiable format (e.g., SupportedScalarLogiset) ($(typeof.(frames(Xs))) were used instead)." + end + end + Xs, backmap = zip([!isminifiable(X) ? minify(X) : (X, identity) for X in frames(Xs)]...) + Xs, backmap +end diff --git a/src/datasets/base/representatives.jl b/src/datasets/base/representatives.jl new file mode 100644 index 0000000..d5764b0 --- /dev/null +++ b/src/datasets/base/representatives.jl @@ -0,0 +1,37 @@ +using SoleLogics: AbstractMultiModalFrame, AbstractRelation, accessibles + +""" + function representatives( + fr::AbstractMultiModalFrame{W}, + S::W, + ::AbstractRelation, + ::AbstractCondition + ) where {W<:AbstractWorld} + +Return an iterator to the (few) *representative* accessible worlds that are +really necessary, upon collation, for computing and propagating truth values +through existential modal operators. + +This allows for some optimizations when model checking specific conditional +formulas. For example, with scalar conditions, +it turns out that when you need to test a formula "⟨L⟩ +(MyFeature ≥ 10)" on a world w, instead of computing "MyFeature" on all worlds +and then maximizing, computing it on a single world is enough to decide the +truth. A few cases arise depending on the relation, the feature and the test +operator (or, better, its *aggregator*). + +Note that this method fallsback to `accessibles`. + +See also +[`accessibles`](@ref), +[`ScalarCondition`](@ref), +[`AbstractMultiModalFrame`](@ref). +""" +function representatives( # Dispatch on feature/aggregator pairs + fr::AbstractMultiModalFrame{W}, + w::W, + r::AbstractRelation, + mc::AbstractCondition +) where {W<:AbstractWorld} + accessibles(fr, w, r) +end diff --git a/src/datasets/base/supported-logiset.jl b/src/datasets/base/supported-logiset.jl new file mode 100644 index 0000000..1e8a07f --- /dev/null +++ b/src/datasets/base/supported-logiset.jl @@ -0,0 +1,58 @@ + +############################################################################################ +# Explicit modal dataset with support +########################################################################################### + +# The lookup table (fwd) in a featured modal dataset provides a quick answer on the truth of +# propositional decisions; as for answering modal decisions (e.g., ⟨L⟩ (minimum(A2) ≥ 10) ) +# with an fwd, one must enumerate the accessible worlds, compute the truth on each world, +# and aggregate the answer (by means of all/any). This process is costly; instead, it is +# sometimes more convenient to initially spend more time computing the truth of any decision, +# and store this information in a *support* lookup table. Similarly, one can decide to deploy +# memoization on this table (instead of computing everything at the beginning, compute it on +# the fly and store it for later calls). +# +# We define an abstract type for explicit modal dataset with support lookup tables +# remove: abstract type ExplicitModalDatasetWithSupport{V,W,FR} <: AbstractActiveScalarLogiset{W,V,FT,Bool,FR} end +# And an abstract type for support lookup tables +abstract type AbstractSupport{V,W} end + +function nonnothingshare(support::AbstractSupport) + (isinf(capacity(support)) ? NaN : nmemoizedvalues(support)/capacity(support)) +end +# +# In general, one can use lookup (with or without memoization) for any decision, even the +# more complex ones, for example: +# ⟨G⟩ (minimum(A2) ≥ 10 ∧ (⟨O⟩ (maximum(A3) > 2) ∨ (minimum(A1) < 0))) +# +# In practice, decision trees only ask about simple decisions such as ⟨L⟩ (minimum(A2) ≥ 10), +# or ⟨G⟩ (maximum(A2) ≤ 50). Because the global operator G behaves differently from other +# relations, it is natural to differentiate between global and relational support tables: +# +abstract type AbstractRelationalSupport{V,W,FR<:AbstractFrame} <: AbstractSupport{V,W} end +abstract type AbstractGlobalSupport{V} <: AbstractSupport{V,W where W<:AbstractWorld} end +# +# Be an *fwd_rs* an fwd relational support, and a *fwd_gs* an fwd global support, +# for simple support tables like these, it is convenient to store, again, modal *gamma* values. +# Similarly to fwd, gammas are basically values on the verge of truth, that can straightforwardly +# anser simple modal questions. +# Consider the decision (w ⊨ f ⋈ a) on the i-th instance, for a given feature f, +# world w, relation R and test operator ⋈, and let gamma (γ) be: +# - fwd_rs[i, f, a, R, w] if R is a regular relation, or +# - fwd_gs[i, f, a] if R is the global relation G, +# where a = aggregator(⋈). In this context, γ is the unique value for which w ⊨ f ⋈ γ holds and: +# - if aggregator(⋈) = minimum: ∀ a > γ: (w ⊨ f ⋈ a) does not hold +# - if aggregator(⋈) = maximum: ∀ a < γ: (w ⊨ f ⋈ a) does not hold +# +# Let us define the world type-agnostic implementations for fwd_rs and fwd_gs (note that any fwd_gs +# is actually inherently world agnostic); world type-specific implementations can be defined +# in a similar way. + +############################################################################################ +############################################################################################ + +isminifiable(::Union{AbstractRelationalSupport,AbstractGlobalSupport}) = true + +function minify(support::Union{AbstractRelationalSupport,AbstractGlobalSupport}) + minify(support.d) #TODO improper +end diff --git a/src/dimensional-datasets/datasets/dimensional-fwds.jl b/src/datasets/dimensional-datasets/datasets/dimensional-fwds.jl similarity index 92% rename from src/dimensional-datasets/datasets/dimensional-fwds.jl rename to src/datasets/dimensional-datasets/datasets/dimensional-fwds.jl index 6c6e413..3b1b4e8 100644 --- a/src/dimensional-datasets/datasets/dimensional-fwds.jl +++ b/src/datasets/dimensional-datasets/datasets/dimensional-fwds.jl @@ -6,7 +6,7 @@ import Base: size, ndims, getindex, setindex! ############################################################################################ ############################################################################################ -abstract type AbstractUniformFullDimensionalFWD{T,N,W<:AbstractWorld,FR<:FullDimensionalFrame{N,W,Bool}} <: AbstractFWD{T,W,FR} end +abstract type AbstractUniformFullDimensionalFWD{T,N,W<:AbstractWorld,FR<:FullDimensionalFrame{N,W,Bool}} <: AbstractFeatureLookupSet{T,W,FR} end channel_size(fwd::AbstractUniformFullDimensionalFWD) = error("TODO add message inviting to add channel_size") frame(fwd::AbstractUniformFullDimensionalFWD, i_sample) = FullDimensionalFrame(channel_size(fwd)) @@ -19,11 +19,21 @@ struct UniformFullDimensionalFWD{ W<:AbstractWorld, N, D<:AbstractArray{T}, - FR<:FullDimensionalFrame{N,W,Bool} + FR<:FullDimensionalFrame{N,W,Bool}, + G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, + G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, } <: AbstractUniformFullDimensionalFWD{T,N,W,FR} d :: D + # Features + features :: Vector{FT} + + # Test operators associated with each feature, grouped by their respective aggregator + grouped_featsaggrsnops :: G1 + + grouped_featsnaggrs :: G2 + function UniformFullDimensionalFWD{T,W,N,D,FR}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{T},FR<:FullDimensionalFrame{N,W,Bool}} new{T,W,N,D,FR}(d) end @@ -34,13 +44,13 @@ struct UniformFullDimensionalFWD{ ############################################################################################ - function UniformFullDimensionalFWD(X::DimensionalFeaturedDataset{T,N,W}) where {T,N,W<:OneWorld} + function UniformFullDimensionalFWD(X::DimensionalLogiset{T,N,W}) where {T,N,W<:OneWorld} UniformFullDimensionalFWD{T,W,N}(Array{T,2}(undef, nsamples(X), nfeatures(X))) end - function UniformFullDimensionalFWD(X::DimensionalFeaturedDataset{T,N,W}) where {T,N,W<:Interval} + function UniformFullDimensionalFWD(X::DimensionalLogiset{T,N,W}) where {T,N,W<:Interval} UniformFullDimensionalFWD{T,W,N}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, nsamples(X), nfeatures(X))) end - function UniformFullDimensionalFWD(X::DimensionalFeaturedDataset{T,N,W}) where {T,N,W<:Interval2D} + function UniformFullDimensionalFWD(X::DimensionalLogiset{T,N,W}) where {T,N,W<:Interval2D} UniformFullDimensionalFWD{T,W,N}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, nsamples(X), nfeatures(X))) end @@ -190,11 +200,11 @@ fwd_channel_interpret_world(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) wh fwc[w.x.x, w.x.y, w.y.x, w.y.y] const FWDFeatureSlice{T} = Union{ - # FWDFeatureSlice(DimensionalFeaturedDataset{T where T,0,OneWorld}) + # FWDFeatureSlice(DimensionalLogiset{T where T,0,OneWorld}) T, # Note: should be, but it throws error OneWorldFeaturedChannel{T}, IntervalFeaturedChannel{T}, Interval2DFeaturedChannel{T}, - # FWDFeatureSlice(DimensionalFeaturedDataset{T where T,2,<:Interval2D}) + # FWDFeatureSlice(DimensionalLogiset{T where T,2,<:Interval2D}) } @@ -206,7 +216,7 @@ const FWDFeatureSlice{T} = Union{ # nsamples(fwd::OneWorldFWD) = size(fwd.d, 1) # nfeatures(fwd::OneWorldFWD) = size(fwd.d, 2) -# function fwd_init(::Type{OneWorldFWD}, X::DimensionalFeaturedDataset{T,0,OneWorld}) where {T} +# function fwd_init(::Type{OneWorldFWD}, X::DimensionalLogiset{T,0,OneWorld}) where {T} # OneWorldFWD{T}(Array{T,2}(undef, nsamples(X), nfeatures(X))) # end @@ -216,7 +226,7 @@ const FWDFeatureSlice{T} = Union{ # hasnans(fwd::OneWorldFWD) = any(_isnan.(fwd.d)) -# Base.@propagate_inbounds @inline fwdread( +# Base.@propagate_inbounds @inline featvalue( # fwd :: OneWorldFWD{T}, # i_sample :: Integer, # w :: OneWorld, @@ -252,7 +262,7 @@ const FWDFeatureSlice{T} = Union{ # nsamples(fwd::IntervalFWD) = size(fwd, 3) # nfeatures(fwd::IntervalFWD) = size(fwd, 4) -# function fwd_init(::Type{IntervalFWD}, X::DimensionalFeaturedDataset{T,1,<:Interval}) where {T} +# function fwd_init(::Type{IntervalFWD}, X::DimensionalLogiset{T,1,<:Interval}) where {T} # IntervalFWD{T}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, nsamples(X), nfeatures(X))) # end @@ -265,7 +275,7 @@ const FWDFeatureSlice{T} = Union{ # any([hasnans(fwd.d[x,y,:,:]) for x in 1:size(fwd, 1) for y in (x+1):size(fwd, 2)]) # end -# Base.@propagate_inbounds @inline fwdread( +# Base.@propagate_inbounds @inline featvalue( # fwd :: IntervalFWD{T}, # i_sample :: Integer, # w :: Interval, @@ -302,7 +312,7 @@ const FWDFeatureSlice{T} = Union{ # nfeatures(fwd::Interval2DFWD) = size(fwd, 6) -# function fwd_init(::Type{Interval2DFWD}, X::DimensionalFeaturedDataset{T,2,<:Interval2D}) where {T} +# function fwd_init(::Type{Interval2DFWD}, X::DimensionalLogiset{T,2,<:Interval2D}) where {T} # Interval2DFWD{T}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, nsamples(X), nfeatures(X))) # end @@ -315,7 +325,7 @@ const FWDFeatureSlice{T} = Union{ # any([hasnans(fwd.d[xx,xy,yx,yy,:,:]) for xx in 1:size(fwd, 1) for xy in (xx+1):size(fwd, 2) for yx in 1:size(fwd, 3) for yy in (yx+1):size(fwd, 4)]) # end -# Base.@propagate_inbounds @inline fwdread( +# Base.@propagate_inbounds @inline featvalue( # fwd :: Interval2DFWD{T}, # i_sample :: Integer, # w :: Interval2D, diff --git a/src/dimensional-datasets/datasets/dimensional-featured-dataset.jl b/src/datasets/dimensional-datasets/datasets/dimensional-logiset.jl similarity index 74% rename from src/dimensional-datasets/datasets/dimensional-featured-dataset.jl rename to src/datasets/dimensional-datasets/datasets/dimensional-logiset.jl index 74b834a..d7e0018 100644 --- a/src/dimensional-datasets/datasets/dimensional-featured-dataset.jl +++ b/src/datasets/dimensional-datasets/datasets/dimensional-logiset.jl @@ -9,7 +9,7 @@ # ############################################################################################ -struct DimensionalFeaturedDataset{ +struct DimensionalLogiset{ V<:Number, N, W<:AbstractWorld, @@ -17,7 +17,7 @@ struct DimensionalFeaturedDataset{ FT<:AbstractFeature{V}, G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, -} <: AbstractActiveFeaturedDataset{V,W,FullDimensionalFrame{N,W,Bool},FT} +} <: AbstractActiveScalarLogiset{W,V,FT,Bool,FullDimensionalFrame{N,W,Bool}} # Core data (a dimensional domain) domain :: D @@ -25,9 +25,6 @@ struct DimensionalFeaturedDataset{ # Worlds & Relations ontology :: Ontology{W} # Union{Nothing,} - # Features - features :: Vector{FT} - # Test operators associated with each feature, grouped by their respective aggregator # Note: currently, cannot specify the full type (probably due to @computed) grouped_featsaggrsnops :: G1 @@ -40,7 +37,7 @@ struct DimensionalFeaturedDataset{ ######################################################################################## - function DimensionalFeaturedDataset{V,N,W}( + function DimensionalLogiset{V,N,W}( domain::PassiveDimensionalDataset{N}, ontology::Ontology{W}, features::AbstractVector{<:AbstractFeature}, @@ -48,7 +45,7 @@ struct DimensionalFeaturedDataset{ allow_no_instances = false, initialworld = nothing, ) where {V,N,W<:AbstractWorld} - ty = "DimensionalFeaturedDataset{$(V),$(N),$(W)}" + ty = "DimensionalLogiset{$(V),$(N),$(W)}" features = collect(features) FT = Union{typeof.(features)...} features = Vector{FT}(features) @@ -63,7 +60,7 @@ struct DimensionalFeaturedDataset{ sum(vcat([[length(test_ops) for test_ops in aggrs] for aggrs in grouped_featsaggrsnops]...)) > 0 "" * "Can't instantiate $(ty) with no test operator: $(grouped_featsaggrsnops)" grouped_featsnaggrs = features_grouped_featsaggrsnops2grouped_featsnaggrs(features, grouped_featsaggrsnops) - check_initialworld(DimensionalFeaturedDataset, initialworld, W) + check_initialworld(DimensionalLogiset, initialworld, W) new{ V, N, @@ -84,7 +81,7 @@ struct DimensionalFeaturedDataset{ ######################################################################################## - function DimensionalFeaturedDataset{V,N,W}( + function DimensionalLogiset{V,N,W}( domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, ontology :: Ontology{W}, features :: AbstractVector{<:AbstractFeature}, @@ -93,10 +90,10 @@ struct DimensionalFeaturedDataset{ ) where {V,N,W<:AbstractWorld} domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{N,W}(domain) : domain) grouped_featsaggrsnops = grouped_featsnops2grouped_featsaggrsnops(grouped_featsnops) - DimensionalFeaturedDataset{V,N,W}(domain, ontology, features, grouped_featsaggrsnops; kwargs...) + DimensionalLogiset{V,N,W}(domain, ontology, features, grouped_featsaggrsnops; kwargs...) end - function DimensionalFeaturedDataset{V,N,W}( + function DimensionalLogiset{V,N,W}( domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, ontology :: Ontology{W}, mixed_features :: AbstractVector; @@ -165,34 +162,34 @@ struct DimensionalFeaturedDataset{ end _features, featsnops end - DimensionalFeaturedDataset{V,N,worldtype(ontology)}(domain, ontology, _features, featsnops; kwargs...) + DimensionalLogiset{V,N,worldtype(ontology)}(domain, ontology, _features, featsnops; kwargs...) end ######################################################################################## - function DimensionalFeaturedDataset{V,N}( + function DimensionalLogiset{V,N}( domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, ontology :: Ontology{W}, args...; kwargs..., ) where {V,N,W<:AbstractWorld} domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{N,W}(domain) : domain) - DimensionalFeaturedDataset{V,N,W}(domain, ontology, args...; kwargs...) + DimensionalLogiset{V,N,W}(domain, ontology, args...; kwargs...) end ######################################################################################## - function DimensionalFeaturedDataset{V}( + function DimensionalLogiset{V}( domain :: Union{PassiveDimensionalDataset,AbstractDimensionalDataset}, args...; kwargs..., ) where {V} - DimensionalFeaturedDataset{V,dimensionality(domain)}(domain, args...; kwargs...) + DimensionalLogiset{V,dimensionality(domain)}(domain, args...; kwargs...) end ######################################################################################## - function DimensionalFeaturedDataset( + function DimensionalLogiset( domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, ontology :: Ontology{W}, features :: AbstractVector{<:AbstractFeature}, @@ -200,7 +197,7 @@ struct DimensionalFeaturedDataset{ kwargs..., ) where {N,W<:AbstractWorld} V = Union{featvaltype.(features)...} - DimensionalFeaturedDataset{V}(domain, ontology, features, args...; kwargs...) + DimensionalLogiset{V}(domain, ontology, features, args...; kwargs...) end preserves_type(::Any) = false @@ -208,64 +205,67 @@ struct DimensionalFeaturedDataset{ preserves_type(::typeof(minimum)) = true # TODO fix preserves_type(::typeof(maximum)) = true # TODO fix - function DimensionalFeaturedDataset( + function DimensionalLogiset( domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, ontology :: Ontology{W}, mixed_features :: AbstractVector; kwargs..., ) where {N,W<:AbstractWorld} domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{dimensionality(domain),W}(domain) : domain) - @assert all((f)->(preserves_type(f)), mixed_features) "Please, specify the feature output type V upon construction, as in: DimensionalFeaturedDataset{V}(...)." # TODO highlight and improve + @assert all((f)->(preserves_type(f)), mixed_features) "Please, specify the feature output type V upon construction, as in: DimensionalLogiset{V}(...)." # TODO highlight and improve V = eltype(domain) - DimensionalFeaturedDataset{V}(domain, ontology, mixed_features; kwargs...) + DimensionalLogiset{V}(domain, ontology, mixed_features; kwargs...) end end -domain(X::DimensionalFeaturedDataset) = X.domain -ontology(X::DimensionalFeaturedDataset) = X.ontology -features(X::DimensionalFeaturedDataset) = X.features -grouped_featsaggrsnops(X::DimensionalFeaturedDataset) = X.grouped_featsaggrsnops -grouped_featsnaggrs(X::DimensionalFeaturedDataset) = X.grouped_featsnaggrs +domain(X::DimensionalLogiset) = X.domain +ontology(X::DimensionalLogiset) = X.ontology +features(X::DimensionalLogiset) = X.features +grouped_featsaggrsnops(X::DimensionalLogiset) = X.grouped_featsaggrsnops +grouped_featsnaggrs(X::DimensionalLogiset) = X.grouped_featsnaggrs -function Base.getindex(X::DimensionalFeaturedDataset, args...) +function Base.getindex(X::DimensionalLogiset, args...) domain(X)[args...]::featvaltype(X) end -Base.size(X::DimensionalFeaturedDataset) = Base.size(domain(X)) +Base.size(X::DimensionalLogiset) = Base.size(domain(X)) -dimensionality(X::DimensionalFeaturedDataset{V,N,W}) where {V,N,W} = N -worldtype(X::DimensionalFeaturedDataset{V,N,W}) where {V,N,W} = W +dimensionality(X::DimensionalLogiset{V,N,W}) where {V,N,W} = N +worldtype(X::DimensionalLogiset{V,N,W}) where {V,N,W} = W -nsamples(X::DimensionalFeaturedDataset) = nsamples(domain(X)) -nattributes(X::DimensionalFeaturedDataset) = nattributes(domain(X)) +nsamples(X::DimensionalLogiset) = nsamples(domain(X)) +nattributes(X::DimensionalLogiset) = nattributes(domain(X)) -relations(X::DimensionalFeaturedDataset) = relations(ontology(X)) -nrelations(X::DimensionalFeaturedDataset) = length(relations(X)) -nfeatures(X::DimensionalFeaturedDataset) = length(features(X)) +relations(X::DimensionalLogiset) = relations(ontology(X)) +nrelations(X::DimensionalLogiset) = length(relations(X)) +nfeatures(X::DimensionalLogiset) = length(features(X)) -channel_size(X::DimensionalFeaturedDataset, args...) = channel_size(domain(X), args...) -max_channel_size(X::DimensionalFeaturedDataset) = max_channel_size(domain(X)) +channel_size(X::DimensionalLogiset, args...) = channel_size(domain(X), args...) +max_channel_size(X::DimensionalLogiset) = max_channel_size(domain(X)) -get_instance(X::DimensionalFeaturedDataset, args...) = get_instance(domain(X), args...) +get_instance(X::DimensionalLogiset, args...) = get_instance(domain(X), args...) -_slice_dataset(X::DimensionalFeaturedDataset, inds::AbstractVector{<:Integer}, args...; kwargs...) = - DimensionalFeaturedDataset(_slice_dataset(domain(X), inds, args...; kwargs...), ontology(X), features(X), grouped_featsaggrsnops(X); initialworld = initialworld(X)) +_slice_dataset(X::DimensionalLogiset, inds::AbstractVector{<:Integer}, args...; kwargs...) = + DimensionalLogiset(_slice_dataset(domain(X), inds, args...; kwargs...), ontology(X), features(X), grouped_featsaggrsnops(X); initialworld = initialworld(X)) -frame(X::DimensionalFeaturedDataset, i_sample) = frame(domain(X), i_sample) -initialworld(X::DimensionalFeaturedDataset) = X.initialworld -function initialworld(X::DimensionalFeaturedDataset, i_sample) +frame(X::DimensionalLogiset, i_sample) = frame(domain(X), i_sample) +initialworld(X::DimensionalLogiset) = X.initialworld +function initialworld(X::DimensionalLogiset, i_sample) initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_sample] : initialworld(X) end -function display_structure(X::DimensionalFeaturedDataset; indent_str = "") +function displaystructure(X::DimensionalLogiset; indent_str = "") out = "$(typeof(X))\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "├ relations: \t$((length(relations(X))))\t$(relations(X))\n" - out *= indent_str * "├ domain shape\t$(Base.size(domain(X)))\n" - out *= indent_str * "├ max_channel_size\t$(max_channel_size(X))" - out *= indent_str * "└ initialworld(s)\t$(initialworld(X))" + out *= indent_str * "├ relations:\t($((nrelations(X))))\t[$(join(syntaxstring.(relations(X)), ", "))]\n" + out *= indent_str * "├ features:\t($((nfeatures(X))))\t[$(join(syntaxstring.(features(X)), ", "))]\n" + out *= indent_str * "├ domain shape:\t\t$(Base.size(domain(X)))\n" + out *= indent_str * "├ nsamples:\t\t$(nsamples(X))\n" + out *= indent_str * "├ nattributes:\t\t$(nattributes(X))\n" + out *= indent_str * "├ max_channel_size:\t$(max_channel_size(X))\n" + out *= indent_str * "└ initialworld(s):\t$(initialworld(X))" out end -hasnans(X::DimensionalFeaturedDataset) = hasnans(domain(X)) +hasnans(X::DimensionalLogiset) = hasnans(domain(X)) diff --git a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl b/src/datasets/dimensional-datasets/datasets/dimensional-supports.jl similarity index 96% rename from src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl rename to src/datasets/dimensional-datasets/datasets/dimensional-supports.jl index a574615..4e2c065 100644 --- a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl +++ b/src/datasets/dimensional-datasets/datasets/dimensional-supports.jl @@ -88,7 +88,7 @@ struct UniformFullDimensionalRelationalSupport{ end function UniformFullDimensionalRelationalSupport( - fd::FeaturedDataset, + fd::Logiset, perform_initialization::Bool = false, ) UniformFullDimensionalRelationalSupport(fwd(fd), nfeatsnaggrs(fd), nrelations(fd), perform_initialization) @@ -266,7 +266,7 @@ end # hasnans(support::OneWorldFWD_RS) = any(_isnan.(support.d)) -# function fwd_rs_init(fd::FeaturedDataset{T,OneWorld}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} +# function fwd_rs_init(fd::Logiset{T,OneWorld}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} # if perform_initialization # _fwd_rs = fill!(Array{Union{T,Nothing}, 3}(undef, nsamples(fd), nfeatsnaggrs, nrelations), nothing) # OneWorldFWD_RS{Union{T,Nothing}}(_fwd_rs) @@ -312,7 +312,7 @@ end # any([hasnans(support.d[x,y,:,:,:]) for x in 1:size(support.d, 1) for y in (x+1):size(support.d, 2)]) # end -# function fwd_rs_init(fd::FeaturedDataset{T,<:Interval}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} +# function fwd_rs_init(fd::Logiset{T,<:Interval}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} # _fwd = fd.fwd # if perform_initialization # _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, size(_fwd, 1), size(_fwd, 2), nsamples(fd), nfeatsnaggrs, nrelations), nothing) @@ -352,7 +352,7 @@ end # TODO... hasnans(support::Interval2DFWD_RS) = any(_isnan.(support.d)) # TODO...? hasnans(support::Interval2DFWD_RS) = any([hasnans(support.d[xx,xy,yx,yy,:,:,:]) for xx in 1:size(support.d, 1) for xy in (xx+1):size(support.d, 2) for yx in 1:size(support.d, 3) for yy in (yx+1):size(support.d, 4)]) -# fwd_rs_init(fd::FeaturedDataset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} = begin +# fwd_rs_init(fd::Logiset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} = begin # _fwd = fd.fwd # if perform_initialization # _fwd_rs = fill!(Array{Union{T,Nothing}, 7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), nsamples(fd), nfeatsnaggrs, nrelations), nothing) @@ -396,7 +396,7 @@ end # hasnans(support::Interval2DFWD_RS) = any(_isnan.(support.d)) -# function fwd_rs_init(fd::FeaturedDataset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} +# function fwd_rs_init(fd::Logiset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} # _fwd = fd.fwd # if perform_initialization # _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), nsamples(fd), nfeatsnaggrs, nrelations), nothing) diff --git a/src/dimensional-datasets/datasets/main.jl b/src/datasets/dimensional-datasets/datasets/main.jl similarity index 74% rename from src/dimensional-datasets/datasets/main.jl rename to src/datasets/dimensional-datasets/datasets/main.jl index 6f99f7c..c34ffa9 100644 --- a/src/dimensional-datasets/datasets/main.jl +++ b/src/datasets/dimensional-datasets/datasets/main.jl @@ -25,13 +25,13 @@ using SoleModels using SoleModels: Aggregator, AbstractCondition using SoleModels: BoundedExplicitConditionalAlphabet using SoleModels: CanonicalFeatureGeq, CanonicalFeatureGeqSoft, CanonicalFeatureLeq, CanonicalFeatureLeqSoft -using SoleModels: AbstractConditionalDataset, AbstractMultiModalFrame -using SoleModels: MultiFrameConditionalDataset, AbstractActiveFeaturedDataset +using SoleModels: AbstractLogiset, AbstractMultiModalFrame +using SoleModels: MultiFrameLogiset, AbstractLogiset using SoleModels: apply_test_operator, existential_aggregator, aggregator_bottom, aggregator_to_binary -import SoleModels: representatives, FeatMetaCondition, FeatCondition, featvaltype +import SoleModels: representatives, ScalarMetaCondition, ScalarCondition, featvaltype import SoleModels: nsamples, nrelations, nfeatures, check, _slice_dataset, minify -import SoleModels: nframes, frames, display_structure, frame -import SoleModels: grouped_featsaggrsnops, features, grouped_metaconditions, alphabet, find_feature_id, find_relation_id, isminifiable +import SoleModels: nframes, frames, displaystructure, frame +import SoleModels: grouped_featsaggrsnops, features, grouped_metaconditions, alphabet, findfeature, findrelation, isminifiable using SoleModels: grouped_featsnops2grouped_featsaggrsnops, grouped_featsaggrsnops2grouped_featsnops, @@ -40,15 +40,19 @@ using SoleModels: grouped_featsnops2grouped_featsaggrsnops, features_grouped_featsaggrsnops2grouped_featsnaggrs ############################################################################################ -function check_initialworld(FD::Type{<:AbstractConditionalDataset}, initialworld, W) +function check_initialworld(FD::Type{<:AbstractLogiset}, initialworld, W) @assert isnothing(initialworld) || initialworld isa W "Cannot instantiate" * " $(FD) with worldtype = $(W) but initialworld of type $(typeof(initialworld))." end -include("passive-dimensional-dataset.jl") +include("passive-dimensional-datasets.jl") -include("dimensional-featured-dataset.jl") -include("featured-dataset.jl") +include("dimensional-logiset.jl") + +# World-specific featured world datasets and supports +include("dimensional-fwds.jl") + +_default_fwd_type(::Type{<:FullDimensionalFrame}) = UniformFullDimensionalFWD abstract type SupportingDataset{W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} end @@ -56,7 +60,7 @@ isminifiable(X::SupportingDataset) = false worldtype(X::SupportingDataset{W}) where {W} = W -function display_structure(X::SupportingDataset; indent_str = "") +function displaystructure(X::SupportingDataset; indent_str = "") out = "$(typeof(X))\t$((Base.summarysize(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs" out *= " ($(round(nmemoizedvalues(X))) values)\n" out @@ -64,10 +68,6 @@ end abstract type FeaturedSupportingDataset{V<:Number,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} <: SupportingDataset{W,FR} end - -include("supported-featured-dataset.jl") - -include("one-step-featured-supporting-dataset/main.jl") -include("generic-supporting-datasets.jl") +include("dimensional-supports.jl") include("check.jl") diff --git a/src/dimensional-datasets/datasets/passive-dimensional-dataset.jl b/src/datasets/dimensional-datasets/datasets/passive-dimensional-dataset.jl similarity index 96% rename from src/dimensional-datasets/datasets/passive-dimensional-dataset.jl rename to src/datasets/dimensional-datasets/datasets/passive-dimensional-dataset.jl index 9ad1a8a..e859956 100644 --- a/src/dimensional-datasets/datasets/passive-dimensional-dataset.jl +++ b/src/datasets/dimensional-datasets/datasets/passive-dimensional-dataset.jl @@ -20,7 +20,7 @@ struct PassiveDimensionalDataset{ W<:AbstractWorld, DOM<:AbstractDimensionalDataset, FR<:AbstractDimensionalFrame{N,W}, -} <: AbstractConditionalDataset{W,AbstractCondition,Bool,FR} # TODO remove AbstractCondition. Note: truth value could by different +} <: AbstractLogiset{W,FT where FT<:DimensionalFeature,Bool,FR} # Note: truth value could by different d::DOM diff --git a/src/dimensional-datasets/dimensional-features.jl b/src/datasets/dimensional-datasets/dimensional-features.jl similarity index 98% rename from src/dimensional-datasets/dimensional-features.jl rename to src/datasets/dimensional-datasets/dimensional-features.jl index aee77a9..8fb5a53 100644 --- a/src/dimensional-datasets/dimensional-features.jl +++ b/src/datasets/dimensional-datasets/dimensional-features.jl @@ -6,7 +6,7 @@ import Base: isequal, hash, show import SoleLogics: syntaxstring """ - abstract type DimensionalFeature{U} <: AbstractFeature{U} end + abstract type DimensionalFeature{U<:Real} <: AbstractFeature{U} end Abstract type for dimensional features, representing functions that can be computed on dimensional, geometrical worlds. @@ -20,7 +20,7 @@ The value of a feature for a given world can be then evaluated in a `Condition`, See also [`Interval`](@ref), [`Interval2D`](@ref), [`GeometricalWorld`](@ref), [`AbstractFeature`](@ref). """ -abstract type DimensionalFeature{U} <: AbstractFeature{U} end +abstract type DimensionalFeature{U<:Real} <: AbstractFeature{U} end # const DimensionalFeatureFunction = FunctionWrapper{Number,Tuple{AbstractArray{<:Number}}} @@ -88,7 +88,7 @@ Note that only one in `attribute_names_map` and `attribute_name_prefix` should b See also [`parsecondition`](@ref), -[`FeatCondition`](@ref), +[`ScalarCondition`](@ref), [`syntaxstring`](@ref). """ function attribute_name( diff --git a/src/dimensional-datasets/dimensional-ontologies.jl b/src/datasets/dimensional-datasets/dimensional-ontologies.jl similarity index 100% rename from src/dimensional-datasets/dimensional-ontologies.jl rename to src/datasets/dimensional-datasets/dimensional-ontologies.jl diff --git a/src/datasets/dimensional-datasets/gamma-access.jl b/src/datasets/dimensional-datasets/gamma-access.jl new file mode 100644 index 0000000..7c3932c --- /dev/null +++ b/src/datasets/dimensional-datasets/gamma-access.jl @@ -0,0 +1,53 @@ +using SoleLogics: AbstractWorld, AbstractRelation +using SoleModels: AbstractFeature, Aggregator + +############################################################################################ + +@inline function onestep_accessible_aggregation( + X::PassiveDimensionalDataset{N,W}, + i_sample::Integer, + w::W, + r::AbstractRelation, + f::AbstractFeature{V}, + aggr::Aggregator, + args..., +) where {N,V,W<:AbstractWorld} + vs = [X[i_sample, w2, f] for w2 in representatives(X, i_sample, w, r, f, aggr)] + return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) +end + +@inline function onestep_accessible_aggregation( + X::PassiveDimensionalDataset{N,W}, + i_sample::Integer, + r::GlobalRel, + f::AbstractFeature{V}, + aggr::Aggregator, + args... +) where {N,V,W<:AbstractWorld} + vs = [X[i_sample, w2, f] for w2 in representatives(X, i_sample, r, f, aggr)] + return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) +end + +############################################################################################ + +@inline function onestep_accessible_aggregation( + X::DimensionalLogiset{VV,N,W}, + i_sample::Integer, + w::W, + r::AbstractRelation, + f::AbstractFeature{V}, + aggr::Aggregator, + args..., +) where {VV,N,V<:VV,W<:AbstractWorld} + onestep_accessible_aggregation(domain(X), i_sample, w, r, f, aggr, args...) +end +@inline function onestep_accessible_aggregation( + X::DimensionalLogiset{VV,N,W}, + i_sample::Integer, + r::GlobalRel, + f::AbstractFeature{V}, + aggr::Aggregator, + args..., +) where {VV,N,V<:VV,W<:AbstractWorld} + onestep_accessible_aggregation(domain(X), i_sample, r, f, aggr, args...) +end diff --git a/src/dimensional-datasets/main.jl b/src/datasets/dimensional-datasets/main.jl similarity index 95% rename from src/dimensional-datasets/main.jl rename to src/datasets/dimensional-datasets/main.jl index 05d750d..6929ef3 100644 --- a/src/dimensional-datasets/main.jl +++ b/src/datasets/dimensional-datasets/main.jl @@ -25,12 +25,12 @@ include("parse-dimensional-condition.jl") # Concrete type for ontologies include("ontology.jl") # TODO frame inside the ontology? -export DimensionalFeaturedDataset, FeaturedDataset, SupportedFeaturedDataset +export DimensionalLogiset, Logiset, SupportedScalarLogiset # Dataset structures include("datasets/main.jl") -const GenericModalDataset = Union{AbstractDimensionalDataset,AbstractConditionalDataset,MultiFrameConditionalDataset} +const GenericModalDataset = Union{AbstractDimensionalDataset,AbstractLogiset,MultiFrameLogiset} # TODO? include("gamma-access.jl") diff --git a/src/dimensional-datasets/ontology.jl b/src/datasets/dimensional-datasets/ontology.jl similarity index 100% rename from src/dimensional-datasets/ontology.jl rename to src/datasets/dimensional-datasets/ontology.jl diff --git a/src/dimensional-datasets/parse-dimensional-condition.jl b/src/datasets/dimensional-datasets/parse-dimensional-condition.jl similarity index 92% rename from src/dimensional-datasets/parse-dimensional-condition.jl rename to src/datasets/dimensional-datasets/parse-dimensional-condition.jl index 474a320..374c2b9 100644 --- a/src/dimensional-datasets/parse-dimensional-condition.jl +++ b/src/datasets/dimensional-datasets/parse-dimensional-condition.jl @@ -1,22 +1,22 @@ using StatsBase #= ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Code purpose ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Parse a string "min[V189] <= 250" into the corresponding FeatCondition `parsecondition`. +Parse a string "min[V189] <= 250" into the corresponding ScalarCondition `parsecondition`. -A FeatCondition is built of three parts; for example, the FeatCondition above has: +A ScalarCondition is built of three parts; for example, the ScalarCondition above has: 1) feature -> UnivariateMin(189), 2) test_operator -> <=, 3) threshold -> 250, -which are assembled by `FeatCondition(FeatMetaCondition(feature, test_operator), threshold)`. +which are assembled by `ScalarCondition(ScalarMetaCondition(feature, test_operator), threshold)`. -`parsecondition` can be used within SoleLogics to recognize Proposition{FeatCondition}, +`parsecondition` can be used within SoleLogics to recognize Proposition{ScalarCondition}, when parsing a logical expression: SoleLogics.parseformulatree( "min[V189] <= 250 ∧ min[V37] > 20"; proposition_parser = parsecondition); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ =# """ -Aliases for specific features used for parsing `FeatCondition`s. +Aliases for specific features used for parsing `ScalarCondition`s. """ const BASE_FEATURE_ALIASES = Dict{String,Union{Type,Function}}( # @@ -40,9 +40,9 @@ DEFAULT_FEATVALTYPE = Float64 custom_feature_aliases = Dict{String,Union{Type,Function}}(), attribute_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, attribute_name_prefix::Union{Nothing,String} = nothing, - )::FeatCondition + )::ScalarCondition -Return a `FeatCondition` which is the result of parsing `expression`. +Return a `ScalarCondition` which is the result of parsing `expression`. This can be integrated with the `SoleLogics` parsing system (see Examples section). Currently, this function can only parse `UnivariateFeature`s; for example, "min[V189]", which is parsed as `UnivariateMin(189)`. @@ -50,7 +50,7 @@ which is parsed as `UnivariateMin(189)`. With $(repr(UVF_OPENING_BRACKET)) and $(repr(UVF_CLOSING_BRACKET)) for **opening\\_bracket** and **closing\\_bracket**, respectively, -each `FeatCondition` is shaped as follows: +each `ScalarCondition` is shaped as follows: `"**feature[variable] test\\_operator threshold.**""` @@ -86,8 +86,8 @@ julia> syntaxstring(parseformulatree("min[V1] <= 15 ∧ max[V1] >= 85"; proposit See also [`attribute_name`](@ref), -[`FeatCondition`](@ref), -[`FeatMetaCondition`](@ref), +[`ScalarCondition`](@ref), +[`ScalarMetaCondition`](@ref), [`parseformulatree`](@ref), [`syntaxstring`](@ref). """ @@ -197,7 +197,7 @@ function parsecondition( end test_operator = eval(Meta.parse(_test_operator)) - metacond = FeatMetaCondition(feature, test_operator) + metacond = ScalarMetaCondition(feature, test_operator) - return FeatCondition(metacond, threshold) + return ScalarCondition(metacond, threshold) end diff --git a/src/dimensional-datasets/representatives/Full0DFrame.jl b/src/datasets/dimensional-datasets/representatives/Full0DFrame.jl similarity index 100% rename from src/dimensional-datasets/representatives/Full0DFrame.jl rename to src/datasets/dimensional-datasets/representatives/Full0DFrame.jl diff --git a/src/dimensional-datasets/representatives/Full1DFrame+IA.jl b/src/datasets/dimensional-datasets/representatives/Full1DFrame+IA.jl similarity index 100% rename from src/dimensional-datasets/representatives/Full1DFrame+IA.jl rename to src/datasets/dimensional-datasets/representatives/Full1DFrame+IA.jl diff --git a/src/dimensional-datasets/representatives/Full1DFrame+RCC.jl b/src/datasets/dimensional-datasets/representatives/Full1DFrame+RCC.jl similarity index 100% rename from src/dimensional-datasets/representatives/Full1DFrame+RCC.jl rename to src/datasets/dimensional-datasets/representatives/Full1DFrame+RCC.jl diff --git a/src/dimensional-datasets/representatives/Full1DFrame.jl b/src/datasets/dimensional-datasets/representatives/Full1DFrame.jl similarity index 89% rename from src/dimensional-datasets/representatives/Full1DFrame.jl rename to src/datasets/dimensional-datasets/representatives/Full1DFrame.jl index 793fece..00a3874 100644 --- a/src/dimensional-datasets/representatives/Full1DFrame.jl +++ b/src/datasets/dimensional-datasets/representatives/Full1DFrame.jl @@ -1,6 +1,6 @@ using SoleLogics: intervals_in, short_intervals_in -representatives(fr::Full1DFrame, r::GlobalRel, ::FeatMetaCondition) = intervals_in(1, X(fr)+1) +representatives(fr::Full1DFrame, r::GlobalRel, ::ScalarMetaCondition) = intervals_in(1, X(fr)+1) representatives(fr::Full1DFrame, r::GlobalRel, ::Union{UnivariateMin,UnivariateMax}, ::Union{typeof(minimum),typeof(maximum)}) = short_intervals_in(1, X(fr)+1) representatives(fr::Full1DFrame, r::GlobalRel, ::UnivariateMax, ::typeof(maximum)) = [Interval(1, X(fr)+1) ] diff --git a/src/dimensional-datasets/representatives/Full2DFrame.jl b/src/datasets/dimensional-datasets/representatives/Full2DFrame.jl similarity index 100% rename from src/dimensional-datasets/representatives/Full2DFrame.jl rename to src/datasets/dimensional-datasets/representatives/Full2DFrame.jl diff --git a/src/conditional-data/active-featured-dataset.jl b/src/datasets/scalar-datasets/active-scalar-logiset.jl similarity index 62% rename from src/conditional-data/active-featured-dataset.jl rename to src/datasets/scalar-datasets/active-scalar-logiset.jl index 85900a7..3f8fa3c 100644 --- a/src/conditional-data/active-featured-dataset.jl +++ b/src/datasets/scalar-datasets/active-scalar-logiset.jl @@ -1,36 +1,38 @@ -# Active datasets comprehend structures for representing relation sets, features, enumerating worlds, -# etc. While learning a model can be done only with active modal datasets, testing a model -# can be done with both active and passive modal datasets. -# -abstract type AbstractActiveFeaturedDataset{ - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - FT<:AbstractFeature{V} -} <: AbstractActiveConditionalDataset{W,AbstractCondition,Bool,FR} end -featvaltype(::Type{<:AbstractActiveFeaturedDataset{V}}) where {V} = V -featvaltype(d::AbstractActiveFeaturedDataset) = featvaltype(typeof(d)) - -featuretype(::Type{<:AbstractActiveFeaturedDataset{V,W,FR,FT}}) where {V,W,FR,FT} = FT -featuretype(d::AbstractActiveFeaturedDataset) = featuretype(typeof(d)) +""" +Active scalar datasets are active logical datasets with scalar features. +""" +const AbstractActiveScalarLogiset{ + W<:AbstractWorld, + V<:Number, + F<:AbstractFeature{V}, + T<:TruthValue, + FR<:AbstractFrame{W,T} +} = AbstractLogiset{W,V,F,T,FR} -function grouped_featsaggrsnops(X::AbstractActiveFeaturedDataset) +function grouped_featsaggrsnops(X::AbstractScalarLogiset) return error("Please, provide method grouped_featsaggrsnops(::$(typeof(X))).") end -function features(X::AbstractActiveFeaturedDataset) - return error("Please, provide method features(::$(typeof(X))).") +function check( + p::Proposition{<:ScalarCondition}, + X::AbstractScalarLogiset{W}, + i_sample, + w::W, +) where {W<:AbstractWorld} + cond = atom(p) + featval = featvalue(X, i_sample, w, feature(cond)) + apply_test_operator(test_operator(cond), featval, threshold(cond)) end -function grouped_metaconditions(X::AbstractActiveFeaturedDataset) +function grouped_metaconditions(X::AbstractScalarLogiset) grouped_featsnops = grouped_featsaggrsnops2grouped_featsnops(grouped_featsaggrsnops(X)) [begin - (feat,[FeatMetaCondition(feat,op) for op in ops]) + (feat,[ScalarMetaCondition(feat,op) for op in ops]) end for (feat,ops) in zip(features(X),grouped_featsnops)] end -function alphabet(X::AbstractActiveFeaturedDataset) +function alphabet(X::AbstractScalarLogiset) conds = vcat([begin thresholds = unique([ X[i_sample, w, feature] @@ -39,33 +41,11 @@ function alphabet(X::AbstractActiveFeaturedDataset) ]) [(mc, thresholds) for mc in metaconditions] end for (feature, metaconditions) in grouped_metaconditions(X)]...) - C = FeatCondition{featvaltype(X),FeatMetaCondition{featuretype(X)}} + C = ScalarCondition{featvaltype(X),featuretype(X),ScalarMetaCondition{featuretype(X)}} BoundedExplicitConditionalAlphabet{C}(collect(conds)) end -# Base.length(X::AbstractActiveFeaturedDataset) = nsamples(X) -# Base.iterate(X::AbstractActiveFeaturedDataset, state=1) = state > nsamples(X) ? nothing : (get_instance(X, state), state+1) - -function find_feature_id(X::AbstractActiveFeaturedDataset, feature::AbstractFeature) - id = findfirst(x->(Base.isequal(x, feature)), features(X)) - if isnothing(id) - error("Could not find feature $(feature) in AbstractActiveFeaturedDataset of type $(typeof(X)).") - end - id -end -function find_relation_id(X::AbstractActiveFeaturedDataset, relation::AbstractRelation) - id = findfirst(x->x==relation, relations(X)) - if isnothing(id) - error("Could not find relation $(relation) in AbstractActiveFeaturedDataset of type $(typeof(X)).") - end - id -end - - -# By default an active modal dataset cannot be minified -isminifiable(::AbstractActiveFeaturedDataset) = false - # Convenience functions function grouped_featsnops2grouped_featsaggrsnops( grouped_featsnops::AbstractVector{<:AbstractVector{<:TestOperator}} diff --git a/src/conditional-data/canonical-conditions.jl b/src/datasets/scalar-datasets/canonical-scalar-conditions.jl similarity index 100% rename from src/conditional-data/canonical-conditions.jl rename to src/datasets/scalar-datasets/canonical-scalar-conditions.jl diff --git a/src/dimensional-datasets/gamma-access.jl b/src/datasets/scalar-datasets/gamma-access.jl similarity index 54% rename from src/dimensional-datasets/gamma-access.jl rename to src/datasets/scalar-datasets/gamma-access.jl index 3353b38..c990527 100644 --- a/src/dimensional-datasets/gamma-access.jl +++ b/src/datasets/scalar-datasets/gamma-access.jl @@ -1,65 +1,12 @@ -using SoleLogics: AbstractWorld, AbstractRelation -using SoleModels: AbstractFeature, Aggregator ############################################################################################ -@inline function onestep_accessible_aggregation( - X::PassiveDimensionalDataset{N,W}, - i_sample::Integer, - w::W, - r::AbstractRelation, - f::AbstractFeature{V}, - aggr::Aggregator, - args..., -) where {N,V,W<:AbstractWorld} - vs = [X[i_sample, w2, f] for w2 in representatives(X, i_sample, w, r, f, aggr)] - return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) -end - -@inline function onestep_accessible_aggregation( - X::PassiveDimensionalDataset{N,W}, - i_sample::Integer, - r::GlobalRel, - f::AbstractFeature{V}, - aggr::Aggregator, - args... -) where {N,V,W<:AbstractWorld} - vs = [X[i_sample, w2, f] for w2 in representatives(X, i_sample, r, f, aggr)] - return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) -end - -############################################################################################ - -@inline function onestep_accessible_aggregation( - X::DimensionalFeaturedDataset{VV,N,W}, - i_sample::Integer, - w::W, - r::AbstractRelation, - f::AbstractFeature{V}, - aggr::Aggregator, - args..., -) where {VV,N,V<:VV,W<:AbstractWorld} - onestep_accessible_aggregation(domain(X), i_sample, w, r, f, aggr, args...) -end -@inline function onestep_accessible_aggregation( - X::DimensionalFeaturedDataset{VV,N,W}, - i_sample::Integer, - r::GlobalRel, - f::AbstractFeature{V}, - aggr::Aggregator, - args..., -) where {VV,N,V<:VV,W<:AbstractWorld} - onestep_accessible_aggregation(domain(X), i_sample, r, f, aggr, args...) -end - -############################################################################################ - -@inline function onestep_accessible_aggregation(X::FeaturedDataset{VV,W}, i_sample::Integer, w::W, r::AbstractRelation, f::AbstractFeature{V}, aggr::Aggregator, args...) where {VV,V<:VV,W<:AbstractWorld} +@inline function onestep_accessible_aggregation(X::Logiset{VV,W}, i_sample::Integer, w::W, r::AbstractRelation, f::AbstractFeature{V}, aggr::Aggregator, args...) where {VV,V<:VV,W<:AbstractWorld} vs = [X[i_sample, w2, f] for w2 in representatives(X, i_sample, w, r, f, aggr)] return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) end -@inline function onestep_accessible_aggregation(X::FeaturedDataset{VV,W}, i_sample::Integer, r::GlobalRel, f::AbstractFeature{V}, aggr::Aggregator, args...) where {VV,V<:VV,W<:AbstractWorld} +@inline function onestep_accessible_aggregation(X::Logiset{VV,W}, i_sample::Integer, r::GlobalRel, f::AbstractFeature{V}, aggr::Aggregator, args...) where {VV,V<:VV,W<:AbstractWorld} vs = [X[i_sample, w2, f] for w2 in representatives(X, i_sample, r, f, aggr)] return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) end @@ -67,20 +14,20 @@ end ############################################################################################ function onestep_accessible_aggregation( - X::SupportedFeaturedDataset{VV,W}, + X::SupportedScalarLogiset{VV,W}, i_sample::Integer, w::W, relation::AbstractRelation, feature::AbstractFeature{V}, aggr::Aggregator, i_featsnaggr::Union{Nothing,Integer} = nothing, - i_relation::Integer = find_relation_id(X, relation), + i_relation::Integer = findrelation(X, relation), ) where {VV,V<:VV,W<:AbstractWorld} compute_modal_gamma(support(X), fd(X), i_sample, w, relation, feature, aggregator, i_featsnaggr, i_relation) end @inline function onestep_accessible_aggregation( - X::SupportedFeaturedDataset{VV,W}, + X::SupportedScalarLogiset{VV,W}, i_sample::Integer, r::GlobalRel, f::AbstractFeature{V}, @@ -92,29 +39,29 @@ end ############################################################################################ -function fwdslice_onestep_accessible_aggregation(fd::FeaturedDataset, fwdslice::FWDFeatureSlice, i_sample, r::GlobalRel, f, aggr, args...) +function fwdslice_onestep_accessible_aggregation(fd::Logiset, fwdslice::FWDFeatureSlice, i_sample, r::GlobalRel, f, aggr, args...) # accessible_worlds = allworlds(fd, i_sample) accessible_worlds = representatives(fd, i_sample, r, f, aggr) gamma = apply_aggregator(fwdslice, accessible_worlds, aggr) end -function fwdslice_onestep_accessible_aggregation(fd::FeaturedDataset, fwdslice::FWDFeatureSlice, i_sample, w, r::AbstractRelation, f, aggr, args...) +function fwdslice_onestep_accessible_aggregation(fd::Logiset, fwdslice::FWDFeatureSlice, i_sample, w, r::AbstractRelation, f, aggr, args...) # accessible_worlds = accessibles(fd, i_sample, w, r) accessible_worlds = representatives(fd, i_sample, w, r, f, aggr) gamma = apply_aggregator(fwdslice, accessible_worlds, aggr) end # TODO remove -# function fwdslice_onestep_accessible_aggregation(fd::SupportedFeaturedDataset, fwdslice::FWDFeatureSlice, i_sample, args...) +# function fwdslice_onestep_accessible_aggregation(fd::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_sample, args...) # fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_sample, args...) # end -function fwdslice_onestep_accessible_aggregation(X::SupportedFeaturedDataset, fwdslice::FWDFeatureSlice, i_sample, r::GlobalRel, f, aggr, args...) +function fwdslice_onestep_accessible_aggregation(X::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_sample, r::GlobalRel, f, aggr, args...) fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_sample, r, f, aggr, args...) end -function fwdslice_onestep_accessible_aggregation(X::SupportedFeaturedDataset, fwdslice::FWDFeatureSlice, i_sample, w, r::AbstractRelation, f, aggr, args...) +function fwdslice_onestep_accessible_aggregation(X::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_sample, w, r::AbstractRelation, f, aggr, args...) fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_sample, w, r, f, aggr, args...) end @@ -122,7 +69,7 @@ end function fwdslice_onestep_accessible_aggregation( X::OneStepFeaturedSupportingDataset{V,W}, - fd::FeaturedDataset{V,W}, + fd::Logiset{V,W}, fwdslice::FWDFeatureSlice, i_sample::Integer, r::GlobalRel, @@ -140,7 +87,7 @@ end function fwdslice_onestep_accessible_aggregation( X::OneStepFeaturedSupportingDataset{V,W}, - fd::FeaturedDataset{V,W}, + fd::Logiset{V,W}, fwdslice::FWDFeatureSlice, i_sample::Integer, w::W, diff --git a/src/datasets/scalar-datasets/main.jl b/src/datasets/scalar-datasets/main.jl new file mode 100644 index 0000000..bb90b3f --- /dev/null +++ b/src/datasets/scalar-datasets/main.jl @@ -0,0 +1,30 @@ + +export inverse_test_operator, dual_test_operator, + apply_test_operator, + TestOperator + +# Test operators to be used for comparing features and threshold values +include("test-operators.jl") + +# Alphabets of conditions on the features, to be used in logical datasets +include("scalar-conditions.jl") + +export MixedFeature, CanonicalFeature, canonical_geq, canonical_leq + +export canonical_geq_95, canonical_geq_90, canonical_geq_85, canonical_geq_80, canonical_geq_75, canonical_geq_70, canonical_geq_60, + canonical_leq_95, canonical_leq_90, canonical_leq_85, canonical_leq_80, canonical_leq_75, canonical_leq_70, canonical_leq_60 + +# TODO remove? +# Types for representing common associations between features and operators +include("canonical-scalar-conditions.jl") + +const MixedFeature = Union{AbstractFeature,CanonicalFeature,Function,Tuple{TestOperator,Function},Tuple{TestOperator,AbstractFeature}} + +include("random.jl") + +############################################################################################ +############################################################################################ +############################################################################################ + + +include("active-scalar-logiset.jl") # TODO sort out and precisely define interface diff --git a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl b/src/datasets/scalar-datasets/one-step-featured-supporting-dataset.jl similarity index 94% rename from src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl rename to src/datasets/scalar-datasets/one-step-featured-supporting-dataset.jl index 18c1745..68081a4 100644 --- a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl +++ b/src/datasets/scalar-datasets/one-step-featured-supporting-dataset.jl @@ -45,13 +45,13 @@ struct OneStepFeaturedSupportingDataset{ # A function that computes the support from an explicit modal dataset Base.@propagate_inbounds function OneStepFeaturedSupportingDataset( - fd :: FeaturedDataset{V,W}, + fd :: Logiset{V,W}, relational_support_type :: Type{<:AbstractRelationalSupport} = _default_rs_type(W); compute_relation_glob = false, use_memoization = false, ) where {V,W<:AbstractWorld} - # @logmsg LogOverview "FeaturedDataset -> SupportedFeaturedDataset " + # @logmsg LogOverview "Logiset -> SupportedScalarLogiset " _fwd = fwd(fd) _features = features(fd) @@ -161,7 +161,7 @@ nsamples(X::OneStepFeaturedSupportingDataset) = nsamples(fwd_rs(X)) # TODO delegate to the two components... function checksupportconsistency( - fd::FeaturedDataset{V,W}, + fd::Logiset{V,W}, X::OneStepFeaturedSupportingDataset{V,W}, ) where {V,W<:AbstractWorld} @assert nsamples(fd) == nsamples(X) "Consistency check failed! Unmatching nsamples for fd and support: $(nsamples(fd)) and $(nsamples(X))" @@ -210,7 +210,7 @@ function minify(X::OSSD) where {OSSD<:OneStepFeaturedSupportingDataset} X, backmap end -function display_structure(X::OneStepFeaturedSupportingDataset; indent_str = "") +function displaystructure(X::OneStepFeaturedSupportingDataset; indent_str = "") out = "$(typeof(X))\t$((Base.summarysize(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" out *= indent_str * "├ fwd_rs\t$(Base.summarysize(fwd_rs(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t" if usesmodalmemo(X) @@ -239,18 +239,18 @@ end function compute_global_gamma( X::OneStepFeaturedSupportingDataset{V,W}, - fd::FeaturedDataset{V,W}, + fd::Logiset{V,W}, i_sample::Integer, feature::AbstractFeature, aggregator::Aggregator, i_featsnaggr::Integer = find_featsnaggr_id(X, feature, aggregator), ) where {V,W<:AbstractWorld} _fwd_gs = fwd_gs(X) - # @assert !isnothing(_fwd_gs) "Error. SupportedFeaturedDataset must be built with compute_relation_glob = true for it to be ready to test global decisions." + # @assert !isnothing(_fwd_gs) "Error. SupportedScalarLogiset must be built with compute_relation_glob = true for it to be ready to test global decisions." if usesglobalmemo(X) && isnothing(_fwd_gs[i_sample, i_featsnaggr]) error("TODO finish this: memoization on the global table") # gamma = TODO... - # i_feature = find_feature_id(fd, feature) + # i_feature = findfeature(fd, feature) # fwdslice = fwdread_channel(fwd(fd), i_sample, i_feature) _fwd_gs[i_sample, i_featsnaggr] = gamma end @@ -259,7 +259,7 @@ end function compute_modal_gamma( X::OneStepFeaturedSupportingDataset{V,W}, - fd::FeaturedDataset{V,W}, + fd::Logiset{V,W}, i_sample::Integer, w::W, r::AbstractRelation, @@ -270,13 +270,10 @@ function compute_modal_gamma( )::V where {V,W<:AbstractWorld} _fwd_rs = fwd_rs(X) if usesmodalmemo(X) && isnothing(_fwd_rs[i_sample, w, i_featsnaggr, i_relation]) - i_feature = find_feature_id(fd, feature) + i_feature = findfeature(fd, feature) fwdslice = fwdread_channel(fwd(fd), i_sample, i_feature) gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_sample, w, r, feature, aggregator) fwd_rs[i_sample, w, i_featsnaggr, i_relation, gamma] end _fwd_rs[i_sample, w, i_featsnaggr, i_relation] end - -include("generic-supports.jl") -include("dimensional-supports.jl") diff --git a/src/conditional-data/random.jl b/src/datasets/scalar-datasets/random.jl similarity index 85% rename from src/conditional-data/random.jl rename to src/datasets/scalar-datasets/random.jl index b667675..5048fce 100644 --- a/src/conditional-data/random.jl +++ b/src/datasets/scalar-datasets/random.jl @@ -4,12 +4,12 @@ import Base: rand function Base.rand( rng::AbstractRNG, a::BoundedExplicitConditionalAlphabet; - metaconditions::Union{Nothing,FeatMetaCondition,AbstractVector{<:FeatMetaCondition}} = nothing, + metaconditions::Union{Nothing,ScalarMetaCondition,AbstractVector{<:ScalarMetaCondition}} = nothing, feature::Union{Nothing,AbstractFeature,AbstractVector{<:AbstractFeature}} = nothing, test_operator::Union{Nothing,TestOperator,AbstractVector{<:TestOperator}} = nothing, )::Proposition -Randomly sample a `Proposition` holding a `FeatCondition` from conditional alphabet `a`, +Randomly sample a `Proposition` holding a `ScalarCondition` from conditional alphabet `a`, such that: - if `metaconditions` are specified, then the set of metaconditions (feature-operator pairs) is limited to `metaconditions`; @@ -22,20 +22,20 @@ TODO Examples See also [`BoundedExplicitConditionalAlphabet`](@ref), -[`FeatCondition`](@ref), -[`FeatMetaCondition`](@ref), +[`ScalarCondition`](@ref), +[`ScalarMetaCondition`](@ref), [`AbstractAlphabet'](@ref). """ function Base.rand( rng::AbstractRNG, a::BoundedExplicitConditionalAlphabet; - metaconditions::Union{Nothing,FeatMetaCondition,AbstractVector{<:FeatMetaCondition}} = nothing, + metaconditions::Union{Nothing,ScalarMetaCondition,AbstractVector{<:ScalarMetaCondition}} = nothing, features::Union{Nothing,AbstractFeature,AbstractVector{<:AbstractFeature}} = nothing, test_operators::Union{Nothing,TestOperator,AbstractVector{<:TestOperator}} = nothing, )::Proposition # Transform values to singletons - metaconditions = metaconditions isa FeatMetaCondition ? [metaconditions] : metaconditions + metaconditions = metaconditions isa ScalarMetaCondition ? [metaconditions] : metaconditions features = features isa AbstractFeature ? [features] : features test_operators = test_operators isa TestOperator ? [test_operators] : test_operators @@ -78,5 +78,5 @@ function Base.rand( mc_thresholds = rand(rng, filtered_featconds) - return Proposition(FeatCondition(first(mc_thresholds), rand(rng, last(mc_thresholds)))) + return Proposition(ScalarCondition(first(mc_thresholds), rand(rng, last(mc_thresholds)))) end diff --git a/src/conditional-data/representatives.jl b/src/datasets/scalar-datasets/representatives.jl similarity index 95% rename from src/conditional-data/representatives.jl rename to src/datasets/scalar-datasets/representatives.jl index 8104204..8bbe138 100644 --- a/src/conditional-data/representatives.jl +++ b/src/datasets/scalar-datasets/representatives.jl @@ -5,7 +5,7 @@ using SoleLogics: AbstractMultiModalFrame, AbstractRelation, GlobalRel, Identity fr::AbstractMultiModalFrame{W}, S::W, ::AbstractRelation, - ::FeatMetaCondition + ::ScalarMetaCondition ) where {W<:AbstractWorld} Return an iterator to the (few) *representative* accessible worlds that are @@ -23,7 +23,7 @@ function representatives( # Dispatch on feature/aggregator pairs fr::AbstractMultiModalFrame{W}, w::W, r::AbstractRelation, - mc::FeatMetaCondition + mc::ScalarMetaCondition ) where {W<:AbstractWorld} representatives(fr, w, r, feature(mc), existential_aggregator(test_operator(mc))) end @@ -63,4 +63,4 @@ end # # TODO remove but probably we need this to stay because of ambiguities! # representatives(fr::AbstractMultiModalFrame{W}, w::W, r::IdentityRel, ::AbstractFeature, ::Aggregator) where {W<:AbstractWorld} = accessibles(fr, w, r) # TODO need this? -# `representatives(fr::AbstractMultiModalFrame{W}, S::AbstractWorldSet{W}, ::GlobalRel, ::FeatMetaCondition)` +# `representatives(fr::AbstractMultiModalFrame{W}, S::AbstractWorldSet{W}, ::GlobalRel, ::ScalarMetaCondition)` diff --git a/src/datasets/scalar-datasets/scalar-conditions.jl b/src/datasets/scalar-datasets/scalar-conditions.jl new file mode 100644 index 0000000..74a51f9 --- /dev/null +++ b/src/datasets/scalar-datasets/scalar-conditions.jl @@ -0,0 +1,247 @@ + +############################################################################################ + +""" + struct ScalarMetaCondition{F<:AbstractFeature,O<:TestOperator} <: AbstractCondition{F} + feature::F + test_operator::O + end + +A metacondition representing a scalar comparison method. +Here, the `feature` is a scalar function that can be computed on a world +of an instance of a logical dataset. +A test operator is a binary mathematical relation, comparing the computed feature value +and an external threshold value (see `ScalarCondition`). A metacondition can also be used +for representing the infinite set of conditions that arise with a free threshold +(see `UnboundedExplicitConditionalAlphabet`): \${min(V1) ≥ a, a ∈ ℝ}\$. + +See also +[`AbstractCondition`](@ref), +[`negation`](@ref), +[`ScalarCondition`](@ref). +""" +struct ScalarMetaCondition{F<:AbstractFeature,O<:TestOperator} <: AbstractCondition{F} + + # Feature: a scalar function that can be computed on a world + feature::F + + # Test operator (e.g. ≥) + test_operator::O + +end + +# TODO +# featuretype(::Type{<:ScalarMetaCondition{F}}) where {F<:AbstractFeature} = F +# featuretype(m::ScalarMetaCondition) = featuretype(typeof(F)) + +feature(m::ScalarMetaCondition) = m.feature +test_operator(m::ScalarMetaCondition) = m.test_operator + +negation(m::ScalarMetaCondition) = ScalarMetaCondition(feature(m), inverse_test_operator(test_operator(m))) + +syntaxstring(m::ScalarMetaCondition; kwargs...) = + "$(_syntaxstring_metacondition(m; kwargs...)) ⍰" + +function _syntaxstring_metacondition( + m::ScalarMetaCondition; + use_feature_abbreviations::Bool = false, + kwargs..., +) + if use_feature_abbreviations + _st_featop_abbr(feature(m), test_operator(m); kwargs...) + else + _st_featop_name(feature(m), test_operator(m); kwargs...) + end +end + +_st_featop_name(feature::AbstractFeature, test_operator::TestOperator; kwargs...) = "$(syntaxstring(feature; kwargs...)) $(test_operator)" + +# Abbreviations + +_st_featop_abbr(feature::AbstractFeature, test_operator::TestOperator; kwargs...) = _st_featop_name(feature, test_operator; kwargs...) + +############################################################################################ + +""" + struct ScalarCondition{U,F,M<:ScalarMetaCondition{F}} <: AbstractCondition{F} + metacond::M + a::U + end + +A scalar condition comparing a computed feature value (see `ScalarMetaCondition`) +and a threshold value `a`. +It can be evaluated on a world of an instance of a logical dataset. + +For example: \$min(V1) ≥ 10\$, which translates to +"Within this world, the minimum of variable 1 is greater or equal than 10." +In this case, the feature a [`UnivariateMin`](@ref) object. + +See also +[`AbstractCondition`](@ref), +[`negation`](@ref), +[`ScalarMetaCondition`](@ref). +""" +struct ScalarCondition{U,F,M<:ScalarMetaCondition{F}} <: AbstractCondition{F} + + # Metacondition + metacond::M + + # Threshold value + threshold::U + + function ScalarCondition( + metacond :: M, + threshold :: U + ) where {F<:AbstractFeature,M<:ScalarMetaCondition{F},U} + new{U,F,M}(metacond, threshold) + end + + function ScalarCondition( + condition :: ScalarCondition{U,M}, + threshold :: U + ) where {F<:AbstractFeature,M<:ScalarMetaCondition{F},U} + new{U,F,M}(metacond(condition), threshold) + end + + function ScalarCondition( + feature :: AbstractFeature, + test_operator :: TestOperator, + threshold :: U + ) where {U} + metacond = ScalarMetaCondition(feature, test_operator) + ScalarCondition(metacond, threshold) + end +end + +metacond(c::ScalarCondition) = c.metacond +threshold(c::ScalarCondition) = c.threshold + +feature(c::ScalarCondition) = feature(metacond(c)) +test_operator(c::ScalarCondition) = test_operator(metacond(c)) + +negation(c::ScalarCondition) = ScalarCondition(negation(metacond(c)), threshold(c)) + +syntaxstring(m::ScalarCondition; threshold_decimals = nothing, kwargs...) = + "$(_syntaxstring_metacondition(metacond(m); kwargs...)) $((isnothing(threshold_decimals) ? threshold(m) : round(threshold(m); digits=threshold_decimals)))" + +############################################################################################ +############################################################################################ +############################################################################################ + +""" + abstract type AbstractConditionalAlphabet{C<:ScalarCondition} <: AbstractAlphabet{C} end + +Abstract type for alphabets of conditions. + +See also +[`ScalarCondition`](@ref), +[`ScalarMetaCondition`](@ref), +[`AbstractAlphabet`](@ref). +""" +abstract type AbstractConditionalAlphabet{C<:ScalarCondition} <: AbstractAlphabet{C} end + +""" + struct UnboundedExplicitConditionalAlphabet{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} + metaconditions::Vector{<:ScalarMetaCondition} + end + +An infinite alphabet of conditions induced from a finite set of metaconditions. +For example, if `metaconditions = [ScalarMetaCondition(UnivariateMin(1), ≥)]`, +the alphabet represents the (infinite) set: \${min(V1) ≥ a, a ∈ ℝ}\$. + +See also +[`BoundedExplicitConditionalAlphabet`](@ref), +[`ScalarCondition`](@ref), +[`ScalarMetaCondition`](@ref), +[`AbstractAlphabet`](@ref). +""" +struct UnboundedExplicitConditionalAlphabet{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} + metaconditions::Vector{<:ScalarMetaCondition} + + function UnboundedExplicitConditionalAlphabet{C}( + metaconditions::Vector{<:ScalarMetaCondition} + ) where {C<:ScalarCondition} + new{C}(metaconditions) + end + + function UnboundedExplicitConditionalAlphabet( + features :: AbstractVector{C}, + test_operators :: AbstractVector, + ) where {C<:ScalarCondition} + metaconditions = + [ScalarMetaCondition(f, t) for f in features for t in test_operators] + UnboundedExplicitConditionalAlphabet{C}(metaconditions) + end +end + +Base.isfinite(::Type{<:UnboundedExplicitConditionalAlphabet}) = false + +function Base.in(p::Proposition{<:ScalarCondition}, a::UnboundedExplicitConditionalAlphabet) + fc = atom(p) + idx = findfirst(mc->mc == metacond(fc), a.metaconditions) + return !isnothing(idx) +end + +""" + struct BoundedExplicitConditionalAlphabet{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} + grouped_featconditions::Vector{Tuple{<:ScalarMetaCondition,Vector}} + end + +A finite alphabet of conditions, grouped by (a finite set of) metaconditions. + +See also +[`UnboundedExplicitConditionalAlphabet`](@ref), +[`ScalarCondition`](@ref), +[`ScalarMetaCondition`](@ref), +[`AbstractAlphabet`](@ref). +""" +# Finite alphabet of conditions induced from a set of metaconditions +struct BoundedExplicitConditionalAlphabet{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} + grouped_featconditions::Vector{<:Tuple{ScalarMetaCondition,Vector}} + + function BoundedExplicitConditionalAlphabet{C}( + grouped_featconditions::Vector{<:Tuple{ScalarMetaCondition,Vector}} + ) where {C<:ScalarCondition} + new{C}(grouped_featconditions) + end + + function BoundedExplicitConditionalAlphabet{C}( + metaconditions::Vector{<:ScalarMetaCondition}, + thresholds::Vector{<:Vector}, + ) where {C<:ScalarCondition} + length(metaconditions) != length(thresholds) && + error("Can't instantiate BoundedExplicitConditionalAlphabet with mismatching" * + " number of `metaconditions` and `thresholds`" * + " ($(metaconditions) != $(thresholds)).") + grouped_featconditions = collect(zip(metaconditions, thresholds)) + BoundedExplicitConditionalAlphabet{C}(grouped_featconditions) + end + + function BoundedExplicitConditionalAlphabet( + features :: AbstractVector{C}, + test_operators :: AbstractVector, + thresholds :: Vector + ) where {C<:ScalarCondition} + metaconditions = + [ScalarMetaCondition(f, t) for f in features for t in test_operators] + BoundedExplicitConditionalAlphabet{C}(metaconditions, thresholds) + end +end + +function propositions(a::BoundedExplicitConditionalAlphabet) + Iterators.flatten( + map( + ((mc,thresholds),)->map( + threshold->Proposition(ScalarCondition(mc, threshold)), + thresholds), + a.grouped_featconditions + ) + ) |> collect +end + +function Base.in(p::Proposition{<:ScalarCondition}, a::BoundedExplicitConditionalAlphabet) + fc = atom(p) + grouped_featconditions = a.grouped_featconditions + idx = findfirst(((mc,thresholds),)->mc == metacond(fc), grouped_featconditions) + return !isnothing(idx) && Base.in(threshold(fc), last(grouped_featconditions[idx])) +end diff --git a/src/conditional-data/test-operators.jl b/src/datasets/scalar-datasets/test-operators.jl similarity index 100% rename from src/conditional-data/test-operators.jl rename to src/datasets/scalar-datasets/test-operators.jl diff --git a/src/dimensional-datasets/datasets/featured-dataset.jl b/src/dimensional-datasets/datasets/featured-dataset.jl deleted file mode 100644 index d3bf4d4..0000000 --- a/src/dimensional-datasets/datasets/featured-dataset.jl +++ /dev/null @@ -1,380 +0,0 @@ - -############################################################################################ -# Featured world dataset -############################################################################################ -# -# In the most general case, the representation of a modal dataset is based on a -# multi-dimensional lookup table, referred to as *propositional lookup table*, -# or *featured world dataset* (abbreviated into fwd). -# -# This structure, is such that the value at fwd[i, w, f], referred to as *gamma*, -# is the value of feature f on world w on the i-th instance, and can be used to answer the -# question whether a proposition (e.g., minimum(A1) ≥ 10) holds onto a given world and instance; -# however, an fwd table can be implemented in many ways, mainly depending on the world type. -# -# Note that this structure does not constitute a AbstractActiveFeaturedDataset (see FeaturedDataset a few lines below) -# -############################################################################################ - -abstract type AbstractFWD{V<:Number,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} end - -# # A function for getting a threshold value from the lookup table -# Maybe TODO: but fails with ArgumentError: invalid index: − of type SoleLogics.OneWorld: -fwdread(fwd::AbstractFWD, args...) = Base.getindex(fwd, args...) -@inline function Base.getindex( - fwd :: AbstractFWD{V,W}, - i_sample :: Integer, - w :: W, - i_feature :: Integer -) where {V,W<:AbstractWorld} - error("TODO provide...") -end - -# -# Actually, the interface for AbstractFWD's is a bit tricky; the most straightforward -# way of learning it is by considering the fallback fwd structure defined as follows. -# TODO oh, but the implementation is broken due to a strange error (see https://discourse.julialang.org/t/tricky-too-many-parameters-for-type-error/25182 ) - -# # The most generic fwd structure is a matrix of dictionaries of size (nsamples × nfeatures) -# struct GenericFWD{V,W} <: AbstractFWD{V,W} -# d :: AbstractVector{<:AbstractDict{W,AbstractVector{V,1}},1} -# nfeatures :: Integer -# end - -# nsamples(fwd::GenericFWD{V}) where {V} = size(fwd, 1) -# nfeatures(fwd::GenericFWD{V}) where {V} = fwd.d -# Base.size(fwd::GenericFWD{V}, args...) where {V} = size(fwd.d, args...) - -# # The matrix is initialized with #undef values -# function fwd_init(::Type{GenericFWD}, X::DimensionalFeaturedDataset{V}) where {V} -# d = Array{Dict{W,V}, 2}(undef, nsamples(X)) -# for i in 1:nsamples -# d[i] = Dict{W,Array{V,1}}() -# end -# GenericFWD{V}(d, nfeatures(X)) -# end - -# # A function for initializing individual world slices -# function fwd_init_world_slice(fwd::GenericFWD{V}, i_sample::Integer, w::AbstractWorld) where {V} -# fwd.d[i_sample][w] = Array{V,1}(undef, fwd.nfeatures) -# end - -# # A function for getting a threshold value from the lookup table -# Base.@propagate_inbounds @inline fwdread( -# fwd :: GenericFWD{V}, -# i_sample :: Integer, -# w :: AbstractWorld, -# i_feature :: Integer) where {V} = fwd.d[i_sample][w][i_feature] - -# # A function for setting a threshold value in the lookup table -# Base.@propagate_inbounds @inline function fwd_set(fwd::GenericFWD{V}, w::AbstractWorld, i_sample::Integer, i_feature::Integer, threshold::V) where {V} -# fwd.d[i_sample][w][i_feature] = threshold -# end - -# # A function for setting threshold values for a single feature (from a feature slice, experimental) -# Base.@propagate_inbounds @inline function fwd_set_feature(fwd::GenericFWD{V}, i_feature::Integer, fwdslice::Any) where {V} -# throw_n_log("Warning! fwd_set_feature with GenericFWD is not yet implemented!") -# for ((i_sample,w),threshold::V) in read_fwdslice(fwdslice) -# fwd.d[i_sample][w][i_feature] = threshold -# end -# end - -# # A function for slicing the dataset -# function _slice_dataset(fwd::GenericFWD{V}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {V} -# GenericFWD{V}(if return_view == Val(true) @view fwd.d[inds] else fwd.d[inds] end, fwd.nfeatures) -# end - -# Others... -# Base.@propagate_inbounds @inline fwdread_channeaoeu(fwd::GenericFWD{V}, i_sample::Integer, i_feature::Integer) where {V} = TODO -# const GenericFeaturedChannel{V} = TODO -# fwd_channel_interpret_world(fwc::GenericFeaturedChannel{V}, w::AbstractWorld) where {V} = TODO - -isminifiable(::AbstractFWD) = true - -function minify(fwd::AbstractFWD) - minify(fwd.d) #TODO improper -end - -############################################################################################ -# Explicit modal dataset -# -# An explicit modal dataset is the generic form of a modal dataset, and consists of -# a wrapper around an fwd lookup table. The information it adds are the relation set, -# a few functions for enumerating worlds (`accessibles`, `representatives`), -# and a world set initialization function representing initial conditions (initializing world sets). -# -############################################################################################ - -struct FeaturedDataset{ - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - FT<:AbstractFeature{V}, - FWD<:AbstractFWD{V,W,FR}, - G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, - G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, -} <: AbstractActiveFeaturedDataset{V,W,FR,FT} - - # Core data (fwd lookup table) - fwd :: FWD - - ## Modal frame: - # Accessibility relations - relations :: AbstractVector{<:AbstractRelation} - - # Features - features :: Vector{FT} - - # Test operators associated with each feature, grouped by their respective aggregator - grouped_featsaggrsnops :: G1 - - grouped_featsnaggrs :: G2 - - # Initial world(s) - initialworld :: Union{Nothing,W,AbstractWorldSet{<:W}} - - function FeaturedDataset{V,W,FR,FT,FWD}( - fwd :: FWD, - relations :: AbstractVector{<:AbstractRelation}, - features :: AbstractVector{FT}, - grouped_featsaggrsnops :: AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}; - allow_no_instances = false, - initialworld = nothing, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FWD<:AbstractFWD{V,W,FR},FT<:AbstractFeature{V}} - features = collect(features) - ty = "FeaturedDataset{$(V),$(W),$(FR),$(FT)}" - @assert allow_no_instances || nsamples(fwd) > 0 "Can't instantiate $(ty) with no instance. (fwd's type $(typeof(fwd)))" - @assert length(grouped_featsaggrsnops) > 0 && sum(length.(grouped_featsaggrsnops)) > 0 && sum(vcat([[length(test_ops) for test_ops in aggrs] for aggrs in grouped_featsaggrsnops]...)) > 0 "Can't instantiate $(ty) with no test operator: grouped_featsaggrsnops" - @assert nfeatures(fwd) == length(features) "Can't instantiate $(ty) with different numbers of instances $(nsamples(fwd)) and of features $(length(features))." - grouped_featsnaggrs = features_grouped_featsaggrsnops2grouped_featsnaggrs(features, grouped_featsaggrsnops) - check_initialworld(FeaturedDataset, initialworld, W) - new{ - V, - W, - FR, - FT, - FWD, - typeof(grouped_featsaggrsnops), - typeof(grouped_featsnaggrs) - }( - fwd, - relations, - features, - grouped_featsaggrsnops, - grouped_featsnaggrs, - initialworld, - ) - end - - function FeaturedDataset{V,W,FR}( - fwd :: FWD, - relations :: AbstractVector{<:AbstractRelation}, - features :: AbstractVector{<:AbstractFeature}, - args...; - kwargs... - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FWD<:AbstractFWD{V,W,FR}} - features = collect(features) - FT = Union{typeof.(features)...} - features = Vector{FT}(features) - FeaturedDataset{V,W,FR,FT,FWD}(fwd, relations, features, args...; kwargs...) - end - - function FeaturedDataset{V,W}( - fwd :: AbstractFWD{V,W,FR}, - args...; - kwargs... - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - FeaturedDataset{V,W,FR}(fwd, args...; kwargs...) - end - - function FeaturedDataset( - fwd :: AbstractFWD{V,W}, - relations :: AbstractVector{<:AbstractRelation}, - features :: AbstractVector{<:AbstractFeature}, - grouped_featsaggrsnops_or_featsnops, # AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}} - args...; - kwargs..., - ) where {V,W} - FeaturedDataset{V,W}(fwd, relations, features, grouped_featsaggrsnops_or_featsnops, args...; kwargs...) - end - - function FeaturedDataset( - fwd :: AbstractFWD{V,W}, - relations :: AbstractVector{<:AbstractRelation}, - features :: AbstractVector{<:AbstractFeature}, - grouped_featsnops :: AbstractVector{<:AbstractVector{<:TestOperator}}, - args...; - kwargs..., - ) where {V,W<:AbstractWorld} - grouped_featsaggrsnops = grouped_featsnops2grouped_featsaggrsnops(grouped_featsnops) - FeaturedDataset(fwd, relations, features, grouped_featsaggrsnops, args...; kwargs...) - end - - _default_fwd_type(::Type{<:AbstractWorld}) = error("No GenericFWD has been implemented yet. Please provide a `fwd_type` parameter, as in: FeaturedDataset(X, IntervalFWD)") - _default_fwd_type(::Type{<:Union{OneWorld,Interval,Interval2D}}) = UniformFullDimensionalFWD - - # Quite importantly, an fwd can be computed from a dataset in implicit form (domain + ontology + features) - Base.@propagate_inbounds function FeaturedDataset( - X :: DimensionalFeaturedDataset{V,N,W}, - # fwd_type :: Type{<:AbstractFWD} = _default_fwd_type(W), # TODO - fwd_type = _default_fwd_type(W), - args...; - kwargs..., - ) where {V,N,W<:AbstractWorld} - - fwd = begin - - # Initialize the fwd structure - fwd = fwd_type(X) - - # @logmsg LogOverview "DimensionalFeaturedDataset -> FeaturedDataset" - - _features = features(X) - - _n_samples = nsamples(X) - - # Load any (possible) external features - if any(isa.(_features, ExternalFWDFeature)) - i_external_features = first.(filter(((i_feature,is_external_fwd),)->(is_external_fwd), collect(enumerate(isa.(_features, ExternalFWDFeature))))) - for i_feature in i_external_features - feature = _features[i_feature] - fwdslice_set(fwd, i_feature, feature.fwd) - end - end - - # Load any internal features - i_features = first.(filter(((i_feature,is_external_fwd),)->!(is_external_fwd), collect(enumerate(isa.(_features, ExternalFWDFeature))))) - enum_features = zip(i_features, _features[i_features]) - - # Compute features - # p = Progress(_n_samples, 1, "Computing EMD...") - @inbounds Threads.@threads for i_sample in 1:_n_samples - # @logmsg LogDebug "Instance $(i_sample)/$(_n_samples)" - - # if i_sample == 1 || ((i_sample+1) % (floor(Int, ((_n_samples)/4))+1)) == 0 - # @logmsg LogOverview "Instance $(i_sample)/$(_n_samples)" - # end - - for w in allworlds(X, i_sample) - - fwd_init_world_slice(fwd, i_sample, w) - - # @logmsg LogDebug "World" w - - for (i_feature,feature) in enum_features - - gamma = X[i_sample, w, feature, i_feature] - - # @logmsg LogDebug "Feature $(i_feature)" gamma - - fwd[w, i_sample, i_feature] = gamma - - end - end - # next!(p) - end - fwd - end - - FeaturedDataset( - fwd, - relations(X), - _features, - grouped_featsaggrsnops(X), - args...; - initialworld = SoleLogics.initialworld(X), - kwargs..., - ) - end - -end - - -@inline function Base.getindex( - X::FeaturedDataset{V,W}, - i_sample::Integer, - w::W, - feature::AbstractFeature, - args... -) where {V,W<:AbstractWorld} - i_feature = find_feature_id(X, feature) - # X[i_sample, w, feature, i_feature, args...]::V - fwd(X)[i_sample, w, i_feature, args...]::V -end - -@inline function Base.getindex( - X::FeaturedDataset{V,W}, - i_sample::Integer, - w::W, - feature::AbstractFeature, - i_feature::Integer, - args... -) where {V,W<:AbstractWorld} - fwd(X)[i_sample, w, i_feature, args...]::V -end - -Base.size(X::FeaturedDataset) = Base.size(fwd(X)) - -fwd(X::FeaturedDataset) = X.fwd -relations(X::FeaturedDataset) = X.relations -features(X::FeaturedDataset) = X.features -grouped_featsaggrsnops(X::FeaturedDataset) = X.grouped_featsaggrsnops -grouped_featsnaggrs(X::FeaturedDataset) = X.grouped_featsnaggrs - -nfeatures(X::FeaturedDataset) = length(features(X)) -nrelations(X::FeaturedDataset) = length(relations(X)) -nsamples(X::FeaturedDataset) = nsamples(fwd(X)) -worldtype(X::FeaturedDataset{V,W}) where {V,W<:AbstractWorld} = W - -nfeatsnaggrs(X::FeaturedDataset) = sum(length.(grouped_featsnaggrs(X))) - -frame(X::FeaturedDataset, i_sample) = frame(fwd(X), i_sample) -initialworld(X::FeaturedDataset) = X.initialworld -function initialworld(X::FeaturedDataset, i_sample) - initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_sample] : initialworld(X) -end - -function _slice_dataset(X::FeaturedDataset, inds::AbstractVector{<:Integer}, args...; kwargs...) - FeaturedDataset( - _slice_dataset(fwd(X), inds, args...; kwargs...), - relations(X), - features(X), - grouped_featsaggrsnops(X); - initialworld = initialworld(X) - ) -end - - -function display_structure(X::FeaturedDataset; indent_str = "") - out = "$(typeof(X))\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "├ relations: \t$((length(relations(X))))\t$(relations(X))\n" - out *= indent_str * "├ fwd: \t$(typeof(fwd(X)))\t$(Base.summarysize(fwd(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "└ initialworld(s)\t$(initialworld(X))" - out -end - -function hasnans(X::FeaturedDataset) - # @show hasnans(fwd(X)) - hasnans(fwd(X)) -end - - -isminifiable(::FeaturedDataset) = true - -function minify(X::FeaturedDataset) - new_fwd, backmap = minify(fwd(X)) - X = FeaturedDataset( - new_fwd, - relations(X), - features(X), - grouped_featsaggrsnops(X), - ) - X, backmap -end - -############################################################################################ -############################################################################################ -############################################################################################ - -# World-specific featured world datasets and supports -include("dimensional-fwds.jl") diff --git a/src/dimensional-datasets/datasets/supported-featured-dataset.jl b/src/dimensional-datasets/datasets/supported-featured-dataset.jl deleted file mode 100644 index 20b2414..0000000 --- a/src/dimensional-datasets/datasets/supported-featured-dataset.jl +++ /dev/null @@ -1,192 +0,0 @@ - -############################################################################################ -# Explicit modal dataset with support -########################################################################################### - -# The lookup table (fwd) in a featured modal dataset provides a quick answer on the truth of -# propositional decisions; as for answering modal decisions (e.g., ⟨L⟩ (minimum(A2) ≥ 10) ) -# with an fwd, one must enumerate the accessible worlds, compute the truth on each world, -# and aggregate the answer (by means of all/any). This process is costly; instead, it is -# sometimes more convenient to initially spend more time computing the truth of any decision, -# and store this information in a *support* lookup table. Similarly, one can decide to deploy -# memoization on this table (instead of computing everything at the beginning, compute it on -# the fly and store it for later calls). -# -# We define an abstract type for explicit modal dataset with support lookup tables -# remove: abstract type ExplicitModalDatasetWithSupport{V,W,FR} <: AbstractActiveFeaturedDataset{V,W,FR,FT} end -# And an abstract type for support lookup tables -abstract type AbstractSupport{V,W} end - -function nonnothingshare(support::AbstractSupport) - (isinf(capacity(support)) ? NaN : nmemoizedvalues(support)/capacity(support)) -end -# -# In general, one can use lookup (with or without memoization) for any decision, even the -# more complex ones, for example: -# ⟨G⟩ (minimum(A2) ≥ 10 ∧ (⟨O⟩ (maximum(A3) > 2) ∨ (minimum(A1) < 0))) -# -# In practice, decision trees only ask about simple decisions such as ⟨L⟩ (minimum(A2) ≥ 10), -# or ⟨G⟩ (maximum(A2) ≤ 50). Because the global operator G behaves differently from other -# relations, it is natural to differentiate between global and relational support tables: -# -abstract type AbstractRelationalSupport{V,W,FR<:AbstractFrame} <: AbstractSupport{V,W} end -abstract type AbstractGlobalSupport{V} <: AbstractSupport{V,W where W<:AbstractWorld} end -# -# Be an *fwd_rs* an fwd relational support, and a *fwd_gs* an fwd global support, -# for simple support tables like these, it is convenient to store, again, modal *gamma* values. -# Similarly to fwd, gammas are basically values on the verge of truth, that can straightforwardly -# anser simple modal questions. -# Consider the decision (w ⊨ f ⋈ a) on the i-th instance, for a given feature f, -# world w, relation R and test operator ⋈, and let gamma (γ) be: -# - fwd_rs[i, f, a, R, w] if R is a regular relation, or -# - fwd_gs[i, f, a] if R is the global relation G, -# where a = aggregator(⋈). In this context, γ is the unique value for which w ⊨ f ⋈ γ holds and: -# - if aggregator(⋈) = minimum: ∀ a > γ: (w ⊨ f ⋈ a) does not hold -# - if aggregator(⋈) = maximum: ∀ a < γ: (w ⊨ f ⋈ a) does not hold -# -# Let us define the world type-agnostic implementations for fwd_rs and fwd_gs (note that any fwd_gs -# is actually inherently world agnostic); world type-specific implementations can be defined -# in a similar way. - -############################################################################################ -############################################################################################ - -isminifiable(::Union{AbstractRelationalSupport,AbstractGlobalSupport}) = true - -function minify(support::Union{AbstractRelationalSupport,AbstractGlobalSupport}) - minify(support.d) #TODO improper -end - -############################################################################################ -# Finally, let us define the implementation for explicit modal dataset with support -############################################################################################ - - -struct SupportedFeaturedDataset{ - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - FT<:AbstractFeature{V}, - S<:FeaturedSupportingDataset{V,W,FR}, -} <: AbstractActiveFeaturedDataset{V,W,FR,FT} - - # Core dataset - fd :: FeaturedDataset{V,W,FR,FT} - - # Support structure - support :: S - - ######################################################################################## - - function SupportedFeaturedDataset{V,W,FR,FT,S}( - fd :: FeaturedDataset{V,W,FR,FT}, - support :: S; - allow_no_instances = false, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FT<:AbstractFeature{V},S<:FeaturedSupportingDataset{V,W,FR}} - ty = "SupportedFeaturedDataset{$(V),$(W),$(FR),$(FT),$(S)}" - @assert allow_no_instances || nsamples(fd) > 0 "Can't instantiate $(ty) with no instance." - @assert checksupportconsistency(fd, support) "Can't instantiate $(ty) with an inconsistent support:\n\nemd:\n$(display_structure(fd))\n\nsupport:\n$(display_structure(support))" - new{V,W,FR,FT,S}(fd, support) - end - - function SupportedFeaturedDataset{V,W,FR,FT}(fd::FeaturedDataset{V,W}, support::S, args...; kwargs...) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FT<:AbstractFeature{V},S<:FeaturedSupportingDataset{V,W,FR}} - SupportedFeaturedDataset{V,W,FR,FT,S}(fd, support, args...; kwargs...) - end - - function SupportedFeaturedDataset{V,W,FR}(fd::FeaturedDataset{V,W,FR,FT}, args...; kwargs...) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FT<:AbstractFeature{V}} - SupportedFeaturedDataset{V,W,FR,FT}(fd, args...; kwargs...) - end - - function SupportedFeaturedDataset{V,W}(fd::FeaturedDataset{V,W,FR}, args...; kwargs...) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - SupportedFeaturedDataset{V,W,FR}(fd, args...; kwargs...) - end - - function SupportedFeaturedDataset{V}(fd::FeaturedDataset{V,W,FR}, args...; kwargs...) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - SupportedFeaturedDataset{V,W}(fd, args...; kwargs...) - end - - function SupportedFeaturedDataset(fd::FeaturedDataset{V,W,FR}, args...; kwargs...) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - SupportedFeaturedDataset{V}(fd, args...; kwargs...) - end - - ######################################################################################## - - function SupportedFeaturedDataset( - fd :: FeaturedDataset{V,W,FR}; - compute_relation_glob :: Bool = true, - use_memoization :: Bool = true, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - - support = OneStepFeaturedSupportingDataset( - fd, - compute_relation_glob = compute_relation_glob, - use_memoization = use_memoization - ); - - SupportedFeaturedDataset(fd, support) - end - - ######################################################################################## - - function SupportedFeaturedDataset( - X :: DimensionalFeaturedDataset{V,N,W}; - kwargs..., - ) where {V,N,W<:AbstractWorld} - SupportedFeaturedDataset(FeaturedDataset(X); kwargs...) - end - -end - -fd(X::SupportedFeaturedDataset) = X.fd -support(X::SupportedFeaturedDataset) = X.support - -Base.getindex(X::SupportedFeaturedDataset, args...) = Base.getindex(fd(X), args...)::featvaltype(X) -Base.size(X::SupportedFeaturedDataset) = (size(fd(X)), size(support(X))) -features(X::SupportedFeaturedDataset) = features(fd(X)) -grouped_featsaggrsnops(X::SupportedFeaturedDataset) = grouped_featsaggrsnops(fd(X)) -grouped_featsnaggrs(X::SupportedFeaturedDataset) = grouped_featsnaggrs(fd(X)) -nfeatures(X::SupportedFeaturedDataset) = nfeatures(fd(X)) -nrelations(X::SupportedFeaturedDataset) = nrelations(fd(X)) -nsamples(X::SupportedFeaturedDataset) = nsamples(fd(X)) -relations(X::SupportedFeaturedDataset) = relations(fd(X)) -fwd(X::SupportedFeaturedDataset) = fwd(fd(X)) -worldtype(X::SupportedFeaturedDataset{V,W}) where {V,W} = W - -usesmemo(X::SupportedFeaturedDataset) = usesmemo(support(X)) - -frame(X::SupportedFeaturedDataset, i_sample) = frame(fd(X), i_sample) -initialworld(X::SupportedFeaturedDataset, args...) = initialworld(fd(X), args...) - -function _slice_dataset(X::SupportedFeaturedDataset, inds::AbstractVector{<:Integer}, args...; kwargs...) - SupportedFeaturedDataset( - _slice_dataset(fd(X), inds, args...; kwargs...), - _slice_dataset(support(X), inds, args...; kwargs...), - ) -end - -hasnans(X::SupportedFeaturedDataset) = hasnans(fd(X)) || hasnans(support(X)) - -isminifiable(X::SupportedFeaturedDataset) = isminifiable(fd(X)) && isminifiable(fd(X)) - -function minify(X::EMD) where {EMD<:SupportedFeaturedDataset} - (new_emd, new_support), backmap = - minify([ - fd(X), - support(X), - ]) - - X = EMD( - new_emd, - new_support, - ) - X, backmap -end - -function display_structure(X::SupportedFeaturedDataset; indent_str = "") - out = "$(typeof(X))\t$((Base.summarysize(fd(X)) + Base.summarysize(support(X))) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "├ relations: \t$((length(relations(fd(X)))))\t$(relations(fd(X)))\n" - out *= indent_str * "├ fd\t$(Base.summarysize(fd(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs" - out *= "\t(shape $(Base.size(fd(X))))\n" - out *= indent_str * "└ support: $(display_structure(support(X); indent_str = " "))" - out -end diff --git a/src/conditional-data/minify.jl b/src/utils/minify.jl similarity index 100% rename from src/conditional-data/minify.jl rename to src/utils/minify.jl diff --git a/test/datasets.jl b/test/datasets.jl index ae0e0cd..30856ea 100644 --- a/test/datasets.jl +++ b/test/datasets.jl @@ -7,14 +7,14 @@ using SoleModels.DimensionalDatasets X = Array(reshape(1.0:180.0, 3,3,2,10)) ontology = get_interval_ontology(2) -@test_nowarn DimensionalFeaturedDataset{Float64}(X, ontology, [SoleModels.CanonicalFeatureGeq(), SoleModels.CanonicalFeatureLeq()]) +@test_nowarn DimensionalLogiset{Float64}(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]) -dfd = @test_nowarn DimensionalFeaturedDataset(X, ontology, [SoleModels.CanonicalFeatureGeq(), SoleModels.CanonicalFeatureLeq()]) +dfd = @test_nowarn DimensionalLogiset(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]) -@test_nowarn DimensionalFeaturedDataset{Float64}(X, ontology, [minimum, maximum]) -dfd2 = @test_nowarn DimensionalFeaturedDataset(X, ontology, [minimum, maximum]) +@test_nowarn DimensionalLogiset{Float64}(X, ontology, [minimum, maximum]) +dfd2 = @test_nowarn DimensionalLogiset(X, ontology, [minimum, maximum]) -@test_throws AssertionError DimensionalFeaturedDataset(X, ontology, [StatsBase.mean]) +@test_throws AssertionError DimensionalLogiset(X, ontology, [StatsBase.mean]) @test_nowarn dfd |> alphabet |> propositions @@ -22,12 +22,29 @@ dfd2 = @test_nowarn DimensionalFeaturedDataset(X, ontology, [minimum, maximum]) @test_nowarn length(collect(propositions(alphabet(dfd)))) @test length(collect(propositions(alphabet(dfd))))*2 == length(collect(propositions(alphabet(dfd2)))) -@test all(((propositions(dfd |> SupportedFeaturedDataset |> alphabet))) .== +@test all(((propositions(dfd |> SupportedScalarLogiset |> alphabet))) .== ((propositions(dfd |> alphabet)))) -dfd3 = @test_nowarn DimensionalFeaturedDataset{Float64}(X, ontology, [SoleModels.CanonicalFeatureGeq(), SoleModels.CanonicalFeatureLeq()]; initialworld = Interval2D((2,3),(2,3))) +dfd3 = @test_nowarn DimensionalLogiset{Float64}(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]; initialworld = Interval2D((2,3),(2,3))) check(SyntaxTree(⊤), dfd, 1) check(SyntaxTree(⊤), dfd2, 1) check(SyntaxTree(⊤), dfd3, 1) + + + +X = Array(reshape(1.0:180.0, 3,3,2,10)); + + +d = DimensionalLogiset{UInt16}( +X, +get_ontology(2, :interval, :RCC5), +[minimum, maximum]; +initialworld = SoleLogics.Interval2D((2,3),(2,3)), +) +@assert SoleLogics.Interval2D((2,3),(2,3)) == initialworld(d) +@assert SoleLogics.Interval2D((2,3),(2,3)) == initialworld(d, 1) + + + From 95072e6e5da1c21b6d81c83a88505254588f246f Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 2 Jun 2023 15:46:37 +0200 Subject: [PATCH 07/77] sample->instance; attribute->variable. --- src/SoleModels.jl | 5 +- src/datasets/base/check.jl | 24 ++-- src/datasets/base/conditions.jl | 7 + .../base/generic-supporting-datasets.jl | 12 +- src/datasets/base/generic-supports.jl | 30 ++--- src/datasets/base/logiset-interface.jl | 22 ++-- src/datasets/base/logiset.jl | 50 ++++---- src/datasets/base/main.jl | 6 +- src/datasets/base/multiframe-logiset.jl | 6 +- .../datasets/dimensional-fwds.jl | 110 ++++++++-------- .../datasets/dimensional-logiset.jl | 48 +++---- .../datasets/dimensional-supports.jl | 120 +++++++++--------- .../dimensional-datasets/datasets/main.jl | 4 +- .../datasets/passive-dimensional-dataset.jl | 14 +- .../dimensional-features.jl | 108 ++++++++-------- .../dimensional-datasets/gamma-access.jl | 16 +-- src/datasets/dimensional-datasets/main.jl | 28 ++-- .../parse-dimensional-condition.jl | 52 ++++---- .../scalar-datasets/active-scalar-logiset.jl | 10 +- src/datasets/scalar-datasets/gamma-access.jl | 60 ++++----- .../one-step-featured-supporting-dataset.jl | 54 ++++---- src/models/base.jl | 52 ++++---- src/models/rule-evaluation.jl | 4 +- test/parse.jl | 8 +- test/runtests.jl | 4 +- 25 files changed, 433 insertions(+), 421 deletions(-) diff --git a/src/SoleModels.jl b/src/SoleModels.jl index bfca502..e9936df 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -58,14 +58,15 @@ export AbstractFeature, export propositions -export computefeature +export computefeature, parsecondition +export frames # Definitions for logical datasets (i.e., logisets) include("datasets/base/main.jl") # include("datasets/scalar-datasets/main.jl") -# export nsamples, nframes, frames, nfeatures +# export ninstances, nframes, frames, nfeatures # export get_ontology, # get_interval_ontology diff --git a/src/datasets/base/check.jl b/src/datasets/base/check.jl index 9d236c2..e1eb991 100644 --- a/src/datasets/base/check.jl +++ b/src/datasets/base/check.jl @@ -3,11 +3,11 @@ @inline function check( p::Proposition{<:ScalarCondition}, X::AbstractScalarLogiset{W}, - i_sample::Integer, + i_instance::Integer, w::W, ) where {W<:AbstractWorld} c = atom(p) - apply_test_operator(SoleModels.test_operator(c), X[i_sample, w, SoleModels.feature(c)], SoleModels.threshold(c)) + apply_test_operator(SoleModels.test_operator(c), X[i_instance, w, SoleModels.feature(c)], SoleModels.threshold(c)) end @@ -23,8 +23,8 @@ hasformula(memo_structure::AbstractDict{SyntaxTree}, φ::AbstractFormula) = hask function check( φ::SoleLogics.AbstractFormula, X::AbstractLogiset{W,<:AbstractFeature,<:Number,FR}, - i_sample::Integer; - initialworld::Union{Nothing,W,AbstractVector{<:W}} = SoleLogics.initialworld(X, i_sample), + i_instance::Integer; + initialworld::Union{Nothing,W,AbstractVector{<:W}} = SoleLogics.initialworld(X, i_instance), # use_memo::Union{Nothing,AbstractVector{<:AbstractDict{<:F,<:T}}} = nothing, # use_memo::Union{Nothing,AbstractVector{<:AbstractDict{<:F,<:WorldSet{W}}}} = nothing, use_memo::Union{Nothing,AbstractVector{<:AbstractDict{<:F,<:WorldSet}}} = nothing, @@ -37,7 +37,7 @@ function check( if isnothing(use_memo) ThreadSafeDict{SyntaxTree,WorldSet{W}}() else - use_memo[i_sample] + use_memo[i_instance] end end @@ -47,7 +47,7 @@ function check( # φ = normalize(φ; profile = :modelchecking) # TODO normalize formula and/or use a dedicate memoization structure that normalizes functions - fr = frame(X, i_sample) + fr = frame(X, i_instance) # TODO avoid using when memo is nothing if !hasformula(memo_structure, φ) @@ -63,7 +63,7 @@ function check( if tok isa SoleLogics.AbstractOperator collect(SoleLogics.collateworlds(fr, tok, map(f->memo_structure[f], children(ψ)))) elseif tok isa Proposition - filter(w->check(tok, X, i_sample, w), collect(allworlds(fr))) + filter(w->check(tok, X, i_instance, w), collect(allworlds(fr))) else error("Unexpected token encountered in _check: $(typeof(tok))") end @@ -101,7 +101,7 @@ end # function compute_chained_threshold( # φ::SoleLogics.AbstractFormula, # X::SupportedScalarLogiset{V,W,FR}, -# i_sample; +# i_instance; # use_memo::Union{Nothing,AbstractVector{<:AbstractDict{F,T}}} = nothing, # ) where {V<:Number,W<:AbstractWorld,T<:Bool,FR<:AbstractMultiModalFrame{W,T},F<:SoleLogics.AbstractFormula} @@ -111,13 +111,13 @@ end # if isnothing(use_memo) # ThreadSafeDict{SyntaxTree,V}() # else -# use_memo[i_sample] +# use_memo[i_instance] # end # end # # φ = normalize(φ; profile = :modelchecking) # TODO normalize formula and/or use a dedicate memoization structure that normalizes functions -# fr = frame(X, i_sample) +# fr = frame(X, i_instance) # if !hasformula(memo_structure, φ) # for ψ in unique(SoleLogics.subformulas(φ)) @@ -128,10 +128,10 @@ end # featcond = atom(token(children(φ)[1])) # if tok isa DiamondRelationalOperator # # (L) f > a <-> max(acc) > a -# onestep_accessible_aggregation(X, i_sample, w, relation(tok), feature(featcond), existential_aggregator(test_operator(featcond))) +# onestep_accessible_aggregation(X, i_instance, w, relation(tok), feature(featcond), existential_aggregator(test_operator(featcond))) # elseif tok isa BoxRelationalOperator # # [L] f > a <-> min(acc) > a <-> ! (min(acc) <= a) <-> ¬ (f <= a) -# onestep_accessible_aggregation(X, i_sample, w, relation(tok), feature(featcond), universal_aggregator(test_operator(featcond))) +# onestep_accessible_aggregation(X, i_instance, w, relation(tok), feature(featcond), universal_aggregator(test_operator(featcond))) # else # error("Unexpected operator encountered in onestep_collateworlds: $(typeof(tok))") # end diff --git a/src/datasets/base/conditions.jl b/src/datasets/base/conditions.jl index 8789513..20f3388 100644 --- a/src/datasets/base/conditions.jl +++ b/src/datasets/base/conditions.jl @@ -33,3 +33,10 @@ end Base.isequal(a::AbstractCondition, b::AbstractCondition) = syntaxstring(a) == syntaxstring(b) # nameof(x) == nameof(feature) Base.hash(a::AbstractCondition) = Base.hash(syntaxstring(a)) + +function parsecondition( + expression::String; + kwargs... +) + error("Please, provide method parsecondition(expression::$(typeof(expression)); kwargs...).") +end diff --git a/src/datasets/base/generic-supporting-datasets.jl b/src/datasets/base/generic-supporting-datasets.jl index 6c94f1d..1b5c4e7 100644 --- a/src/datasets/base/generic-supporting-datasets.jl +++ b/src/datasets/base/generic-supporting-datasets.jl @@ -16,9 +16,9 @@ struct GenericSupportingDataset{ function GenericSupportingDataset( fd :: Logiset{V,W,FR}, ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, nsamples(fd)) - for i_sample in 1:nsamples(fd) - memo[i_sample] = ThreadSafeDict{AbstractFormula,Vector{W}}() + memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, ninstances(fd)) + for i_instance in 1:ninstances(fd) + memo[i_instance] = ThreadSafeDict{AbstractFormula,Vector{W}}() end GenericSupportingDataset{W,FR,typeof(memo)}(memo) end @@ -58,9 +58,9 @@ struct ChainedFeaturedSupportingDataset{ function ChainedFeaturedSupportingDataset( fd :: Logiset{V,W,FR}, ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, nsamples(fd)) - for i_sample in 1:nsamples(fd) - memo[i_sample] = ThreadSafeDict{AbstractFormula,Vector{W}}() + memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, ninstances(fd)) + for i_instance in 1:ninstances(fd) + memo[i_instance] = ThreadSafeDict{AbstractFormula,Vector{W}}() end ChainedFeaturedSupportingDataset{V,W,FR,typeof(memo)}(memo) end diff --git a/src/datasets/base/generic-supports.jl b/src/datasets/base/generic-supports.jl index e048084..dbabb3f 100644 --- a/src/datasets/base/generic-supports.jl +++ b/src/datasets/base/generic-supports.jl @@ -20,10 +20,10 @@ struct GenericRelationalSupport{ _nfeatsnaggrs = nfeatsnaggrs(fd) _fwd_rs = begin if perform_initialization - _fwd_rs = Array{Dict{W,Union{V,Nothing}}, 3}(undef, nsamples(fd), _nfeatsnaggrs, nrelations(fd)) + _fwd_rs = Array{Dict{W,Union{V,Nothing}}, 3}(undef, ninstances(fd), _nfeatsnaggrs, nrelations(fd)) fill!(_fwd_rs, nothing) else - Array{Dict{W,V}, 3}(undef, nsamples(fd), _nfeatsnaggrs, nrelations(fd)) + Array{Dict{W,V}, 3}(undef, ninstances(fd), _nfeatsnaggrs, nrelations(fd)) end end GenericRelationalSupport{V,W,FR}(_fwd_rs) @@ -37,7 +37,7 @@ function hasnans(support::GenericRelationalSupport) any(map(d->(any(_isnan.(collect(values(d))))), support.d)) end -nsamples(support::GenericRelationalSupport) = size(support, 1) +ninstances(support::GenericRelationalSupport) = size(support, 1) nfeatsnaggrs(support::GenericRelationalSupport) = size(support, 2) nrelations(support::GenericRelationalSupport) = size(support, 3) capacity(support::GenericRelationalSupport) = Inf @@ -45,19 +45,19 @@ nmemoizedvalues(support::GenericRelationalSupport) = sum(length.(support.d)) @inline function Base.getindex( support :: GenericRelationalSupport{V,W}, - i_sample :: Integer, + i_instance :: Integer, w :: W, i_featsnaggr :: Integer, i_relation :: Integer ) where {V,W<:AbstractWorld} - support.d[i_sample, i_featsnaggr, i_relation][w] + support.d[i_instance, i_featsnaggr, i_relation][w] end Base.size(support::GenericRelationalSupport, args...) = size(support.d, args...) -fwd_rs_init_world_slice(support::GenericRelationalSupport{V,W}, i_sample::Integer, i_featsnaggr::Integer, i_relation::Integer) where {V,W} = - support.d[i_sample, i_featsnaggr, i_relation] = Dict{W,V}() -@inline function Base.setindex!(support::GenericRelationalSupport{V,W}, threshold::V, i_sample::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {V,W} - support.d[i_sample, i_featsnaggr, i_relation][w] = threshold +fwd_rs_init_world_slice(support::GenericRelationalSupport{V,W}, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) where {V,W} = + support.d[i_instance, i_featsnaggr, i_relation] = Dict{W,V}() +@inline function Base.setindex!(support::GenericRelationalSupport{V,W}, threshold::V, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {V,W} + support.d[i_instance, i_featsnaggr, i_relation][w] = threshold end function _slice_dataset(support::GenericRelationalSupport{V,W,FR}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {V,W,FR} GenericRelationalSupport{V,W,FR}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) @@ -79,7 +79,7 @@ struct GenericGlobalSupport{V,D<:AbstractArray{V,2}} <: AbstractGlobalSupport{V} function GenericGlobalSupport(fd::Logiset{V}) where {V} @assert worldtype(fd) != OneWorld "TODO adjust this note: note that you should not use a global support when not using global decisions" _nfeatsnaggrs = nfeatsnaggrs(fd) - GenericGlobalSupport{V}(Array{V,2}(undef, nsamples(fd), _nfeatsnaggrs)) + GenericGlobalSupport{V}(Array{V,2}(undef, ninstances(fd), _nfeatsnaggrs)) end end @@ -93,16 +93,16 @@ function hasnans(support::GenericGlobalSupport) any(_isnan.(support.d)) end -nsamples(support::GenericGlobalSupport) = size(support, 1) +ninstances(support::GenericGlobalSupport) = size(support, 1) nfeatsnaggrs(support::GenericGlobalSupport) = size(support, 2) Base.getindex( support :: GenericGlobalSupport, - i_sample :: Integer, - i_featsnaggr :: Integer) = support.d[i_sample, i_featsnaggr] + i_instance :: Integer, + i_featsnaggr :: Integer) = support.d[i_instance, i_featsnaggr] Base.size(support::GenericGlobalSupport{V}, args...) where {V} = size(support.d, args...) -Base.setindex!(support::GenericGlobalSupport{V}, threshold::V, i_sample::Integer, i_featsnaggr::Integer) where {V} = - support.d[i_sample, i_featsnaggr] = threshold +Base.setindex!(support::GenericGlobalSupport{V}, threshold::V, i_instance::Integer, i_featsnaggr::Integer) where {V} = + support.d[i_instance, i_featsnaggr] = threshold function _slice_dataset(support::GenericGlobalSupport{V}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {V} GenericGlobalSupport{V}(if return_view == Val(true) @view support.d[inds,:] else support.d[inds,:] end) end diff --git a/src/datasets/base/logiset-interface.jl b/src/datasets/base/logiset-interface.jl index 2080cbe..f288e39 100644 --- a/src/datasets/base/logiset-interface.jl +++ b/src/datasets/base/logiset-interface.jl @@ -36,21 +36,21 @@ abstract type AbstractLogiset{ function evaluatecondition( X::AbstractLogiset{W,V,F,T}, - i_sample, + i_instance, w::W, c::AbstractCondition, )::T where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue} - error("Please, provide method evaluatecondition(X::$(typeof(X)), i_sample::$(typeof(i_sample)), w::$(typeof(w)), c::$(typeof(c))).") + error("Please, provide method evaluatecondition(X::$(typeof(X)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), c::$(typeof(c))).") end function check( f::AbstractFormula, X::AbstractLogiset{W,V,F,T}, - i_sample, + i_instance, w::W, )::T where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue} # TODO implement once for all. - error("Please, provide method check(f::$(typeof(f)), X::$(typeof(X)), i_sample::$(typeof(i_sample)), w::$(typeof(w))).") + error("Please, provide method check(f::$(typeof(f)), X::$(typeof(X)), i_instance::$(typeof(i_instance)), w::$(typeof(w))).") end function displaystructure(X::AbstractLogiset; kwargs...)::String @@ -60,11 +60,11 @@ end function check( p::Proposition{A}, X::AbstractLogiset{W,V,F,T}, - i_sample, + i_instance, w::W, )::T where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue,A<:AbstractCondition} cond = atom(p) - evaluatecondition(X, i_sample, w, cond) + evaluatecondition(X, i_instance, w, cond) end function Base.show(io::IO, X::AbstractLogiset; kwargs...) @@ -85,23 +85,23 @@ end function featvalue( X::AbstractLogiset{W}, - i_sample, + i_instance, w::W, f::AbstractFeature, ) where {W<:AbstractWorld} - error("Please, provide method featvalue(::$(typeof(X)), i_sample::$(typeof(i_sample)), w::$(typeof(w)), f::$(typeof(f))).") + error("Please, provide method featvalue(::$(typeof(X)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), f::$(typeof(f))).") end isminifiable(::AbstractLogiset) = false function initialworld( X::AbstractLogiset{W,V,F,T}, - i_sample + i_instance ) where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue} - error("Please, provide method initialworld(::$(typeof(X)), i_sample::$(typeof(i_sample))).") + error("Please, provide method initialworld(::$(typeof(X)), i_instance::$(typeof(i_instance))).") end -representatives(X::AbstractLogiset, i_sample, args...) = representatives(frame(X, i_sample), args...) +representatives(X::AbstractLogiset, i_instance, args...) = representatives(frame(X, i_instance), args...) ############################################################################################ # Helpers diff --git a/src/datasets/base/logiset.jl b/src/datasets/base/logiset.jl index 0dc8f0f..3592e1f 100644 --- a/src/datasets/base/logiset.jl +++ b/src/datasets/base/logiset.jl @@ -19,7 +19,7 @@ featvaltype(d::AbstractFeatureLookupSet) = featvaltype(typeof(d)) """ @inline function featvalue( featstruct :: AbstractFeatureLookupSet{V,FR}, - i_sample :: Integer, + i_instance :: Integer, w :: W, feature :: F, ) where {V,F<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} @@ -31,17 +31,17 @@ See also """ function featvalue( featstruct :: AbstractFeatureLookupSet{V,FR}, - i_sample :: Integer, + i_instance :: Integer, w :: W, feature :: F, )::V where {V,F<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} - error("Please, provide method featvalue(::$(typeof(featstruct)), i_sample::$(typeof(i_sample)), w::$(typeof(w)), feature::$(typeof(feature)))::$(V).") + error("Please, provide method featvalue(::$(typeof(featstruct)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), feature::$(typeof(feature)))::$(V).") end """ @inline function featvalue( featstruct :: AbstractFeatureLookupSet{V,FR}, - i_sample :: Integer, + i_instance :: Integer, w :: W, i_feature :: Integer, ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} @@ -53,11 +53,11 @@ See also """ function featvalue( featstruct :: AbstractFeatureLookupSet{V,FR}, - i_sample :: Integer, + i_instance :: Integer, w :: W, i_feature :: Integer, )::V where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} - error("Please, provide method featvalue(::$(typeof(featstruct)), i_sample::$(typeof(i_sample)), w::$(typeof(w)), i_feature::$(typeof(i_feature)))::$(V).") + error("Please, provide method featvalue(::$(typeof(featstruct)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), i_feature::$(typeof(i_feature)))::$(V).") end @inline Base.getindex(featstruct::AbstractFeatureLookupSet, args...) = featvalue(featstruct, args...) @@ -66,7 +66,7 @@ end ############################################################################################ ############################################################################################ -# # The most generic featstruct structure is a matrix of dictionaries of size (nsamples × nfeatures) +# # The most generic featstruct structure is a matrix of dictionaries of size (ninstances × nfeatures) # struct GenericFWD{ # V, # F<:AbstractFeature{V}, @@ -79,40 +79,40 @@ end # end # Base.size(featstruct::GenericFWD{V}, args...) where {V} = size(featstruct.d, args...) -# nsamples(featstruct::GenericFWD{V}) where {V} = size(featstruct, 1) +# ninstances(featstruct::GenericFWD{V}) where {V} = size(featstruct, 1) # nfeatures(featstruct::GenericFWD{V}) where {V} = featstruct.d # # The matrix is initialized with #undef values # function fwd_init(::Type{GenericFWD}, X::AbstractLogiset{W,V}) where {W,V} -# d = Array{Dict{W,V}, 2}(undef, nsamples(X)) -# for i in 1:nsamples +# d = Array{Dict{W,V}, 2}(undef, ninstances(X)) +# for i in 1:ninstances # d[i] = Dict{W,Array{V,1}}() # end # GenericFWD{V}(d, nfeatures(X)) # end # # A function for initializing individual world slices -# function fwd_init_world_slice(featstruct::GenericFWD{V}, i_sample::Integer, w::AbstractWorld) where {V} -# featstruct.d[i_sample][w] = Array{V,1}(undef, featstruct.nfeatures) +# function fwd_init_world_slice(featstruct::GenericFWD{V}, i_instance::Integer, w::AbstractWorld) where {V} +# featstruct.d[i_instance][w] = Array{V,1}(undef, featstruct.nfeatures) # end # # A function for getting a threshold value from the lookup table # Base.@propagate_inbounds @inline featvalue( # featstruct :: GenericFWD{V}, -# i_sample :: Integer, +# i_instance :: Integer, # w :: AbstractWorld, -# i_feature :: Integer) where {V} = featstruct.d[i_sample][w][i_feature] +# i_feature :: Integer) where {V} = featstruct.d[i_instance][w][i_feature] # # A function for setting a threshold value in the lookup table -# Base.@propagate_inbounds @inline function fwd_set(featstruct::GenericFWD{V}, w::AbstractWorld, i_sample::Integer, i_feature::Integer, threshold::V) where {V} -# featstruct.d[i_sample][w][i_feature] = threshold +# Base.@propagate_inbounds @inline function fwd_set(featstruct::GenericFWD{V}, w::AbstractWorld, i_instance::Integer, i_feature::Integer, threshold::V) where {V} +# featstruct.d[i_instance][w][i_feature] = threshold # end # # A function for setting threshold values for a single feature (from a feature slice, experimental) # Base.@propagate_inbounds @inline function fwd_set_feature(featstruct::GenericFWD{V}, i_feature::Integer, fwdslice::Any) where {V} # throw_n_log("Warning! fwd_set_feature with GenericFWD is not yet implemented!") -# for ((i_sample,w),threshold::V) in read_fwdslice(fwdslice) -# featstruct.d[i_sample][w][i_feature] = threshold +# for ((i_instance,w),threshold::V) in read_fwdslice(fwdslice) +# featstruct.d[i_instance][w][i_feature] = threshold # end # end @@ -122,7 +122,7 @@ end # end # Others... -# Base.@propagate_inbounds @inline fwdread_channeaoeu(featstruct::GenericFWD{V}, i_sample::Integer, i_feature::Integer) where {V} = TODO +# Base.@propagate_inbounds @inline fwdread_channeaoeu(featstruct::GenericFWD{V}, i_instance::Integer, i_feature::Integer) where {V} = TODO # const GenericFeaturedChannel{V} = TODO # fwd_channel_interpret_world(fwc::GenericFeaturedChannel{V}, w::AbstractWorld) where {V} = TODO @@ -168,8 +168,8 @@ struct Logiset{ ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FWD<:AbstractFeatureLookupSet{V,FR},F<:AbstractFeature{V}} features = collect(features) ty = "Logiset{$(V),$(W),$(FR),$(F)}" - @assert allow_no_instances || nsamples(featstruct) > 0 "Can't instantiate $(ty) with no instance. (featstruct's type $(typeof(featstruct)))" - @assert nfeatures(featstruct) == length(features) "Can't instantiate $(ty) with different numbers of instances $(nsamples(featstruct)) and of features $(length(features))." + @assert allow_no_instances || ninstances(featstruct) > 0 "Can't instantiate $(ty) with no instance. (featstruct's type $(typeof(featstruct)))" + @assert nfeatures(featstruct) == length(features) "Can't instantiate $(ty) with different numbers of instances $(ninstances(featstruct)) and of features $(length(features))." check_initialworld(Logiset, initialworld, W) new{ V, @@ -231,13 +231,13 @@ features(X::Logiset) = X.features nfeatures(X::Logiset) = length(features(X)) nrelations(X::Logiset) = length(relations(X)) -nsamples(X::Logiset) = nsamples(featstruct(X)) +ninstances(X::Logiset) = ninstances(featstruct(X)) worldtype(X::Logiset{V,W}) where {V,W<:AbstractWorld} = W -frame(X::Logiset, i_sample) = frame(featstruct(X), i_sample) +frame(X::Logiset, i_instance) = frame(featstruct(X), i_instance) initialworld(X::Logiset) = X.initialworld -function initialworld(X::Logiset, i_sample) - initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_sample] : initialworld(X) +function initialworld(X::Logiset, i_instance) + initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_instance] : initialworld(X) end function _slice_dataset(X::Logiset, inds::AbstractVector{<:Integer}, args...; kwargs...) diff --git a/src/datasets/base/main.jl b/src/datasets/base/main.jl index 926a261..f611662 100644 --- a/src/datasets/base/main.jl +++ b/src/datasets/base/main.jl @@ -1,4 +1,4 @@ -import SoleBase: frame +import SoleLogics: frame using SoleLogics: OneWorld, Interval, Interval2D using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame @@ -6,8 +6,8 @@ using SoleLogics: X, Y, Z using SoleLogics: AbstractWorld, IdentityRel import SoleLogics: syntaxstring -import SoleData: nsamples, nfeatures -import SoleData: nframes, frames, hasnans, _slice_dataset +import SoleData: ninstances, nfeatures +import SoleData: hasnans, _slice_dataset # Features to be computed on worlds of dataset instances include("features.jl") diff --git a/src/datasets/base/multiframe-logiset.jl b/src/datasets/base/multiframe-logiset.jl index 29b0c0c..2fbc1b0 100644 --- a/src/datasets/base/multiframe-logiset.jl +++ b/src/datasets/base/multiframe-logiset.jl @@ -19,7 +19,7 @@ struct MultiFrameLogiset{MD<:AbstractLogiset} end function MultiFrameLogiset{MD}(Xs::AbstractVector) where {MD<:AbstractLogiset} Xs = collect(Xs) - @assert length(Xs) > 0 && length(unique(nsamples.(Xs))) == 1 "Can't create an empty MultiFrameLogiset or with mismatching number of samples (nframes: $(length(Xs)), frame_sizes: $(nsamples.(Xs)))." + @assert length(Xs) > 0 && length(unique(ninstances.(Xs))) == 1 "Can't create an empty MultiFrameLogiset or with mismatching number of instances (nframes: $(length(Xs)), frame_sizes: $(ninstances.(Xs)))." new{MD}(Xs) end function MultiFrameLogiset{MD}() where {MD<:AbstractLogiset} @@ -39,14 +39,14 @@ end frames(X::MultiFrameLogiset) = X.frames Base.iterate(X::MultiFrameLogiset, state=1) = state > length(X) ? nothing : (get_instance(X, state), state+1) -Base.length(X::MultiFrameLogiset) = nsamples(X) +Base.length(X::MultiFrameLogiset) = ninstances(X) Base.push!(X::MultiFrameLogiset, f::AbstractLogiset) = push!(frames(X), f) Base.size(X::MultiFrameLogiset) = map(size, frames(X)) frame(X::MultiFrameLogiset, i_frame::Integer) = nframes(X) > 0 ? frames(X)[i_frame] : error("MultiFrameLogiset has no frame!") nframes(X::MultiFrameLogiset) = length(frames(X)) -nsamples(X::MultiFrameLogiset) = nsamples(frame(X, 1)) +ninstances(X::MultiFrameLogiset) = ninstances(frame(X, 1)) # max_channel_size(X::MultiFrameLogiset) = map(max_channel_size, frames(X)) # TODO: figure if this is useless or not. Note: channel_size doesn't make sense at this point. nfeatures(X::MultiFrameLogiset) = map(nfeatures, frames(X)) # Note: used for safety checks in fit_tree.jl diff --git a/src/datasets/dimensional-datasets/datasets/dimensional-fwds.jl b/src/datasets/dimensional-datasets/datasets/dimensional-fwds.jl index 3b1b4e8..e8b4344 100644 --- a/src/datasets/dimensional-datasets/datasets/dimensional-fwds.jl +++ b/src/datasets/dimensional-datasets/datasets/dimensional-fwds.jl @@ -9,7 +9,7 @@ import Base: size, ndims, getindex, setindex! abstract type AbstractUniformFullDimensionalFWD{T,N,W<:AbstractWorld,FR<:FullDimensionalFrame{N,W,Bool}} <: AbstractFeatureLookupSet{T,W,FR} end channel_size(fwd::AbstractUniformFullDimensionalFWD) = error("TODO add message inviting to add channel_size") -frame(fwd::AbstractUniformFullDimensionalFWD, i_sample) = FullDimensionalFrame(channel_size(fwd)) +frame(fwd::AbstractUniformFullDimensionalFWD, i_instance) = FullDimensionalFrame(channel_size(fwd)) ############################################################################################ ############################################################################################ @@ -45,13 +45,13 @@ struct UniformFullDimensionalFWD{ ############################################################################################ function UniformFullDimensionalFWD(X::DimensionalLogiset{T,N,W}) where {T,N,W<:OneWorld} - UniformFullDimensionalFWD{T,W,N}(Array{T,2}(undef, nsamples(X), nfeatures(X))) + UniformFullDimensionalFWD{T,W,N}(Array{T,2}(undef, ninstances(X), nfeatures(X))) end function UniformFullDimensionalFWD(X::DimensionalLogiset{T,N,W}) where {T,N,W<:Interval} - UniformFullDimensionalFWD{T,W,N}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, nsamples(X), nfeatures(X))) + UniformFullDimensionalFWD{T,W,N}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, ninstances(X), nfeatures(X))) end function UniformFullDimensionalFWD(X::DimensionalLogiset{T,N,W}) where {T,N,W<:Interval2D} - UniformFullDimensionalFWD{T,W,N}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, nsamples(X), nfeatures(X))) + UniformFullDimensionalFWD{T,W,N}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, ninstances(X), nfeatures(X))) end end @@ -59,7 +59,7 @@ end Base.size(fwd::UniformFullDimensionalFWD, args...) = size(fwd.d, args...) Base.ndims(fwd::UniformFullDimensionalFWD, args...) = ndims(fwd.d, args...) -nsamples(fwd::UniformFullDimensionalFWD) = size(fwd, ndims(fwd)-1) +ninstances(fwd::UniformFullDimensionalFWD) = size(fwd, ndims(fwd)-1) nfeatures(fwd::UniformFullDimensionalFWD) = size(fwd, ndims(fwd)) ############################################################################################ @@ -75,14 +75,14 @@ function capacity(fwd::UniformFullDimensionalFWD{T,OneWorld}) where {T} end function capacity(fwd::UniformFullDimensionalFWD{T,<:Interval}) where {T} prod([ - nsamples(fwd), + ninstances(fwd), nfeatures(fwd), div(size(fwd, 1)*(size(fwd, 2)),2), ]) end function capacity(fwd::UniformFullDimensionalFWD{T,<:Interval2D}) where {T} prod([ - nsamples(fwd), + ninstances(fwd), nfeatures(fwd), div(size(fwd, 1)*(size(fwd, 2)),2), div(size(fwd, 3)*(size(fwd, 4)),2), @@ -106,7 +106,7 @@ end ############################################################################################ -function fwd_init_world_slice(fwd::UniformFullDimensionalFWD, i_sample::Integer, w::AbstractWorld) +function fwd_init_world_slice(fwd::UniformFullDimensionalFWD, i_instance::Integer, w::AbstractWorld) nothing end @@ -114,43 +114,43 @@ end @inline function Base.getindex( fwd :: UniformFullDimensionalFWD{T,OneWorld}, - i_sample :: Integer, + i_instance :: Integer, w :: OneWorld, i_feature :: Integer ) where {T} - fwd.d[i_sample, i_feature] + fwd.d[i_instance, i_feature] end @inline function Base.getindex( fwd :: UniformFullDimensionalFWD{T,<:Interval}, - i_sample :: Integer, + i_instance :: Integer, w :: Interval, i_feature :: Integer ) where {T} - fwd.d[w.x, w.y, i_sample, i_feature] + fwd.d[w.x, w.y, i_instance, i_feature] end @inline function Base.getindex( fwd :: UniformFullDimensionalFWD{T,<:Interval2D}, - i_sample :: Integer, + i_instance :: Integer, w :: Interval2D, i_feature :: Integer ) where {T} - fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_sample, i_feature] + fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] end ############################################################################################ -@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,OneWorld}, threshold::T, w::OneWorld, i_sample::Integer, i_feature::Integer) where {T} - fwd.d[i_sample, i_feature] = threshold +@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,OneWorld}, threshold::T, w::OneWorld, i_instance::Integer, i_feature::Integer) where {T} + fwd.d[i_instance, i_feature] = threshold end -@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,<:Interval}, threshold::T, w::Interval, i_sample::Integer, i_feature::Integer) where {T} - fwd.d[w.x, w.y, i_sample, i_feature] = threshold +@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,<:Interval}, threshold::T, w::Interval, i_instance::Integer, i_feature::Integer) where {T} + fwd.d[w.x, w.y, i_instance, i_feature] = threshold end -@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, threshold::T, w::Interval2D, i_sample::Integer, i_feature::Integer) where {T} - fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_sample, i_feature] = threshold +@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, threshold::T, w::Interval2D, i_instance::Integer, i_feature::Integer) where {T} + fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] = threshold end ############################################################################################ @@ -175,16 +175,16 @@ Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimension fwd.d[:, i_feature] = fwdslice end -Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,OneWorld}, i_sample::Integer, i_feature::Integer) where {T} = - fwd.d[i_sample, i_feature] +Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,OneWorld}, i_instance::Integer, i_feature::Integer) where {T} = + fwd.d[i_instance, i_feature] const OneWorldFeaturedChannel{T} = T fwd_channel_interpret_world(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimensionalFWD{T,<:Interval}, i_feature::Integer, fwdslice::Array{T,3}) where {T} fwd.d[:, :, :, i_feature] = fwdslice end -Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,<:Interval}, i_sample::Integer, i_feature::Integer) where {T} = - @views fwd.d[:,:,i_sample, i_feature] +Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,<:Interval}, i_instance::Integer, i_feature::Integer) where {T} = + @views fwd.d[:,:,i_instance, i_feature] const IntervalFeaturedChannel{T} = AbstractArray{T,2} fwd_channel_interpret_world(fwc::IntervalFeaturedChannel{T}, w::Interval) where {T} = fwc[w.x, w.y] @@ -193,8 +193,8 @@ fwd_channel_interpret_world(fwc::IntervalFeaturedChannel{T}, w::Interval) where Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, i_feature::Integer, fwdslice::Array{T,5}) where {T} fwd.d[:, :, :, :, :, i_feature] = fwdslice end -Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, i_sample::Integer, i_feature::Integer) where {T} = - @views fwd.d[:,:,:,:,i_sample, i_feature] +Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, i_instance::Integer, i_feature::Integer) where {T} = + @views fwd.d[:,:,:,:,i_instance, i_feature] const Interval2DFeaturedChannel{T} = AbstractArray{T,4} fwd_channel_interpret_world(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) where {T} = fwc[w.x.x, w.x.y, w.y.x, w.y.y] @@ -213,14 +213,14 @@ const FWDFeatureSlice{T} = Union{ # channel_size(fwd::OneWorldFWD) = () -# nsamples(fwd::OneWorldFWD) = size(fwd.d, 1) +# ninstances(fwd::OneWorldFWD) = size(fwd.d, 1) # nfeatures(fwd::OneWorldFWD) = size(fwd.d, 2) # function fwd_init(::Type{OneWorldFWD}, X::DimensionalLogiset{T,0,OneWorld}) where {T} -# OneWorldFWD{T}(Array{T,2}(undef, nsamples(X), nfeatures(X))) +# OneWorldFWD{T}(Array{T,2}(undef, ninstances(X), nfeatures(X))) # end -# function fwd_init_world_slice(fwd::OneWorldFWD, i_sample::Integer, w::AbstractWorld) +# function fwd_init_world_slice(fwd::OneWorldFWD, i_instance::Integer, w::AbstractWorld) # nothing # end @@ -228,12 +228,12 @@ const FWDFeatureSlice{T} = Union{ # Base.@propagate_inbounds @inline featvalue( # fwd :: OneWorldFWD{T}, -# i_sample :: Integer, +# i_instance :: Integer, # w :: OneWorld, -# i_feature :: Integer) where {T} = fwd.d[i_sample, i_feature] +# i_feature :: Integer) where {T} = fwd.d[i_instance, i_feature] -# @inline function Base.setindex!(fwd::OneWorldFWD{T},, threshold::T w::OneWorld, i_sample::Integer, i_feature::Integer) where {T} -# fwd.d[i_sample, i_feature] = threshold +# @inline function Base.setindex!(fwd::OneWorldFWD{T},, threshold::T w::OneWorld, i_instance::Integer, i_feature::Integer) where {T} +# fwd.d[i_instance, i_feature] = threshold # end # function _slice_dataset(fwd::OneWorldFWD{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} @@ -244,13 +244,13 @@ const FWDFeatureSlice{T} = Union{ # fwd.d[:, i_feature] = fwdslice # end -# Base.@propagate_inbounds @inline fwdread_channel(fwd::OneWorldFWD{T}, i_sample::Integer, i_feature::Integer) where {T} = -# fwd.d[i_sample, i_feature] +# Base.@propagate_inbounds @inline fwdread_channel(fwd::OneWorldFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = +# fwd.d[i_instance, i_feature] # const OneWorldFeaturedChannel{T} = T # fwd_channel_interpret_world(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc ############################################################################################ -# FWD, Interval: 4D array (x × y × nsamples × nfeatures) +# FWD, Interval: 4D array (x × y × ninstances × nfeatures) ############################################################################################ # struct IntervalFWD{T} <: UniformFullDimensionalFWD{T,1,<:Interval} @@ -259,14 +259,14 @@ const FWDFeatureSlice{T} = Union{ # channel_size(fwd::IntervalFWD) = (size(fwd, 1),) -# nsamples(fwd::IntervalFWD) = size(fwd, 3) +# ninstances(fwd::IntervalFWD) = size(fwd, 3) # nfeatures(fwd::IntervalFWD) = size(fwd, 4) # function fwd_init(::Type{IntervalFWD}, X::DimensionalLogiset{T,1,<:Interval}) where {T} -# IntervalFWD{T}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, nsamples(X), nfeatures(X))) +# IntervalFWD{T}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, ninstances(X), nfeatures(X))) # end -# function fwd_init_world_slice(fwd::IntervalFWD, i_sample::Integer, w::AbstractWorld) +# function fwd_init_world_slice(fwd::IntervalFWD, i_instance::Integer, w::AbstractWorld) # nothing # end @@ -277,12 +277,12 @@ const FWDFeatureSlice{T} = Union{ # Base.@propagate_inbounds @inline featvalue( # fwd :: IntervalFWD{T}, -# i_sample :: Integer, +# i_instance :: Integer, # w :: Interval, -# i_feature :: Integer) where {T} = fwd.d[w.x, w.y, i_sample, i_feature] +# i_feature :: Integer) where {T} = fwd.d[w.x, w.y, i_instance, i_feature] -# @inline function Base.setindex!(fwd::IntervalFWD{T},, threshold::T w::Interval, i_sample::Integer, i_feature::Integer) where {T} -# fwd.d[w.x, w.y, i_sample, i_feature] = threshold +# @inline function Base.setindex!(fwd::IntervalFWD{T},, threshold::T w::Interval, i_instance::Integer, i_feature::Integer) where {T} +# fwd.d[w.x, w.y, i_instance, i_feature] = threshold # end # Base.@propagate_inbounds @inline function fwdslice_set(fwd::IntervalFWD{T}, i_feature::Integer, fwdslice::Array{T,3}) where {T} @@ -292,14 +292,14 @@ const FWDFeatureSlice{T} = Union{ # function _slice_dataset(fwd::IntervalFWD{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} # IntervalFWD{T}(if return_view == Val(true) @view fwd.d[:,:,inds,:] else fwd.d[:,:,inds,:] end) # end -# Base.@propagate_inbounds @inline fwdread_channel(fwd::IntervalFWD{T}, i_sample::Integer, i_feature::Integer) where {T} = -# @views fwd.d[:,:,i_sample, i_feature] +# Base.@propagate_inbounds @inline fwdread_channel(fwd::IntervalFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = +# @views fwd.d[:,:,i_instance, i_feature] # const IntervalFeaturedChannel{T} = AbstractArray{T,2} # fwd_channel_interpret_world(fwc::IntervalFeaturedChannel{T}, w::Interval) where {T} = # fwc[w.x, w.y] ############################################################################################ -# FWD, Interval: 6D array (x.x × x.y × y.x × y.y × nsamples × nfeatures) +# FWD, Interval: 6D array (x.x × x.y × y.x × y.y × ninstances × nfeatures) ############################################################################################ # struct Interval2DFWD{T} <: UniformFullDimensionalFWD{T,2,<:Interval2D} @@ -308,15 +308,15 @@ const FWDFeatureSlice{T} = Union{ # channel_size(fwd::Interval2DFWD) = (size(fwd, 1),size(fwd, 3)) -# nsamples(fwd::Interval2DFWD) = size(fwd, 5) +# ninstances(fwd::Interval2DFWD) = size(fwd, 5) # nfeatures(fwd::Interval2DFWD) = size(fwd, 6) # function fwd_init(::Type{Interval2DFWD}, X::DimensionalLogiset{T,2,<:Interval2D}) where {T} -# Interval2DFWD{T}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, nsamples(X), nfeatures(X))) +# Interval2DFWD{T}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, ninstances(X), nfeatures(X))) # end -# function fwd_init_world_slice(fwd::Interval2DFWD, i_sample::Integer, w::AbstractWorld) +# function fwd_init_world_slice(fwd::Interval2DFWD, i_instance::Integer, w::AbstractWorld) # nothing # end @@ -327,12 +327,12 @@ const FWDFeatureSlice{T} = Union{ # Base.@propagate_inbounds @inline featvalue( # fwd :: Interval2DFWD{T}, -# i_sample :: Integer, +# i_instance :: Integer, # w :: Interval2D, -# i_feature :: Integer) where {T} = fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_sample, i_feature] +# i_feature :: Integer) where {T} = fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] -# @inline function Base.setindex!(fwd::Interval2DFWD{T},, threshold::T w::Interval2D, i_sample::Integer, i_feature::Integer) where {T} -# fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_sample, i_feature] = threshold +# @inline function Base.setindex!(fwd::Interval2DFWD{T},, threshold::T w::Interval2D, i_instance::Integer, i_feature::Integer) where {T} +# fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] = threshold # end # Base.@propagate_inbounds @inline function fwdslice_set(fwd::Interval2DFWD{T}, i_feature::Integer, fwdslice::Array{T,5}) where {T} @@ -342,8 +342,8 @@ const FWDFeatureSlice{T} = Union{ # function _slice_dataset(fwd::Interval2DFWD{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} # Interval2DFWD{T}(if return_view == Val(true) @view fwd.d[:,:,:,:,inds,:] else fwd.d[:,:,:,:,inds,:] end) # end -# Base.@propagate_inbounds @inline fwdread_channel(fwd::Interval2DFWD{T}, i_sample::Integer, i_feature::Integer) where {T} = -# @views fwd.d[:,:,:,:,i_sample, i_feature] +# Base.@propagate_inbounds @inline fwdread_channel(fwd::Interval2DFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = +# @views fwd.d[:,:,:,:,i_instance, i_feature] # const Interval2DFeaturedChannel{T} = AbstractArray{T,4} # fwd_channel_interpret_world(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) where {T} = # fwc[w.x.x, w.x.y, w.y.x, w.y.y] diff --git a/src/datasets/dimensional-datasets/datasets/dimensional-logiset.jl b/src/datasets/dimensional-datasets/datasets/dimensional-logiset.jl index d7e0018..32085ad 100644 --- a/src/datasets/dimensional-datasets/datasets/dimensional-logiset.jl +++ b/src/datasets/dimensional-datasets/datasets/dimensional-logiset.jl @@ -49,7 +49,7 @@ struct DimensionalLogiset{ features = collect(features) FT = Union{typeof.(features)...} features = Vector{FT}(features) - @assert allow_no_instances || nsamples(domain) > 0 "" * + @assert allow_no_instances || ninstances(domain) > 0 "" * "Can't instantiate $(ty) with no instance. (domain's type $(typeof(domain)))" @assert length(features) == length(grouped_featsaggrsnops) "" * "Can't instantiate $(ty) with mismatching length(features) and" * @@ -114,7 +114,7 @@ struct DimensionalLogiset{ # readymade features cnv_feat(cf::AbstractFeature) = ([≥, ≤], cf) cnv_feat(cf::Tuple{TestOperator,AbstractFeature}) = ([cf[1]], cf[2]) - # single-attribute features + # single-variable features cnv_feat(cf::Any) = cf cnv_feat(cf::CanonicalFeature) = cf cnv_feat(cf::Function) = ([≥, ≤], cf) @@ -127,35 +127,35 @@ struct DimensionalLogiset{ isa(x, Tuple{AbstractVector,AbstractFeature}), mixed_features, ) - attribute_specific_cfs = filter(x-> + variable_specific_cfs = filter(x-> isa(x, CanonicalFeature) || # isa(x, Tuple{<:AbstractVector{<:TestOperator},Function}) || (isa(x, Tuple{AbstractVector,Function}) && !isa(x, Tuple{AbstractVector,AbstractFeature})), mixed_features, ) - @assert length(readymade_cfs) + length(attribute_specific_cfs) == length(mixed_features) "" * + @assert length(readymade_cfs) + length(variable_specific_cfs) == length(mixed_features) "" * "Unexpected" * " mixed_features. $(mixed_features)." * - " $(filter(x->(! (x in readymade_cfs) && ! (x in attribute_specific_cfs)), mixed_features))." * - " $(length(readymade_cfs)) + $(length(attribute_specific_cfs)) == $(length(mixed_features))." + " $(filter(x->(! (x in readymade_cfs) && ! (x in variable_specific_cfs)), mixed_features))." * + " $(length(readymade_cfs)) + $(length(variable_specific_cfs)) == $(length(mixed_features))." for (test_ops,cf) in readymade_cfs push!(_features, cf) push!(featsnops, test_ops) end - single_attr_feats_n_featsnops(i_attr,cf::SoleModels.CanonicalFeatureGeq) = ([≥],DimensionalDatasets.UnivariateMin{V}(i_attr)) - single_attr_feats_n_featsnops(i_attr,cf::SoleModels.CanonicalFeatureLeq) = ([≤],DimensionalDatasets.UnivariateMax{V}(i_attr)) - single_attr_feats_n_featsnops(i_attr,cf::SoleModels.CanonicalFeatureGeqSoft) = ([≥],DimensionalDatasets.UnivariateSoftMin{V}(i_attr, cf.alpha)) - single_attr_feats_n_featsnops(i_attr,cf::SoleModels.CanonicalFeatureLeqSoft) = ([≤],DimensionalDatasets.UnivariateSoftMax{V}(i_attr, cf.alpha)) - single_attr_feats_n_featsnops(i_attr,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},typeof(minimum)}) = (test_ops,DimensionalDatasets.UnivariateMin{V}(i_attr)) - single_attr_feats_n_featsnops(i_attr,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},typeof(maximum)}) = (test_ops,DimensionalDatasets.UnivariateMax{V}(i_attr)) - single_attr_feats_n_featsnops(i_attr,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},Function}) = (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_attr, (x)->(V(cf(x))))) - single_attr_feats_n_featsnops(i_attr,::Any) = throw_n_log("Unknown mixed_feature type: $(cf), $(typeof(cf))") - - for i_attr in 1:nattributes(domain) - for (test_ops,cf) in map((cf)->single_attr_feats_n_featsnops(i_attr,cf),attribute_specific_cfs) + single_var_feats_n_featsnops(i_var,cf::SoleModels.CanonicalFeatureGeq) = ([≥],DimensionalDatasets.UnivariateMin{V}(i_var)) + single_var_feats_n_featsnops(i_var,cf::SoleModels.CanonicalFeatureLeq) = ([≤],DimensionalDatasets.UnivariateMax{V}(i_var)) + single_var_feats_n_featsnops(i_var,cf::SoleModels.CanonicalFeatureGeqSoft) = ([≥],DimensionalDatasets.UnivariateSoftMin{V}(i_var, cf.alpha)) + single_var_feats_n_featsnops(i_var,cf::SoleModels.CanonicalFeatureLeqSoft) = ([≤],DimensionalDatasets.UnivariateSoftMax{V}(i_var, cf.alpha)) + single_var_feats_n_featsnops(i_var,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},typeof(minimum)}) = (test_ops,DimensionalDatasets.UnivariateMin{V}(i_var)) + single_var_feats_n_featsnops(i_var,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},typeof(maximum)}) = (test_ops,DimensionalDatasets.UnivariateMax{V}(i_var)) + single_var_feats_n_featsnops(i_var,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},Function}) = (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_var, (x)->(V(cf(x))))) + single_var_feats_n_featsnops(i_var,::Any) = throw_n_log("Unknown mixed_feature type: $(cf), $(typeof(cf))") + + for i_var in 1:nvariables(domain) + for (test_ops,cf) in map((cf)->single_var_feats_n_featsnops(i_var,cf),variable_specific_cfs) push!(featsnops, test_ops) push!(_features, cf) end @@ -234,8 +234,8 @@ Base.size(X::DimensionalLogiset) = Base.size(domain(X)) dimensionality(X::DimensionalLogiset{V,N,W}) where {V,N,W} = N worldtype(X::DimensionalLogiset{V,N,W}) where {V,N,W} = W -nsamples(X::DimensionalLogiset) = nsamples(domain(X)) -nattributes(X::DimensionalLogiset) = nattributes(domain(X)) +ninstances(X::DimensionalLogiset) = ninstances(domain(X)) +nvariables(X::DimensionalLogiset) = nvariables(domain(X)) relations(X::DimensionalLogiset) = relations(ontology(X)) nrelations(X::DimensionalLogiset) = length(relations(X)) @@ -249,10 +249,10 @@ get_instance(X::DimensionalLogiset, args...) = get_instance(domain(X), args. _slice_dataset(X::DimensionalLogiset, inds::AbstractVector{<:Integer}, args...; kwargs...) = DimensionalLogiset(_slice_dataset(domain(X), inds, args...; kwargs...), ontology(X), features(X), grouped_featsaggrsnops(X); initialworld = initialworld(X)) -frame(X::DimensionalLogiset, i_sample) = frame(domain(X), i_sample) +frame(X::DimensionalLogiset, i_instance) = frame(domain(X), i_instance) initialworld(X::DimensionalLogiset) = X.initialworld -function initialworld(X::DimensionalLogiset, i_sample) - initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_sample] : initialworld(X) +function initialworld(X::DimensionalLogiset, i_instance) + initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_instance] : initialworld(X) end function displaystructure(X::DimensionalLogiset; indent_str = "") @@ -260,8 +260,8 @@ function displaystructure(X::DimensionalLogiset; indent_str = "") out *= indent_str * "├ relations:\t($((nrelations(X))))\t[$(join(syntaxstring.(relations(X)), ", "))]\n" out *= indent_str * "├ features:\t($((nfeatures(X))))\t[$(join(syntaxstring.(features(X)), ", "))]\n" out *= indent_str * "├ domain shape:\t\t$(Base.size(domain(X)))\n" - out *= indent_str * "├ nsamples:\t\t$(nsamples(X))\n" - out *= indent_str * "├ nattributes:\t\t$(nattributes(X))\n" + out *= indent_str * "├ ninstances:\t\t$(ninstances(X))\n" + out *= indent_str * "├ nvariables:\t\t$(nvariables(X))\n" out *= indent_str * "├ max_channel_size:\t$(max_channel_size(X))\n" out *= indent_str * "└ initialworld(s):\t$(initialworld(X))" out diff --git a/src/datasets/dimensional-datasets/datasets/dimensional-supports.jl b/src/datasets/dimensional-datasets/datasets/dimensional-supports.jl index 4e2c065..d626cb0 100644 --- a/src/datasets/dimensional-datasets/datasets/dimensional-supports.jl +++ b/src/datasets/dimensional-datasets/datasets/dimensional-supports.jl @@ -16,7 +16,7 @@ nmemoizedvalues(support::AbstractUniformFullDimensionalRelationalSupport) = (cap ############################################################################################ # FWD relational support for uniform full dimensional frames: -# a (nsamples × nfeatsnaggrs × nrelations) structure for each world. +# a (ninstances × nfeatsnaggrs × nrelations) structure for each world. # Each world is linearized, resulting in a (3+N*2)-D array ############################################################################################ @@ -46,10 +46,10 @@ struct UniformFullDimensionalRelationalSupport{ # error("TODO actually, using a relational or a global support with a OneWorld frame makes no sense. Figure out what to do here!") _fwd_rs = begin if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 3}(undef, nsamples(fwd), nfeatsnaggrs, nrelations) + _fwd_rs = Array{Union{T,Nothing}, 3}(undef, ninstances(fwd), nfeatsnaggrs, nrelations) fill!(_fwd_rs, nothing) else - Array{T,3}(undef, nsamples(fwd), nfeatsnaggrs, nrelations) + Array{T,3}(undef, ninstances(fwd), nfeatsnaggrs, nrelations) end end UniformFullDimensionalRelationalSupport{T,W,0,typeof(_fwd_rs)}(_fwd_rs) @@ -62,10 +62,10 @@ struct UniformFullDimensionalRelationalSupport{ ) where {T,W<:Interval} _fwd_rs = begin if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 5}(undef, size(fwd, 1), size(fwd, 2), nsamples(fwd), nfeatsnaggrs, nrelations) + _fwd_rs = Array{Union{T,Nothing}, 5}(undef, size(fwd, 1), size(fwd, 2), ninstances(fwd), nfeatsnaggrs, nrelations) fill!(_fwd_rs, nothing) else - Array{T,5}(undef, size(fwd, 1), size(fwd, 2), nsamples(fwd), nfeatsnaggrs, nrelations) + Array{T,5}(undef, size(fwd, 1), size(fwd, 2), ninstances(fwd), nfeatsnaggrs, nrelations) end end UniformFullDimensionalRelationalSupport{T,W,1,typeof(_fwd_rs)}(_fwd_rs) @@ -78,10 +78,10 @@ struct UniformFullDimensionalRelationalSupport{ ) where {T,W<:Interval2D} _fwd_rs = begin if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), nsamples(fwd), nfeatsnaggrs, nrelations) + _fwd_rs = Array{Union{T,Nothing}, 7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), ninstances(fwd), nfeatsnaggrs, nrelations) fill!(_fwd_rs, nothing) else - Array{T,7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), nsamples(fwd), nfeatsnaggrs, nrelations) + Array{T,7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), ninstances(fwd), nfeatsnaggrs, nrelations) end end UniformFullDimensionalRelationalSupport{T,W,2,typeof(_fwd_rs)}(_fwd_rs) @@ -99,7 +99,7 @@ end Base.size(support::UniformFullDimensionalRelationalSupport, args...) = size(support.d, args...) Base.ndims(support::UniformFullDimensionalRelationalSupport, args...) = ndims(support.d, args...) -nsamples(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)-2) +ninstances(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)-2) nfeatsnaggrs(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)-1) nrelations(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)) @@ -110,7 +110,7 @@ function capacity(support::UniformFullDimensionalRelationalSupport{T,OneWorld}) end function capacity(support::UniformFullDimensionalRelationalSupport{T,<:Interval}) where {T} prod([ - nsamples(support), + ninstances(support), nfeatsnaggrs(support), nrelations(support), div(size(support, 1)*(size(support, 2)),2), @@ -118,7 +118,7 @@ function capacity(support::UniformFullDimensionalRelationalSupport{T,<:Interval} end function capacity(support::UniformFullDimensionalRelationalSupport{T,<:Interval2D}) where {T} prod([ - nsamples(support), + ninstances(support), nfeatsnaggrs(support), nrelations(support), div(size(support, 1)*(size(support, 2)),2), @@ -145,7 +145,7 @@ end function fwd_rs_init_world_slice( support::UniformFullDimensionalRelationalSupport, - i_sample::Integer, + i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer ) @@ -158,30 +158,30 @@ end @inline function Base.getindex( support :: UniformFullDimensionalRelationalSupport{T,W}, - i_sample :: Integer, + i_instance :: Integer, w :: W, i_featsnaggr :: Integer, i_relation :: Integer ) where {T,W<:OneWorld} - support.d[i_sample, i_featsnaggr, i_relation] + support.d[i_instance, i_featsnaggr, i_relation] end @inline function Base.getindex( support :: UniformFullDimensionalRelationalSupport{T,W}, - i_sample :: Integer, + i_instance :: Integer, w :: W, i_featsnaggr :: Integer, i_relation :: Integer ) where {T,W<:Interval} - support.d[w.x, w.y, i_sample, i_featsnaggr, i_relation] + support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] end @inline function Base.getindex( support :: UniformFullDimensionalRelationalSupport{T,W}, - i_sample :: Integer, + i_instance :: Integer, w :: W, i_featsnaggr :: Integer, i_relation :: Integer ) where {T,W<:Interval2D} - support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_sample, i_featsnaggr, i_relation] + support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] end ############################################################################################ @@ -189,34 +189,34 @@ end Base.@propagate_inbounds @inline function Base.setindex!( support::UniformFullDimensionalRelationalSupport{T,OneWorld}, threshold::T, - i_sample::Integer, + i_instance::Integer, w::OneWorld, i_featsnaggr::Integer, i_relation::Integer, ) where {T} - support.d[i_sample, i_featsnaggr, i_relation] = threshold + support.d[i_instance, i_featsnaggr, i_relation] = threshold end Base.@propagate_inbounds @inline function Base.setindex!( support::UniformFullDimensionalRelationalSupport{T,<:Interval}, threshold::T, - i_sample::Integer, + i_instance::Integer, w::Interval, i_featsnaggr::Integer, i_relation::Integer, ) where {T} - support.d[w.x, w.y, i_sample, i_featsnaggr, i_relation] = threshold + support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] = threshold end Base.@propagate_inbounds @inline function Base.setindex!( support::UniformFullDimensionalRelationalSupport{T,<:Interval2D}, threshold::T, - i_sample::Integer, + i_instance::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer, ) where {T} - support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_sample, i_featsnaggr, i_relation] = threshold + support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] = threshold end ############################################################################################ @@ -244,47 +244,47 @@ function _slice_dataset( end ############################################################################################ -# FWD support, OneWorld: 3D array (nsamples × nfeatsnaggrs × nrelations) +# FWD support, OneWorld: 3D array (ninstances × nfeatsnaggrs × nrelations) ############################################################################################ # struct OneWorldFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,OneWorld} # d :: Array{T,3} # end -# nsamples(support::OneWorldFWD_RS) = size(support, 1) +# ninstances(support::OneWorldFWD_RS) = size(support, 1) # nfeatsnaggrs(support::OneWorldFWD_RS) = size(support, 2) # nrelations(support::OneWorldFWD_RS) = size(support, 3) # capacity(support::OneWorldFWD_RS) = prod(size(support.d)) # @inline Base.getindex( # support :: OneWorldFWD_RS{T}, -# i_sample :: Integer, +# i_instance :: Integer, # w :: OneWorld, # i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[i_sample, i_featsnaggr, i_relation] +# i_relation :: Integer) where {T} = support.d[i_instance, i_featsnaggr, i_relation] # Base.size(support::OneWorldFWD_RS, args...) = size(support.d, args...) # hasnans(support::OneWorldFWD_RS) = any(_isnan.(support.d)) # function fwd_rs_init(fd::Logiset{T,OneWorld}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} # if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 3}(undef, nsamples(fd), nfeatsnaggrs, nrelations), nothing) +# _fwd_rs = fill!(Array{Union{T,Nothing}, 3}(undef, ninstances(fd), nfeatsnaggrs, nrelations), nothing) # OneWorldFWD_RS{Union{T,Nothing}}(_fwd_rs) # else -# _fwd_rs = Array{T,3}(undef, nsamples(fd), nfeatsnaggrs, nrelations) +# _fwd_rs = Array{T,3}(undef, ninstances(fd), nfeatsnaggrs, nrelations) # OneWorldFWD_RS{T}(_fwd_rs) # end # end -# fwd_rs_init_world_slice(support::OneWorldFWD_RS, i_sample::Integer, i_featsnaggr::Integer, i_relation::Integer) = +# fwd_rs_init_world_slice(support::OneWorldFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = # nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::OneWorldFWD_RS{T}, threshold::T, i_sample::Integer, w::OneWorld, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[i_sample, i_featsnaggr, i_relation] = threshold +# Base.@propagate_inbounds @inline Base.setindex!(support::OneWorldFWD_RS{T}, threshold::T, i_instance::Integer, w::OneWorld, i_featsnaggr::Integer, i_relation::Integer) where {T} = +# support.d[i_instance, i_featsnaggr, i_relation] = threshold # function _slice_dataset(support::OneWorldFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} # OneWorldFWD_RS{T}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) # end ############################################################################################ -# FWD support, Interval: 5D array (x × y × nsamples × nfeatsnaggrs × nrelations) +# FWD support, Interval: 5D array (x × y × ninstances × nfeatsnaggrs × nrelations) ############################################################################################ @@ -292,18 +292,18 @@ end # d :: Array{T,5} # end -# nsamples(support::IntervalFWD_RS) = size(support, 3) +# ninstances(support::IntervalFWD_RS) = size(support, 3) # nfeatsnaggrs(support::IntervalFWD_RS) = size(support, 4) # nrelations(support::IntervalFWD_RS) = size(support, 5) # capacity(support::IntervalFWD_RS) = -# prod([nsamples(support), nfeatsnaggrs(support), nrelations(support), div(size(support.d, 1)*(size(support.d, 1)+1),2)]) +# prod([ninstances(support), nfeatsnaggrs(support), nrelations(support), div(size(support.d, 1)*(size(support.d, 1)+1),2)]) # @inline Base.getindex( # support :: IntervalFWD_RS{T}, -# i_sample :: Integer, +# i_instance :: Integer, # w :: Interval, # i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[w.x, w.y, i_sample, i_featsnaggr, i_relation] +# i_relation :: Integer) where {T} = support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] # Base.size(support::IntervalFWD_RS, args...) = size(support.d, args...) @@ -315,38 +315,38 @@ end # function fwd_rs_init(fd::Logiset{T,<:Interval}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} # _fwd = fd.fwd # if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, size(_fwd, 1), size(_fwd, 2), nsamples(fd), nfeatsnaggrs, nrelations), nothing) +# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, size(_fwd, 1), size(_fwd, 2), ninstances(fd), nfeatsnaggrs, nrelations), nothing) # IntervalFWD_RS{Union{T,Nothing}}(_fwd_rs) # else -# _fwd_rs = Array{T,5}(undef, size(_fwd, 1), size(_fwd, 2), nsamples(fd), nfeatsnaggrs, nrelations) +# _fwd_rs = Array{T,5}(undef, size(_fwd, 1), size(_fwd, 2), ninstances(fd), nfeatsnaggrs, nrelations) # IntervalFWD_RS{T}(_fwd_rs) # end # end -# fwd_rs_init_world_slice(support::IntervalFWD_RS, i_sample::Integer, i_featsnaggr::Integer, i_relation::Integer) = +# fwd_rs_init_world_slice(support::IntervalFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = # nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::IntervalFWD_RS{T}, threshold::T, i_sample::Integer, w::Interval, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[w.x, w.y, i_sample, i_featsnaggr, i_relation] = threshold +# Base.@propagate_inbounds @inline Base.setindex!(support::IntervalFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval, i_featsnaggr::Integer, i_relation::Integer) where {T} = +# support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] = threshold # function _slice_dataset(support::IntervalFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} # IntervalFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) # end ############################################################################################ -# FWD support, Interval2D: 7D array (x.x × x.y × y.x × y.y × nsamples × nfeatsnaggrs × nrelations) +# FWD support, Interval2D: 7D array (x.x × x.y × y.x × y.y × ninstances × nfeatsnaggrs × nrelations) ############################################################################################ # struct Interval2DFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,<:Interval2D} # d :: Array{T,7} # end -# nsamples(support::Interval2DFWD_RS) = size(support, 5) +# ninstances(support::Interval2DFWD_RS) = size(support, 5) # nfeatsnaggrs(support::Interval2DFWD_RS) = size(support, 6) # nrelations(support::Interval2DFWD_RS) = size(support, 7) # @inline Base.getindex( # support :: Interval2DFWD_RS{T}, -# i_sample :: Integer, +# i_instance :: Integer, # w :: Interval2D, # i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_sample, i_featsnaggr, i_relation] +# i_relation :: Integer) where {T} = support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] # size(support::Interval2DFWD_RS) = size(support.d, args...) # TODO... hasnans(support::Interval2DFWD_RS) = any(_isnan.(support.d)) @@ -355,24 +355,24 @@ end # fwd_rs_init(fd::Logiset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} = begin # _fwd = fd.fwd # if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), nsamples(fd), nfeatsnaggrs, nrelations), nothing) +# _fwd_rs = fill!(Array{Union{T,Nothing}, 7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), ninstances(fd), nfeatsnaggrs, nrelations), nothing) # Interval2DFWD_RS{Union{T,Nothing}}(_fwd_rs) # else -# _fwd_rs = Array{T,7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), nsamples(fd), nfeatsnaggrs, nrelations) +# _fwd_rs = Array{T,7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), ninstances(fd), nfeatsnaggrs, nrelations) # Interval2DFWD_RS{T}(_fwd_rs) # end # end -# fwd_rs_init_world_slice(support::Interval2DFWD_RS, i_sample::Integer, i_featsnaggr::Integer, i_relation::Integer) = +# fwd_rs_init_world_slice(support::Interval2DFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = # nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_sample::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_sample, i_featsnaggr, i_relation] = threshold +# Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = +# support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] = threshold # function _slice_dataset(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} # Interval2DFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,:,:,inds,:,:] else support.d[:,:,:,:,inds,:,:] end) # end ############################################################################################ -# FWD support, Interval2D: 7D array (linearized(x) × linearized(y) × nsamples × nfeatsnaggrs × nrelations) +# FWD support, Interval2D: 7D array (linearized(x) × linearized(y) × ninstances × nfeatsnaggrs × nrelations) ############################################################################################ # # TODO rewrite @@ -381,17 +381,17 @@ end # d :: Array{T,5} # end -# nsamples(support::Interval2DFWD_RS) = size(support, 3) +# ninstances(support::Interval2DFWD_RS) = size(support, 3) # nfeatsnaggrs(support::Interval2DFWD_RS) = size(support, 4) # nrelations(support::Interval2DFWD_RS) = size(support, 5) # capacity(support::Interval2DFWD_RS) = prod(size(support.d)) # @inline Base.getindex( # support :: Interval2DFWD_RS{T}, -# i_sample :: Integer, +# i_instance :: Integer, # w :: Interval2D, # i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[w.x.x+div((w.x.y-2)*(w.x.y-1),2), w.y.x+div((w.y.y-2)*(w.y.y-1),2), i_sample, i_featsnaggr, i_relation] +# i_relation :: Integer) where {T} = support.d[w.x.x+div((w.x.y-2)*(w.x.y-1),2), w.y.x+div((w.y.y-2)*(w.y.y-1),2), i_instance, i_featsnaggr, i_relation] # Base.size(support::Interval2DFWD_RS, args...) = size(support.d, args...) # hasnans(support::Interval2DFWD_RS) = any(_isnan.(support.d)) @@ -399,17 +399,17 @@ end # function fwd_rs_init(fd::Logiset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} # _fwd = fd.fwd # if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), nsamples(fd), nfeatsnaggrs, nrelations), nothing) +# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), ninstances(fd), nfeatsnaggrs, nrelations), nothing) # Interval2DFWD_RS{Union{T,Nothing}}(_fwd_rs) # else -# _fwd_rs = Array{T,5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), nsamples(fd), nfeatsnaggrs, nrelations) +# _fwd_rs = Array{T,5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), ninstances(fd), nfeatsnaggrs, nrelations) # Interval2DFWD_RS{T}(_fwd_rs) # end # end -# fwd_rs_init_world_slice(support::Interval2DFWD_RS, i_sample::Integer, i_featsnaggr::Integer, i_relation::Integer) = +# fwd_rs_init_world_slice(support::Interval2DFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = # nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_sample::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[w.x.x+div((w.x.y-2)*(w.x.y-1),2), w.y.x+div((w.y.y-2)*(w.y.y-1),2), i_sample, i_featsnaggr, i_relation] = threshold +# Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = +# support.d[w.x.x+div((w.x.y-2)*(w.x.y-1),2), w.y.x+div((w.y.y-2)*(w.y.y-1),2), i_instance, i_featsnaggr, i_relation] = threshold # function _slice_dataset(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} # Interval2DFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) # end diff --git a/src/datasets/dimensional-datasets/datasets/main.jl b/src/datasets/dimensional-datasets/datasets/main.jl index c34ffa9..24c2fe2 100644 --- a/src/datasets/dimensional-datasets/datasets/main.jl +++ b/src/datasets/dimensional-datasets/datasets/main.jl @@ -17,7 +17,7 @@ using SoleLogics: AbstractFrame, AbstractDimensionalFrame, FullDimensionalFrame import SoleLogics: worldtype, accessibles, allworlds, alphabet, initialworld using SoleData -import SoleData: _isnan, hasnans, nattributes, max_channel_size, channel_size +import SoleData: _isnan, hasnans, nvariables, max_channel_size, channel_size import SoleData: instance, get_instance, slice_dataset, _slice_dataset import SoleData: dimensionality @@ -29,7 +29,7 @@ using SoleModels: AbstractLogiset, AbstractMultiModalFrame using SoleModels: MultiFrameLogiset, AbstractLogiset using SoleModels: apply_test_operator, existential_aggregator, aggregator_bottom, aggregator_to_binary import SoleModels: representatives, ScalarMetaCondition, ScalarCondition, featvaltype -import SoleModels: nsamples, nrelations, nfeatures, check, _slice_dataset, minify +import SoleModels: ninstances, nrelations, nfeatures, check, _slice_dataset, minify import SoleModels: nframes, frames, displaystructure, frame import SoleModels: grouped_featsaggrsnops, features, grouped_metaconditions, alphabet, findfeature, findrelation, isminifiable diff --git a/src/datasets/dimensional-datasets/datasets/passive-dimensional-dataset.jl b/src/datasets/dimensional-datasets/datasets/passive-dimensional-dataset.jl index e859956..305e2b0 100644 --- a/src/datasets/dimensional-datasets/datasets/passive-dimensional-dataset.jl +++ b/src/datasets/dimensional-datasets/datasets/passive-dimensional-dataset.jl @@ -1,5 +1,5 @@ using SoleData: slice_dataset -import SoleData: get_instance, nsamples, nattributes, channel_size, max_channel_size, dimensionality, eltype +import SoleData: get_instance, ninstances, nvariables, channel_size, max_channel_size, dimensionality, eltype using SoleData: AbstractDimensionalDataset, AbstractDimensionalInstance, AbstractDimensionalChannel, @@ -59,19 +59,19 @@ end @inline function Base.getindex( X::PassiveDimensionalDataset{N,W}, - i_sample::Integer, + i_instance::Integer, w::W, f::AbstractFeature{U}, args..., ) where {N,W<:AbstractWorld,U} - w_values = interpret_world(w, get_instance(X.d, i_sample)) + w_values = interpret_world(w, get_instance(X.d, i_instance)) computefeature(f, w_values)::U end Base.size(X::PassiveDimensionalDataset) = Base.size(X.d) -nattributes(X::PassiveDimensionalDataset) = nattributes(X.d) -nsamples(X::PassiveDimensionalDataset) = nsamples(X.d) +nvariables(X::PassiveDimensionalDataset) = nvariables(X.d) +ninstances(X::PassiveDimensionalDataset) = ninstances(X.d) channel_size(X::PassiveDimensionalDataset) = channel_size(X.d) max_channel_size(X::PassiveDimensionalDataset) = max_channel_size(X.d) dimensionality(X::PassiveDimensionalDataset) = dimensionality(X.d) @@ -86,9 +86,9 @@ hasnans(X::PassiveDimensionalDataset) = hasnans(X.d) worldtype(X::PassiveDimensionalDataset{N,W}) where {N,W} = W -frame(X::PassiveDimensionalDataset, i_sample) = _frame(X.d, i_sample) +frame(X::PassiveDimensionalDataset, i_instance) = _frame(X.d, i_instance) ############################################################################################ -_frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_sample) = _frame(X) +_frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance) = _frame(X) _frame(X::Union{UniformDimensionalDataset,AbstractArray}) = FullDimensionalFrame(channel_size(X)) diff --git a/src/datasets/dimensional-datasets/dimensional-features.jl b/src/datasets/dimensional-datasets/dimensional-features.jl index 8fb5a53..3873f91 100644 --- a/src/datasets/dimensional-datasets/dimensional-features.jl +++ b/src/datasets/dimensional-datasets/dimensional-features.jl @@ -13,7 +13,7 @@ representing functions that can be computed on dimensional, geometrical worlds. Dimensional worlds are geometric entity that live in a *dimensional* context; for example, an `Interval` of a time series. As an example of a dimensional feature, consider min(V1), -which computes the minimum for attribute 1 for a given world. +which computes the minimum for variable 1 for a given world. The value of a feature for a given world can be then evaluated in a `Condition`, such as: min(V1) >= 10. @@ -67,23 +67,23 @@ See also [`Interval`](@ref), """ abstract type AbstractUnivariateFeature{U} <: DimensionalFeature{U} end -i_attribute(f::AbstractUnivariateFeature) = f.i_attribute +i_variable(f::AbstractUnivariateFeature) = f.i_variable """ - function attribute_name( + function variable_name( f::AbstractUnivariateFeature; - attribute_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, - attribute_name_prefix::Union{Nothing,String} = $(repr(UVF_VARPREFIX)), + variable_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, + variable_name_prefix::Union{Nothing,String} = $(repr(UVF_VARPREFIX)), )::String -Return the name of the attribute targeted by a univariate feature. -By default, an attribute name is a number prefixed by $(repr(UVF_VARPREFIX)); -however, `attribute_names_map` or `attribute_name_prefix` can be used to -customize attribute names. -The prefix can be customized by specifying `attribute_name_prefix`. +Return the name of the variable targeted by a univariate feature. +By default, an variable name is a number prefixed by $(repr(UVF_VARPREFIX)); +however, `variable_names_map` or `variable_name_prefix` can be used to +customize variable names. +The prefix can be customized by specifying `variable_name_prefix`. Alternatively, a mapping from string to integer (either via a Dictionary or a Vector) -can be passed as `attribute_names_map`. -Note that only one in `attribute_names_map` and `attribute_name_prefix` should be provided. +can be passed as `variable_names_map`. +Note that only one in `variable_names_map` and `variable_name_prefix` should be provided. See also @@ -91,18 +91,18 @@ See also [`ScalarCondition`](@ref), [`syntaxstring`](@ref). """ -function attribute_name( +function variable_name( f::AbstractUnivariateFeature; - attribute_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, - attribute_name_prefix::Union{Nothing,String} = nothing, + variable_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, + variable_name_prefix::Union{Nothing,String} = nothing, kwargs..., # TODO remove this. ) - if isnothing(attribute_names_map) - attribute_name_prefix = isnothing(attribute_name_prefix) ? UVF_VARPREFIX : attribute_name_prefix - "$(attribute_name_prefix)$(i_attribute(f))" + if isnothing(variable_names_map) + variable_name_prefix = isnothing(variable_name_prefix) ? UVF_VARPREFIX : variable_name_prefix + "$(variable_name_prefix)$(i_variable(f))" else - @assert isnothing(attribute_name_prefix) - "$(attribute_names_map[i_attribute(f)])" + @assert isnothing(variable_name_prefix) + "$(variable_names_map[i_variable(f)])" end end @@ -111,7 +111,7 @@ function featurename(f::AbstractFeature; kwargs...) end function syntaxstring(f::AbstractUnivariateFeature; kwargs...) - n = attribute_name(f; kwargs...) + n = variable_name(f; kwargs...) "" "$(featurename(f))$UVF_OPENING_BRACKET$n$UVF_CLOSING_BRACKET" end @@ -120,7 +120,7 @@ end """ struct UnivariateFeature{U} <: AbstractUnivariateFeature{U} - i_attribute::Integer + i_variable::Integer f::Function end @@ -135,17 +135,17 @@ See also [`Interval`](@ref), [`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateFeature{U} <: AbstractUnivariateFeature{U} - i_attribute::Integer + i_variable::Integer f::Function end function computefeature(f::UnivariateFeature{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} - (f.f(SoleBase.vectorize(channelvariable(channel, f.i_attribute));))::U + (f.f(SoleBase.vectorize(channelvariable(channel, f.i_variable));))::U end featurename(f::UnivariateFeature) = string(f.f) """ struct UnivariateNamedFeature{U} <: AbstractUnivariateFeature{U} - i_attribute::Integer + i_variable::Integer name::String end @@ -157,7 +157,7 @@ See also [`Interval`](@ref), [`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateNamedFeature{U} <: AbstractUnivariateFeature{U} - i_attribute::Integer + i_variable::Integer name::String end function computefeature(f::UnivariateNamedFeature, channel::AbstractDimensionalChannel{T}) where {T} @@ -171,7 +171,7 @@ featurename(f::UnivariateNamedFeature) = f.name """ struct UnivariateMin{U} <: AbstractUnivariateFeature{U} - i_attribute::Integer + i_variable::Integer end Notable univariate feature computing the minimum value for a given variable. @@ -183,24 +183,24 @@ See also [`Interval`](@ref), [`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateMin{U} <: AbstractUnivariateFeature{U} - i_attribute::Integer - function UnivariateMin{U}(i_attribute::Integer) where {U<:Real} - return new{U}(i_attribute) + i_variable::Integer + function UnivariateMin{U}(i_variable::Integer) where {U<:Real} + return new{U}(i_variable) end - function UnivariateMin(i_attribute::Integer) + function UnivariateMin(i_variable::Integer) @warn "Please specify the type of the feature for UnivariateMin." * - " For example: UnivariateMin{Float64}($(i_attribute))." - return UnivariateMin{Real}(i_attribute) + " For example: UnivariateMin{Float64}($(i_variable))." + return UnivariateMin{Real}(i_variable) end end function computefeature(f::UnivariateMin{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} - (minimum(channelvariable(channel, f.i_attribute)))::U + (minimum(channelvariable(channel, f.i_variable)))::U end featurename(f::UnivariateMin) = "min" """ struct UnivariateMax{U} <: AbstractUnivariateFeature{U} - i_attribute::Integer + i_variable::Integer end Notable univariate feature computing the maximum value for a given variable. @@ -212,18 +212,18 @@ See also [`Interval`](@ref), [`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateMax{U} <: AbstractUnivariateFeature{U} - i_attribute::Integer - function UnivariateMax{U}(i_attribute::Integer) where {U<:Real} - return new{U}(i_attribute) + i_variable::Integer + function UnivariateMax{U}(i_variable::Integer) where {U<:Real} + return new{U}(i_variable) end - function UnivariateMax(i_attribute::Integer) + function UnivariateMax(i_variable::Integer) @warn "Please specify the type of the feature for UnivariateMax." * - " For example: UnivariateMax{Float64}($(i_attribute))." - return UnivariateMax{Real}(i_attribute) + " For example: UnivariateMax{Float64}($(i_variable))." + return UnivariateMax{Real}(i_variable) end end function computefeature(f::UnivariateMax{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} - (maximum(channelvariable(channel, f.i_attribute)))::U + (maximum(channelvariable(channel, f.i_variable)))::U end featurename(f::UnivariateMax) = "max" @@ -232,7 +232,7 @@ featurename(f::UnivariateMax) = "max" """ struct UnivariateSoftMin{U,T<:AbstractFloat} <: AbstractUnivariateFeature{U} - i_attribute::Integer + i_variable::Integer alpha::T end @@ -245,24 +245,24 @@ See also [`Interval`](@ref), [`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateSoftMin{U,T<:AbstractFloat} <: AbstractUnivariateFeature{U} - i_attribute::Integer + i_variable::Integer alpha::T - function UnivariateSoftMin{U}(i_attribute::Integer, alpha::T) where {U<:Real,T} + function UnivariateSoftMin{U}(i_variable::Integer, alpha::T) where {U<:Real,T} @assert !(alpha > 1.0 || alpha < 0.0) "Can't instantiate UnivariateSoftMin with alpha = $(alpha)" @assert !isone(alpha) "Can't instantiate UnivariateSoftMin with alpha = $(alpha). Use UnivariateMin instead!" - new{U,T}(i_attribute, alpha) + new{U,T}(i_variable, alpha) end end alpha(f::UnivariateSoftMin) = f.alpha featurename(f::UnivariateSoftMin) = "min" * utils.subscriptnumber(rstrip(rstrip(string(f.alpha*100), '0'), '.')) function computefeature(f::UnivariateSoftMin{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} - utils.softminimum(channelvariable(channel, f.i_attribute), f.alpha)::U + utils.softminimum(channelvariable(channel, f.i_variable), f.alpha)::U end """ struct UnivariateSoftMax{U,T<:AbstractFloat} <: AbstractUnivariateFeature{U} - i_attribute::Integer + i_variable::Integer alpha::T end @@ -275,26 +275,26 @@ See also [`Interval`](@ref), [`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateSoftMax{U,T<:AbstractFloat} <: AbstractUnivariateFeature{U} - i_attribute::Integer + i_variable::Integer alpha::T - function UnivariateSoftMax{U}(i_attribute::Integer, alpha::T) where {U<:Real,T} + function UnivariateSoftMax{U}(i_variable::Integer, alpha::T) where {U<:Real,T} @assert !(alpha > 1.0 || alpha < 0.0) "Can't instantiate UnivariateSoftMax with alpha = $(alpha)" @assert !isone(alpha) "Can't instantiate UnivariateSoftMax with alpha = $(alpha). Use UnivariateMax instead!" - new{U,T}(i_attribute, alpha) + new{U,T}(i_variable, alpha) end end alpha(f::UnivariateSoftMax) = f.alpha featurename(f::UnivariateSoftMax) = "max" * utils.subscriptnumber(rstrip(rstrip(string(f.alpha*100), '0'), '.')) function computefeature(f::UnivariateSoftMax{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} - utils.softmaximum(channelvariable(channel, f.i_attribute), f.alpha)::U + utils.softmaximum(channelvariable(channel, f.i_variable), f.alpha)::U end # simplified propositional cases: function computefeature(f::UnivariateSoftMin{U}, channel::AbstractDimensionalChannel{T,1}) where {U<:Real,T} - channelvariable(channel, f.i_attribute)::U + channelvariable(channel, f.i_variable)::U end function computefeature(f::UnivariateSoftMax{U}, channel::AbstractDimensionalChannel{T,1}) where {U<:Real,T} - channelvariable(channel, f.i_attribute)::U + channelvariable(channel, f.i_variable)::U end ############################################################################################ diff --git a/src/datasets/dimensional-datasets/gamma-access.jl b/src/datasets/dimensional-datasets/gamma-access.jl index 7c3932c..78720d7 100644 --- a/src/datasets/dimensional-datasets/gamma-access.jl +++ b/src/datasets/dimensional-datasets/gamma-access.jl @@ -5,26 +5,26 @@ using SoleModels: AbstractFeature, Aggregator @inline function onestep_accessible_aggregation( X::PassiveDimensionalDataset{N,W}, - i_sample::Integer, + i_instance::Integer, w::W, r::AbstractRelation, f::AbstractFeature{V}, aggr::Aggregator, args..., ) where {N,V,W<:AbstractWorld} - vs = [X[i_sample, w2, f] for w2 in representatives(X, i_sample, w, r, f, aggr)] + vs = [X[i_instance, w2, f] for w2 in representatives(X, i_instance, w, r, f, aggr)] return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) end @inline function onestep_accessible_aggregation( X::PassiveDimensionalDataset{N,W}, - i_sample::Integer, + i_instance::Integer, r::GlobalRel, f::AbstractFeature{V}, aggr::Aggregator, args... ) where {N,V,W<:AbstractWorld} - vs = [X[i_sample, w2, f] for w2 in representatives(X, i_sample, r, f, aggr)] + vs = [X[i_instance, w2, f] for w2 in representatives(X, i_instance, r, f, aggr)] return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) end @@ -32,22 +32,22 @@ end @inline function onestep_accessible_aggregation( X::DimensionalLogiset{VV,N,W}, - i_sample::Integer, + i_instance::Integer, w::W, r::AbstractRelation, f::AbstractFeature{V}, aggr::Aggregator, args..., ) where {VV,N,V<:VV,W<:AbstractWorld} - onestep_accessible_aggregation(domain(X), i_sample, w, r, f, aggr, args...) + onestep_accessible_aggregation(domain(X), i_instance, w, r, f, aggr, args...) end @inline function onestep_accessible_aggregation( X::DimensionalLogiset{VV,N,W}, - i_sample::Integer, + i_instance::Integer, r::GlobalRel, f::AbstractFeature{V}, aggr::Aggregator, args..., ) where {VV,N,V<:VV,W<:AbstractWorld} - onestep_accessible_aggregation(domain(X), i_sample, r, f, aggr, args...) + onestep_accessible_aggregation(domain(X), i_instance, r, f, aggr, args...) end diff --git a/src/datasets/dimensional-datasets/main.jl b/src/datasets/dimensional-datasets/main.jl index 6929ef3..92f0833 100644 --- a/src/datasets/dimensional-datasets/main.jl +++ b/src/datasets/dimensional-datasets/main.jl @@ -48,19 +48,19 @@ include("representatives/Full1DFrame+IA.jl") include("representatives/Full1DFrame+RCC.jl") include("representatives/Full2DFrame.jl") -_st_featop_abbr(f::UnivariateMin, ::typeof(≥); kwargs...) = "$(attribute_name(f; kwargs...)) ⪴" -_st_featop_abbr(f::UnivariateMax, ::typeof(≤); kwargs...) = "$(attribute_name(f; kwargs...)) ⪳" -_st_featop_abbr(f::UnivariateSoftMin, ::typeof(≥); kwargs...) = "$(attribute_name(f; kwargs...)) $("⪴" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" -_st_featop_abbr(f::UnivariateSoftMax, ::typeof(≤); kwargs...) = "$(attribute_name(f; kwargs...)) $("⪳" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" - -_st_featop_abbr(f::UnivariateMin, ::typeof(<); kwargs...) = "$(attribute_name(f; kwargs...)) ⪶" -_st_featop_abbr(f::UnivariateMax, ::typeof(>); kwargs...) = "$(attribute_name(f; kwargs...)) ⪵" -_st_featop_abbr(f::UnivariateSoftMin, ::typeof(<); kwargs...) = "$(attribute_name(f; kwargs...)) $("⪶" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" -_st_featop_abbr(f::UnivariateSoftMax, ::typeof(>); kwargs...) = "$(attribute_name(f; kwargs...)) $("⪵" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" - -_st_featop_abbr(f::UnivariateMin, ::typeof(≤); kwargs...) = "$(attribute_name(f; kwargs...)) ↘" -_st_featop_abbr(f::UnivariateMax, ::typeof(≥); kwargs...) = "$(attribute_name(f; kwargs...)) ↗" -_st_featop_abbr(f::UnivariateSoftMin, ::typeof(≤); kwargs...) = "$(attribute_name(f; kwargs...)) $("↘" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" -_st_featop_abbr(f::UnivariateSoftMax, ::typeof(≥); kwargs...) = "$(attribute_name(f; kwargs...)) $("↗" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" +_st_featop_abbr(f::UnivariateMin, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) ⪴" +_st_featop_abbr(f::UnivariateMax, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) ⪳" +_st_featop_abbr(f::UnivariateSoftMin, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) $("⪴" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" +_st_featop_abbr(f::UnivariateSoftMax, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) $("⪳" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" + +_st_featop_abbr(f::UnivariateMin, ::typeof(<); kwargs...) = "$(variable_name(f; kwargs...)) ⪶" +_st_featop_abbr(f::UnivariateMax, ::typeof(>); kwargs...) = "$(variable_name(f; kwargs...)) ⪵" +_st_featop_abbr(f::UnivariateSoftMin, ::typeof(<); kwargs...) = "$(variable_name(f; kwargs...)) $("⪶" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" +_st_featop_abbr(f::UnivariateSoftMax, ::typeof(>); kwargs...) = "$(variable_name(f; kwargs...)) $("⪵" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" + +_st_featop_abbr(f::UnivariateMin, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) ↘" +_st_featop_abbr(f::UnivariateMax, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) ↗" +_st_featop_abbr(f::UnivariateSoftMin, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) $("↘" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" +_st_featop_abbr(f::UnivariateSoftMax, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) $("↗" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" end diff --git a/src/datasets/dimensional-datasets/parse-dimensional-condition.jl b/src/datasets/dimensional-datasets/parse-dimensional-condition.jl index 374c2b9..6fc5957 100644 --- a/src/datasets/dimensional-datasets/parse-dimensional-condition.jl +++ b/src/datasets/dimensional-datasets/parse-dimensional-condition.jl @@ -1,3 +1,5 @@ +import SoleModels: parsecondition + using StatsBase #= ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Code purpose ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -38,8 +40,8 @@ DEFAULT_FEATVALTYPE = Float64 opening_bracket::String = $(repr(UVF_OPENING_BRACKET)), closing_bracket::String = $(repr(UVF_CLOSING_BRACKET)), custom_feature_aliases = Dict{String,Union{Type,Function}}(), - attribute_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, - attribute_name_prefix::Union{Nothing,String} = nothing, + variable_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, + variable_name_prefix::Union{Nothing,String} = nothing, )::ScalarCondition Return a `ScalarCondition` which is the result of parsing `expression`. @@ -72,8 +74,8 @@ where: custom features/functions; if not provided, `SoleModels.BASE_FEATURE_ALIASES` will be used. -`attribute_names_map`, `attribute_name_prefix` can influence the -parsing of the attribute name; please, refer to `attribute_name` for their behavior. +`variable_names_map`, `variable_name_prefix` can influence the +parsing of the variable name; please, refer to `variable_name` for their behavior. # Examples ```julia-repl @@ -85,7 +87,7 @@ julia> syntaxstring(parseformulatree("min[V1] <= 15 ∧ max[V1] >= 85"; proposit ``` See also -[`attribute_name`](@ref), +[`variable_name`](@ref), [`ScalarCondition`](@ref), [`ScalarMetaCondition`](@ref), [`parseformulatree`](@ref), @@ -97,11 +99,11 @@ function parsecondition( opening_bracket::String = UVF_OPENING_BRACKET, closing_bracket::String = UVF_CLOSING_BRACKET, custom_feature_aliases = Dict{String,Union{Type,Function}}(), - attribute_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, - attribute_name_prefix::Union{Nothing,String} = nothing, + variable_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, + variable_name_prefix::Union{Nothing,String} = nothing, ) - @assert isnothing(attribute_names_map) || isnothing(attribute_name_prefix) "" * - "Cannot parse attribute with both attribute_names_map and attribute_name_prefix." * + @assert isnothing(variable_names_map) || isnothing(variable_name_prefix) "" * + "Cannot parse variable with both variable_names_map and variable_name_prefix." * " (expression = $(repr(expression)))" if isnothing(featvaltype) @@ -117,20 +119,20 @@ function parsecondition( featdict = merge(BASE_FEATURE_ALIASES, custom_feature_aliases) - (_feature, _attribute, _test_operator, _threshold) = begin + (_feature, _variable, _test_operator, _threshold) = begin # 4 slices are found initially in this order: # 1) a feature name (e.g. "min"), - # 2) an attribute inside feature's brackets (e.g. "[V189]"), + # 2) an variable inside feature's brackets (e.g. "[V189]"), # 3) a test operator ("<=", ">=", "<" or ">"), # 4) a threshold value. # Regex is more or less: # (\w*) *(\[.*\]) *(<=|>=|<|>) *(\d*). - attribute_name_prefix = isnothing(attribute_name_prefix) && - isnothing(attribute_names_map) ? UVF_VARPREFIX : attribute_name_prefix - attribute_name_prefix = isnothing(attribute_name_prefix) ? "" : attribute_name_prefix + variable_name_prefix = isnothing(variable_name_prefix) && + isnothing(variable_names_map) ? UVF_VARPREFIX : variable_name_prefix + variable_name_prefix = isnothing(variable_name_prefix) ? "" : variable_name_prefix - r = Regex("^\\s*(\\w+)\\s*\\$(opening_bracket)\\s*$(attribute_name_prefix)(\\S+)\\s*\\$(closing_bracket)\\s*([^\\s\\d]+)\\s*(\\S+)\\s*\$") - # r = Regex("^\\s*(\\w+)\\s*\\$(opening_bracket)\\s*$(attribute_name_prefix)(\\S+)\\s*\\$(closing_bracket)\\s*(\\S+)\\s+(\\S+)\\s*\$") + r = Regex("^\\s*(\\w+)\\s*\\$(opening_bracket)\\s*$(variable_name_prefix)(\\S+)\\s*\\$(closing_bracket)\\s*([^\\s\\d]+)\\s*(\\S+)\\s*\$") + # r = Regex("^\\s*(\\w+)\\s*\\$(opening_bracket)\\s*$(variable_name_prefix)(\\S+)\\s*\\$(closing_bracket)\\s*(\\S+)\\s+(\\S+)\\s*\$") slices = string.(match(r, expression)) # Assert for malformed strings (e.g. "123.4250.2") @@ -167,13 +169,13 @@ function parsecondition( end feature = begin - i_attr = begin - if isnothing(attribute_names_map) - parse(Int, _attribute) - elseif attribute_names_map isa Union{AbstractDict,AbstractVector} - findfirst(attribute_names_map, attribute) + i_var = begin + if isnothing(variable_names_map) + parse(Int, _variable) + elseif variable_names_map isa Union{AbstractDict,AbstractVector} + findfirst(variable_names_map, variable) else - error("Unexpected attribute_names_map of type $(typeof(attribute_names_map))" * + error("Unexpected variable_names_map of type $(typeof(variable_names_map))" * " encountered.") end end @@ -184,15 +186,15 @@ function parsecondition( # If it is a function, wrap it into a UnivariateFeature # otherwise, it is a feature, and it is used as a constructor. if feat_or_fun isa Function - UnivariateFeature{featvaltype}(i_attr, feat_or_fun) + UnivariateFeature{featvaltype}(i_var, feat_or_fun) else - feat_or_fun{featvaltype}(i_attr) + feat_or_fun{featvaltype}(i_var) end else # If it is not a known feature, interpret it as a Julia function, # and wrap it into a UnivariateFeature. f = eval(Meta.parse(_feature)) - UnivariateFeature{featvaltype}(i_attr, f) + UnivariateFeature{featvaltype}(i_var, f) end end diff --git a/src/datasets/scalar-datasets/active-scalar-logiset.jl b/src/datasets/scalar-datasets/active-scalar-logiset.jl index 3f8fa3c..d613ea6 100644 --- a/src/datasets/scalar-datasets/active-scalar-logiset.jl +++ b/src/datasets/scalar-datasets/active-scalar-logiset.jl @@ -17,11 +17,11 @@ end function check( p::Proposition{<:ScalarCondition}, X::AbstractScalarLogiset{W}, - i_sample, + i_instance, w::W, ) where {W<:AbstractWorld} cond = atom(p) - featval = featvalue(X, i_sample, w, feature(cond)) + featval = featvalue(X, i_instance, w, feature(cond)) apply_test_operator(test_operator(cond), featval, threshold(cond)) end @@ -35,9 +35,9 @@ end function alphabet(X::AbstractScalarLogiset) conds = vcat([begin thresholds = unique([ - X[i_sample, w, feature] - for i_sample in 1:nsamples(X) - for w in allworlds(X, i_sample) + X[i_instance, w, feature] + for i_instance in 1:ninstances(X) + for w in allworlds(X, i_instance) ]) [(mc, thresholds) for mc in metaconditions] end for (feature, metaconditions) in grouped_metaconditions(X)]...) diff --git a/src/datasets/scalar-datasets/gamma-access.jl b/src/datasets/scalar-datasets/gamma-access.jl index c990527..a357f47 100644 --- a/src/datasets/scalar-datasets/gamma-access.jl +++ b/src/datasets/scalar-datasets/gamma-access.jl @@ -1,13 +1,13 @@ ############################################################################################ -@inline function onestep_accessible_aggregation(X::Logiset{VV,W}, i_sample::Integer, w::W, r::AbstractRelation, f::AbstractFeature{V}, aggr::Aggregator, args...) where {VV,V<:VV,W<:AbstractWorld} - vs = [X[i_sample, w2, f] for w2 in representatives(X, i_sample, w, r, f, aggr)] +@inline function onestep_accessible_aggregation(X::Logiset{VV,W}, i_instance::Integer, w::W, r::AbstractRelation, f::AbstractFeature{V}, aggr::Aggregator, args...) where {VV,V<:VV,W<:AbstractWorld} + vs = [X[i_instance, w2, f] for w2 in representatives(X, i_instance, w, r, f, aggr)] return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) end -@inline function onestep_accessible_aggregation(X::Logiset{VV,W}, i_sample::Integer, r::GlobalRel, f::AbstractFeature{V}, aggr::Aggregator, args...) where {VV,V<:VV,W<:AbstractWorld} - vs = [X[i_sample, w2, f] for w2 in representatives(X, i_sample, r, f, aggr)] +@inline function onestep_accessible_aggregation(X::Logiset{VV,W}, i_instance::Integer, r::GlobalRel, f::AbstractFeature{V}, aggr::Aggregator, args...) where {VV,V<:VV,W<:AbstractWorld} + vs = [X[i_instance, w2, f] for w2 in representatives(X, i_instance, r, f, aggr)] return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) end @@ -15,7 +15,7 @@ end function onestep_accessible_aggregation( X::SupportedScalarLogiset{VV,W}, - i_sample::Integer, + i_instance::Integer, w::W, relation::AbstractRelation, feature::AbstractFeature{V}, @@ -23,46 +23,46 @@ function onestep_accessible_aggregation( i_featsnaggr::Union{Nothing,Integer} = nothing, i_relation::Integer = findrelation(X, relation), ) where {VV,V<:VV,W<:AbstractWorld} - compute_modal_gamma(support(X), fd(X), i_sample, w, relation, feature, aggregator, i_featsnaggr, i_relation) + compute_modal_gamma(support(X), fd(X), i_instance, w, relation, feature, aggregator, i_featsnaggr, i_relation) end @inline function onestep_accessible_aggregation( X::SupportedScalarLogiset{VV,W}, - i_sample::Integer, + i_instance::Integer, r::GlobalRel, f::AbstractFeature{V}, aggr::Aggregator, args... ) where {VV,V<:VV,W<:AbstractWorld} - compute_global_gamma(support(X), fd(X), i_sample, f, aggr, args...) + compute_global_gamma(support(X), fd(X), i_instance, f, aggr, args...) end ############################################################################################ -function fwdslice_onestep_accessible_aggregation(fd::Logiset, fwdslice::FWDFeatureSlice, i_sample, r::GlobalRel, f, aggr, args...) - # accessible_worlds = allworlds(fd, i_sample) - accessible_worlds = representatives(fd, i_sample, r, f, aggr) +function fwdslice_onestep_accessible_aggregation(fd::Logiset, fwdslice::FWDFeatureSlice, i_instance, r::GlobalRel, f, aggr, args...) + # accessible_worlds = allworlds(fd, i_instance) + accessible_worlds = representatives(fd, i_instance, r, f, aggr) gamma = apply_aggregator(fwdslice, accessible_worlds, aggr) end -function fwdslice_onestep_accessible_aggregation(fd::Logiset, fwdslice::FWDFeatureSlice, i_sample, w, r::AbstractRelation, f, aggr, args...) - # accessible_worlds = accessibles(fd, i_sample, w, r) - accessible_worlds = representatives(fd, i_sample, w, r, f, aggr) +function fwdslice_onestep_accessible_aggregation(fd::Logiset, fwdslice::FWDFeatureSlice, i_instance, w, r::AbstractRelation, f, aggr, args...) + # accessible_worlds = accessibles(fd, i_instance, w, r) + accessible_worlds = representatives(fd, i_instance, w, r, f, aggr) gamma = apply_aggregator(fwdslice, accessible_worlds, aggr) end # TODO remove -# function fwdslice_onestep_accessible_aggregation(fd::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_sample, args...) -# fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_sample, args...) +# function fwdslice_onestep_accessible_aggregation(fd::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_instance, args...) +# fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_instance, args...) # end -function fwdslice_onestep_accessible_aggregation(X::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_sample, r::GlobalRel, f, aggr, args...) - fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_sample, r, f, aggr, args...) +function fwdslice_onestep_accessible_aggregation(X::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_instance, r::GlobalRel, f, aggr, args...) + fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_instance, r, f, aggr, args...) end -function fwdslice_onestep_accessible_aggregation(X::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_sample, w, r::AbstractRelation, f, aggr, args...) - fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_sample, w, r, f, aggr, args...) +function fwdslice_onestep_accessible_aggregation(X::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_instance, w, r::AbstractRelation, f, aggr, args...) + fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_instance, w, r, f, aggr, args...) end ############################################################################################ @@ -71,25 +71,25 @@ function fwdslice_onestep_accessible_aggregation( X::OneStepFeaturedSupportingDataset{V,W}, fd::Logiset{V,W}, fwdslice::FWDFeatureSlice, - i_sample::Integer, + i_instance::Integer, r::GlobalRel, feature::AbstractFeature, aggr::Aggregator, i_featsnaggr::Integer = find_featsnaggr_id(X, feature, aggr), ) where {V,W<:AbstractWorld} _fwd_gs = fwd_gs(X) - if isnothing(_fwd_gs[i_sample, i_featsnaggr]) - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_sample, r, feature, aggr) - _fwd_gs[i_sample, i_featsnaggr] = gamma + if isnothing(_fwd_gs[i_instance, i_featsnaggr]) + gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, r, feature, aggr) + _fwd_gs[i_instance, i_featsnaggr] = gamma end - _fwd_gs[i_sample, i_featsnaggr] + _fwd_gs[i_instance, i_featsnaggr] end function fwdslice_onestep_accessible_aggregation( X::OneStepFeaturedSupportingDataset{V,W}, fd::Logiset{V,W}, fwdslice::FWDFeatureSlice, - i_sample::Integer, + i_instance::Integer, w::W, r::AbstractRelation, feature::AbstractFeature, @@ -98,11 +98,11 @@ function fwdslice_onestep_accessible_aggregation( i_relation = nothing, # TODO fix )::V where {V,W<:AbstractWorld} _fwd_rs = fwd_rs(X) - if isnothing(_fwd_rs[i_sample, w, i_featsnaggr, i_relation]) - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_sample, w, r, feature, aggr) - _fwd_rs[i_sample, w, i_featsnaggr, i_relation] = gamma + if isnothing(_fwd_rs[i_instance, w, i_featsnaggr, i_relation]) + gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, w, r, feature, aggr) + _fwd_rs[i_instance, w, i_featsnaggr, i_relation] = gamma end - _fwd_rs[i_sample, w, i_featsnaggr, i_relation] + _fwd_rs[i_instance, w, i_featsnaggr, i_relation] end ############################################################################################ diff --git a/src/datasets/scalar-datasets/one-step-featured-supporting-dataset.jl b/src/datasets/scalar-datasets/one-step-featured-supporting-dataset.jl index 68081a4..420e4fb 100644 --- a/src/datasets/scalar-datasets/one-step-featured-supporting-dataset.jl +++ b/src/datasets/scalar-datasets/one-step-featured-supporting-dataset.jl @@ -35,7 +35,7 @@ struct OneStepFeaturedSupportingDataset{ @assert nfeatsnaggrs(fwd_rs) == length(featsnaggrs) "Can't instantiate $(ty) with unmatching nfeatsnaggrs for fwd_rs and provided featsnaggrs: $(nfeatsnaggrs(fwd_rs)) and $(length(featsnaggrs))" if fwd_gs != nothing @assert nfeatsnaggrs(fwd_gs) == length(featsnaggrs) "Can't instantiate $(ty) with unmatching nfeatsnaggrs for fwd_gs and provided featsnaggrs: $(nfeatsnaggrs(fwd_gs)) and $(length(featsnaggrs))" - @assert nsamples(fwd_gs) == nsamples(fwd_rs) "Can't instantiate $(ty) with unmatching nsamples for fwd_gs and fwd_rs support: $(nsamples(fwd_gs)) and $(nsamples(fwd_rs))" + @assert ninstances(fwd_gs) == ninstances(fwd_rs) "Can't instantiate $(ty) with unmatching ninstances for fwd_gs and fwd_rs support: $(ninstances(fwd_gs)) and $(ninstances(fwd_rs))" end new{V,W,FR,VV,FWDRS,FWDGS,G}(fwd_rs, fwd_gs, featsnaggrs) end @@ -71,7 +71,7 @@ struct OneStepFeaturedSupportingDataset{ end end - _n_samples = nsamples(fd) + _n_instances = ninstances(fd) nrelations = length(_relations) nfeatsnaggrs = sum(length.(_grouped_featsnaggrs)) @@ -87,19 +87,19 @@ struct OneStepFeaturedSupportingDataset{ end end - # p = Progress(_n_samples, 1, "Computing EMD supports...") - Threads.@threads for i_sample in 1:_n_samples - # @logmsg LogDebug "Instance $(i_sample)/$(_n_samples)" + # p = Progress(_n_instances, 1, "Computing EMD supports...") + Threads.@threads for i_instance in 1:_n_instances + # @logmsg LogDebug "Instance $(i_instance)/$(_n_instances)" - # if i_sample == 1 || ((i_sample+1) % (floor(Int, ((_n_samples)/4))+1)) == 0 - # @logmsg LogOverview "Instance $(i_sample)/$(_n_samples)" + # if i_instance == 1 || ((i_instance+1) % (floor(Int, ((_n_instances)/4))+1)) == 0 + # @logmsg LogOverview "Instance $(i_instance)/$(_n_instances)" # end for (i_feature,aggregators) in enumerate(_grouped_featsnaggrs) feature = _features[i_feature] # @logmsg LogDebug "Feature $(i_feature)" - fwdslice = fwdread_channel(_fwd, i_sample, i_feature) + fwdslice = fwdread_channel(_fwd, i_instance, i_feature) # @logmsg LogDebug fwdslice @@ -111,11 +111,11 @@ struct OneStepFeaturedSupportingDataset{ for (i_featsnaggr,aggr) in aggregators # Threads.@threads for (i_featsnaggr,aggr) in aggregators - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_sample, globalrel, feature, aggr) + gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, globalrel, feature, aggr) # @logmsg LogDebug "Aggregator[$(i_featsnaggr)]=$(aggr) --> $(gamma)" - fwd_gs[i_sample, i_featsnaggr] = gamma + fwd_gs[i_instance, i_featsnaggr] = gamma end end @@ -126,21 +126,21 @@ struct OneStepFeaturedSupportingDataset{ # @logmsg LogDebug "Relation $(i_relation)/$(nrelations)" for (i_featsnaggr,aggr) in aggregators - fwd_rs_init_world_slice(fwd_rs, i_sample, i_featsnaggr, i_relation) + fwd_rs_init_world_slice(fwd_rs, i_instance, i_featsnaggr, i_relation) end - for w in allworlds(fd, i_sample) + for w in allworlds(fd, i_instance) # @logmsg LogDebug "World" w # TODO optimize: all aggregators are likely reading the same raw values. for (i_featsnaggr,aggr) in aggregators - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_sample, w, relation, feature, aggr) + gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, w, relation, feature, aggr) # @logmsg LogDebug "Aggregator" aggr gamma - fwd_rs[i_sample, w, i_featsnaggr, i_relation] = gamma + fwd_rs[i_instance, w, i_featsnaggr, i_relation] = gamma end end end @@ -156,7 +156,7 @@ fwd_rs(X::OneStepFeaturedSupportingDataset) = X.fwd_rs fwd_gs(X::OneStepFeaturedSupportingDataset) = X.fwd_gs featsnaggrs(X::OneStepFeaturedSupportingDataset) = X.featsnaggrs -nsamples(X::OneStepFeaturedSupportingDataset) = nsamples(fwd_rs(X)) +ninstances(X::OneStepFeaturedSupportingDataset) = ninstances(fwd_rs(X)) # nfeatsnaggrs(X::OneStepFeaturedSupportingDataset) = nfeatsnaggrs(fwd_rs(X)) # TODO delegate to the two components... @@ -164,7 +164,7 @@ function checksupportconsistency( fd::Logiset{V,W}, X::OneStepFeaturedSupportingDataset{V,W}, ) where {V,W<:AbstractWorld} - @assert nsamples(fd) == nsamples(X) "Consistency check failed! Unmatching nsamples for fd and support: $(nsamples(fd)) and $(nsamples(X))" + @assert ninstances(fd) == ninstances(X) "Consistency check failed! Unmatching ninstances for fd and support: $(ninstances(fd)) and $(ninstances(X))" # @assert nrelations(fd) == (nrelations(fwd_rs(X)) + (isnothing(fwd_gs(X)) ? 0 : 1)) "Consistency check failed! Unmatching nrelations for fd and support: $(nrelations(fd)) and $(nrelations(fwd_rs(X)))+$((isnothing(fwd_gs(X)) ? 0 : 1))" @assert nrelations(fd) >= nrelations(fwd_rs(X)) "Consistency check failed! Inconsistent nrelations for fd and support: $(nrelations(fd)) < $(nrelations(fwd_rs(X)))" _nfeatsnaggrs = nfeatsnaggrs(fd) @@ -240,27 +240,27 @@ end function compute_global_gamma( X::OneStepFeaturedSupportingDataset{V,W}, fd::Logiset{V,W}, - i_sample::Integer, + i_instance::Integer, feature::AbstractFeature, aggregator::Aggregator, i_featsnaggr::Integer = find_featsnaggr_id(X, feature, aggregator), ) where {V,W<:AbstractWorld} _fwd_gs = fwd_gs(X) # @assert !isnothing(_fwd_gs) "Error. SupportedScalarLogiset must be built with compute_relation_glob = true for it to be ready to test global decisions." - if usesglobalmemo(X) && isnothing(_fwd_gs[i_sample, i_featsnaggr]) + if usesglobalmemo(X) && isnothing(_fwd_gs[i_instance, i_featsnaggr]) error("TODO finish this: memoization on the global table") # gamma = TODO... # i_feature = findfeature(fd, feature) - # fwdslice = fwdread_channel(fwd(fd), i_sample, i_feature) - _fwd_gs[i_sample, i_featsnaggr] = gamma + # fwdslice = fwdread_channel(fwd(fd), i_instance, i_feature) + _fwd_gs[i_instance, i_featsnaggr] = gamma end - _fwd_gs[i_sample, i_featsnaggr] + _fwd_gs[i_instance, i_featsnaggr] end function compute_modal_gamma( X::OneStepFeaturedSupportingDataset{V,W}, fd::Logiset{V,W}, - i_sample::Integer, + i_instance::Integer, w::W, r::AbstractRelation, feature::AbstractFeature, @@ -269,11 +269,11 @@ function compute_modal_gamma( i_relation = nothing, )::V where {V,W<:AbstractWorld} _fwd_rs = fwd_rs(X) - if usesmodalmemo(X) && isnothing(_fwd_rs[i_sample, w, i_featsnaggr, i_relation]) + if usesmodalmemo(X) && isnothing(_fwd_rs[i_instance, w, i_featsnaggr, i_relation]) i_feature = findfeature(fd, feature) - fwdslice = fwdread_channel(fwd(fd), i_sample, i_feature) - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_sample, w, r, feature, aggregator) - fwd_rs[i_sample, w, i_featsnaggr, i_relation, gamma] + fwdslice = fwdread_channel(fwd(fd), i_instance, i_feature) + gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, w, r, feature, aggregator) + fwd_rs[i_instance, w, i_featsnaggr, i_relation, gamma] end - _fwd_rs[i_sample, w, i_featsnaggr, i_relation] + _fwd_rs[i_instance, w, i_featsnaggr, i_relation] end diff --git a/src/models/base.jl b/src/models/base.jl index 542547a..9231e03 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -40,8 +40,8 @@ function check( kwargs... ) map( - i_sample->check(c, slice_dataset(d, [i_sample]; return_view = true), args...; kwargs...)[1], - 1:nsamples(d) + i_instance->check(c, slice_dataset(d, [i_instance]; return_view = true), args...; kwargs...)[1], + 1:ninstances(d) ) end @@ -91,7 +91,7 @@ struct TrueCondition <: AbstractLogicalBooleanCondition end formula(::TrueCondition) = SyntaxTree(⊤) check(::TrueCondition, i::AbstractInterpretation, args...; kwargs...) = true check(::TrueCondition, d::AbstractInterpretationSet, args...; kwargs...) = - fill(true, nsamples(d)) + fill(true, ninstances(d)) """ struct LogicalTruthCondition{F<:AbstractFormula} <: AbstractLogicalBooleanCondition @@ -225,7 +225,7 @@ end m::AbstractModel, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), functional_args::Tuple = (), functional_kwargs::NamedTuple = (;), kwargs... @@ -264,10 +264,10 @@ end function apply( m::AbstractModel, d::AbstractInterpretationSet, - i_sample::Integer; + i_instance::Integer; kwargs... )::outputtype(m) - interpretation = get_instance(d, i_sample) + interpretation = get_instance(d, i_instance) apply(m, interpretation; kwargs...) end @@ -276,7 +276,7 @@ function apply( d::AbstractInterpretationSet; kwargs... )::AbstractVector{<:outputtype(m)} - map(i_sample->apply(m, d, i_sample; kwargs...), 1:nsamples(d)) + map(i_instance->apply(m, d, i_instance; kwargs...), 1:ninstances(d)) end """ @@ -380,8 +380,8 @@ end outcome(m::ConstantModel) = m.outcome isopen(::ConstantModel) = false apply(m::ConstantModel, i::AbstractInterpretation; kwargs...) = outcome(m) -apply(m::ConstantModel, d::AbstractInterpretationSet, i_sample::Integer; kwargs...) = outcome(m) -apply(m::ConstantModel, d::AbstractInterpretationSet; kwargs...) = fill(outcome(m), nsamples(d)) +apply(m::ConstantModel, d::AbstractInterpretationSet, i_instance::Integer; kwargs...) = outcome(m) +apply(m::ConstantModel, d::AbstractInterpretationSet; kwargs...) = fill(outcome(m), ninstances(d)) convert(::Type{ConstantModel{O}}, o::O) where {O} = ConstantModel{O}(o) convert(::Type{<:AbstractModel{F}}, m::ConstantModel) where {F} = ConstantModel{F}(m) @@ -463,17 +463,17 @@ end function apply( m::FunctionModel, d::AbstractInterpretationSet, - i_sample::Integer; + i_instance::Integer; functional_models_gets_single_instance::Bool = false, functional_args::Tuple = (), functional_kwargs::NamedTuple = (;), kwargs..., ) if functional_models_gets_single_instance - interpretation = get_instance(d, i_sample) + interpretation = get_instance(d, i_instance) f(m)(interpretation, functional_args...; functional_kwargs...) else - f(m)(d, i_sample, functional_args...; functional_kwargs...) + f(m)(d, i_instance, functional_args...; functional_kwargs...) end end @@ -743,13 +743,13 @@ end function apply( m::Rule, d::AbstractInterpretationSet, - i_sample::Integer; + i_instance::Integer; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [Dict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [Dict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), kwargs... ) - if check_antecedent(m, d, i_sample, check_args...; check_kwargs...) == true - apply(consequent(m), d, i_sample; + if check_antecedent(m, d, i_instance, check_args...; check_kwargs...) == true + apply(consequent(m), d, i_instance; check_args = check_args, check_kwargs = check_kwargs, kwargs... @@ -928,19 +928,19 @@ end function apply( m::Branch{O,<:LogicalTruthCondition}, d::AbstractInterpretationSet, - i_sample::Integer; + i_instance::Integer; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [Dict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [Dict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), kwargs... ) where {O} - if check_antecedent(m, d, i_sample, check_args...; check_kwargs...) == true - apply(posconsequent(m), d, i_sample; + if check_antecedent(m, d, i_instance, check_args...; check_kwargs...) == true + apply(posconsequent(m), d, i_instance; check_args = check_args, check_kwargs = check_kwargs, kwargs... ) else - apply(negconsequent(m), d, i_sample; + apply(negconsequent(m), d, i_instance; check_args = check_args, check_kwargs = check_kwargs, kwargs... @@ -952,7 +952,7 @@ function apply( m::Branch{O,<:LogicalTruthCondition}, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), kwargs... ) where {O} cs = check_antecedent(m, d, check_args...; check_kwargs...) @@ -1082,9 +1082,9 @@ function apply( m::DecisionList{O}, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), ) where {O} - nsamp = nsamples(d) + nsamp = ninstances(d) pred = Vector{O}(undef, nsamp) uncovered_idxs = 1:nsamp @@ -1115,10 +1115,10 @@ function apply!( m::DecisionList{O}, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), compute_metrics::Union{Symbol,Bool} = false, ) where {O} - nsamp = nsamples(d) + nsamp = ninstances(d) pred = Vector{O}(undef, nsamp) delays = Vector{Integer}(undef, nsamp) uncovered_idxs = 1:nsamp diff --git a/src/models/rule-evaluation.jl b/src/models/rule-evaluation.jl index 26b119b..aeffb67 100644 --- a/src/models/rule-evaluation.jl +++ b/src/models/rule-evaluation.jl @@ -95,7 +95,7 @@ See also [`AbstractInterpretationSet`](@ref), [`Label`](@ref), [`evaluaterule`](@ref), -[`nsamples`](@ref), +[`ninstances`](@ref), [`outcometype`](@ref), [`consequent`](@ref). """ @@ -107,7 +107,7 @@ function rulemetrics( eval_result = evaluaterule(rule, X, Y) ys = eval_result[:ys] antsat = eval_result[:antsat] - n_instances = nsamples(X) + n_instances = ninstances(X) n_satisfy = sum(antsat) rule_support = n_satisfy / n_instances diff --git a/test/parse.jl b/test/parse.jl index 4aa03dd..7b76b50 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -1,3 +1,5 @@ +using SoleModels.DimensionalDatasets: parsecondition + @test_logs (:warn,) SoleModels.parsecondition("min[V1] <= 32") @test_nowarn SoleModels.parsecondition("min[V1] <= 32"; featvaltype = Float64) @@ -8,9 +10,9 @@ @test_nowarn SoleModels.parsecondition("avg [V8] > 63.2 "; featvaltype = Float64) @test_nowarn SoleModels.parsecondition("mean[V9] <= 1.0e100"; featvaltype = Float64) -@test_nowarn SoleModels.parsecondition("max{3] <= 12"; featvaltype = Float64, opening_bracket="{", attribute_name_prefix = "") +@test_nowarn SoleModels.parsecondition("max{3] <= 12"; featvaltype = Float64, opening_bracket="{", variable_name_prefix = "") @test_nowarn SoleModels.parsecondition(" min[V4} > 43.25 "; featvaltype = Float64, closing_bracket="}") -@test_nowarn SoleModels.parsecondition("max{5} <= 250"; featvaltype = Float64, opening_bracket="{", closing_bracket="}", attribute_name_prefix = "") +@test_nowarn SoleModels.parsecondition("max{5} <= 250"; featvaltype = Float64, opening_bracket="{", closing_bracket="}", variable_name_prefix = "") @test_nowarn SoleModels.parsecondition("mean[V9] <= 1.0e100"; featvaltype = Float64) @test_nowarn SoleModels.parsecondition("mean🌅V9🌄 <= 1.0e100"; featvaltype = Float64, opening_bracket="🌅", closing_bracket="🌄") @@ -26,4 +28,4 @@ @test_throws AssertionError SoleModels.parsecondition("123.4 < avg [V12] > 777.2 ") @test_throws Exception SoleModels.parsecondition("mimimum [V17] < 23.2 <= 156.2") @test_throws AssertionError SoleModels.parsecondition("max[V3} <= 12"; opening_bracket="{") -@test_throws AssertionError SoleModels.parsecondition("max{18] <= 12"; opening_bracket="}", attribute_name_prefix = "") +@test_throws AssertionError SoleModels.parsecondition("max{18] <= 12"; opening_bracket="}", variable_name_prefix = "") diff --git a/test/runtests.jl b/test/runtests.jl index 959a130..56b286f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,9 +17,9 @@ println("Julia version: ", VERSION) test_suites = [ ("Models", ["base.jl", ]), - ("Datasets", ["datasets.jl", ]), + # ("Datasets", ["datasets.jl", ]), ("Miscellaneous", ["misc.jl", "minify.jl"]), - ("Parse", ["parse.jl", ]), + # ("Parse", ["parse.jl", ]), ] @testset "SoleModels.jl" begin From 81c29ec07ac2485fff670b0d394dc48a2afb0c04 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 13 Jun 2023 11:17:47 +0200 Subject: [PATCH 08/77] Datasets base.jl. Supportedatasets, memosets, scalar memosets. --- .cirrus.yml | 12 +- Project.toml | 2 + README.md | 4 +- logo.png | Bin 0 -> 33960 bytes src/SoleModels.jl | 19 +- src/datasets/base/check.jl | 160 -------- src/datasets/base/conditions.jl | 42 --- src/datasets/base/features.jl | 113 ------ .../base/generic-supporting-datasets.jl | 81 ---- src/datasets/base/generic-supports.jl | 108 ------ src/datasets/base/logiset-interface.jl | 123 ------ src/datasets/base/main.jl | 51 --- src/datasets/base/multiframe-logiset.jl | 94 ----- src/datasets/base/supported-logiset.jl | 58 --- src/datasets/dimensional-datasets/main.jl | 66 ---- src/logisets/base/check.jl | 115 ++++++ src/logisets/base/conditions.jl | 128 +++++++ src/logisets/base/features.jl | 81 ++++ src/logisets/base/logiset.jl | 350 ++++++++++++++++++ src/logisets/base/main.jl | 44 +++ src/logisets/base/memosets.jl | 165 +++++++++ src/logisets/base/multilogiset.jl | 166 +++++++++ .../base/representatives.jl | 12 +- .../canonical-conditions.jl} | 0 .../base/scalar-conditions/conditions.jl} | 123 ++++-- .../base/scalar-conditions}/main.jl | 17 +- .../base/scalar-conditions}/random.jl | 6 +- .../scalar-conditions}/representatives.jl | 4 +- .../base/scalar-conditions/scalar-memosets.jl | 262 +++++++++++++ .../base/scalar-conditions}/test-operators.jl | 6 +- .../base/scalar-conditions/var-features.jl} | 244 ++++++++++-- src/logisets/base/supported-logiset.jl | 209 +++++++++++ .../_parse-dimensional-condition.jl} | 58 +-- .../dimensional-logisets/active-logiset.jl} | 26 +- .../datasets/dimensional-fwds.jl | 22 +- .../datasets/dimensional-logiset.jl | 32 +- .../datasets/dimensional-supports.jl | 22 +- .../dimensional-logisets}/datasets/main.jl | 28 +- .../datasets/passive-dimensional-dataset.jl | 12 +- .../dimensional-ontologies.jl | 0 .../dimensional-logisets}/gamma-access.jl | 0 src/logisets/dimensional-logisets/main.jl | 35 ++ .../dimensional-logisets}/ontology.jl | 6 +- .../representatives/Full0DFrame.jl | 0 .../representatives/Full1DFrame+IA.jl | 0 .../representatives/Full1DFrame+RCC.jl | 0 .../representatives/Full1DFrame.jl | 0 .../representatives/Full2DFrame.jl | 0 .../scalar-gamma-access.jl} | 4 +- .../dimensional-logisets/scalar-logiset.jl} | 4 +- ...r-one-step-featured-supporting-dataset.jl} | 78 ++-- src/machine-learning.jl | 6 +- src/models/base.jl | 38 +- src/models/print.jl | 4 +- test/datasets.jl | 175 +++++++-- test/dimensional-datasets.jl | 50 +++ test/parse.jl | 80 ++-- test/runtests.jl | 5 +- 58 files changed, 2323 insertions(+), 1227 deletions(-) create mode 100644 logo.png delete mode 100644 src/datasets/base/check.jl delete mode 100644 src/datasets/base/conditions.jl delete mode 100644 src/datasets/base/features.jl delete mode 100644 src/datasets/base/generic-supporting-datasets.jl delete mode 100644 src/datasets/base/generic-supports.jl delete mode 100644 src/datasets/base/logiset-interface.jl delete mode 100644 src/datasets/base/main.jl delete mode 100644 src/datasets/base/multiframe-logiset.jl delete mode 100644 src/datasets/base/supported-logiset.jl delete mode 100644 src/datasets/dimensional-datasets/main.jl create mode 100644 src/logisets/base/check.jl create mode 100644 src/logisets/base/conditions.jl create mode 100644 src/logisets/base/features.jl create mode 100644 src/logisets/base/logiset.jl create mode 100644 src/logisets/base/main.jl create mode 100644 src/logisets/base/memosets.jl create mode 100644 src/logisets/base/multilogiset.jl rename src/{datasets => logisets}/base/representatives.jl (82%) rename src/{datasets/scalar-datasets/canonical-scalar-conditions.jl => logisets/base/scalar-conditions/canonical-conditions.jl} (100%) rename src/{datasets/scalar-datasets/scalar-conditions.jl => logisets/base/scalar-conditions/conditions.jl} (64%) rename src/{datasets/scalar-datasets => logisets/base/scalar-conditions}/main.jl (65%) rename src/{datasets/scalar-datasets => logisets/base/scalar-conditions}/random.jl (96%) rename src/{datasets/scalar-datasets => logisets/base/scalar-conditions}/representatives.jl (94%) create mode 100644 src/logisets/base/scalar-conditions/scalar-memosets.jl rename src/{datasets/scalar-datasets => logisets/base/scalar-conditions}/test-operators.jl (99%) rename src/{datasets/dimensional-datasets/dimensional-features.jl => logisets/base/scalar-conditions/var-features.jl} (51%) create mode 100644 src/logisets/base/supported-logiset.jl rename src/{datasets/dimensional-datasets/parse-dimensional-condition.jl => logisets/dimensional-logisets/_parse-dimensional-condition.jl} (82%) rename src/{datasets/base/logiset.jl => logisets/dimensional-logisets/active-logiset.jl} (91%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/datasets/dimensional-fwds.jl (92%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/datasets/dimensional-logiset.jl (91%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/datasets/dimensional-supports.jl (95%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/datasets/main.jl (68%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/datasets/passive-dimensional-dataset.jl (88%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/dimensional-ontologies.jl (100%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/gamma-access.jl (100%) create mode 100644 src/logisets/dimensional-logisets/main.jl rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/ontology.jl (92%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/representatives/Full0DFrame.jl (100%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/representatives/Full1DFrame+IA.jl (100%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/representatives/Full1DFrame+RCC.jl (100%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/representatives/Full1DFrame.jl (100%) rename src/{datasets/dimensional-datasets => logisets/dimensional-logisets}/representatives/Full2DFrame.jl (100%) rename src/{datasets/scalar-datasets/gamma-access.jl => logisets/dimensional-logisets/scalar-gamma-access.jl} (98%) rename src/{datasets/scalar-datasets/active-scalar-logiset.jl => logisets/dimensional-logisets/scalar-logiset.jl} (98%) rename src/{datasets/scalar-datasets/one-step-featured-supporting-dataset.jl => logisets/dimensional-logisets/scalar-one-step-featured-supporting-dataset.jl} (77%) create mode 100644 test/dimensional-datasets.jl diff --git a/.cirrus.yml b/.cirrus.yml index 496d836..fe32b48 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,13 +1,15 @@ freebsd_instance: - image: freebsd-12-0-release-amd64 + image_family: freebsd-13-1 task: name: FreeBSD artifacts_cache: folder: ~/.julia/artifacts env: - JULIA_VERSION: 1.0 - JULIA_VERSION: 1.6 - JULIA_VERSION: nightly + matrix: + - JULIA_VERSION: 1.9 + - JULIA_VERSION: 1 + - JULIA_VERSION: nightly + allow_failures: $JULIA_VERSION == 'nightly' install_script: - sh -c "$(fetch https://raw.githubusercontent.com/ararslan/CirrusCI.jl/master/bin/install.sh -o -)" build_script: @@ -15,4 +17,4 @@ task: test_script: - cirrusjl test coverage_script: - - cirrusjl coverage codecov coveralls + - cirrusjl coverage codecov diff --git a/Project.toml b/Project.toml index a7dc0c7..4e6ae0c 100644 --- a/Project.toml +++ b/Project.toml @@ -9,6 +9,8 @@ ComputedFieldTypes = "459fdd68-db75-56b8-8c15-d717a790f88e" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" +Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" +Lazy = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" diff --git a/README.md b/README.md index 7981f2f..17b5b8f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# *SoleModels.jl* – Symbolic Learning Models +
+ +# SoleModels.jl – Symbolic Learning Models [![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://aclai-lab.github.io/SoleModels.jl/stable) [![Build Status](https://api.cirrus-ci.com/github/aclai-lab/SoleModels.jl.svg?branch=main)](https://cirrus-ci.com/github/aclai-lab/SoleModels.jl) diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8d6c40c1686766ade1395418b96b0abf3eb03fb0 GIT binary patch literal 33960 zcmeFYWl&trw>LV2yF-8=gS)%CyGw8%+}+(ZxVr=o8l0dZIE3Kt!AWqqljnc5?t4F+ zs{4LBT{BgC?_O*5TE8XTJ-sJJRapiVi4X|@0HDgrN~!|@KvBq-2LTT9WVJnO9snTa z^VQUKS2y(nIlDSpf$c3p?mo_zAWLts6#(G9Ql6`mVcwZ)`NkGY0>x?aHN@bPwd&W4 z08#X5l$1r5PvWy-%0@dmyn_I*!@k!GkGCjeruJX1;2!1_G0!jvkJHGfBOCB?nBn$&He9J75w|tTa|MWmZ$bNo|1Ot!Zb5M zI?Cxjx&S87v6CzKQfl8gAlGlprEGBG_9E#66(}fn{*J|5>ds&ND(c5)vn9~XiM6&(+a9^+jUV6Olp3bwo@H??i z9z8Tf9Ipa~Pdj9P$asy;)i^yf8I<&Fj$9~fvB;Y~?bGz>G-8a8qVoM>9Yj48s&CXa zOB@{@dB;|twUH*&YU9%L%kl+{r_BOfshql`;HP}$$Fv<#`>?-J=^^X`Z$t?F4!L{Z z_|5a}59(bFuDEy8Gi9*HaFxkt6iel|YxdT=7bIIB;Oo`XlJT~d^MRJ69+Mf2XCO11 z!aXwLoPzsKM8v2HdSqK4vuO1F(V&?)IkW{+8J6uO(+D0^$S+Ep#@j5{JPXiAES|V{ zzH)g--vybQ47?2m^BBQ1ZxU7t?1)6Wy^%SP9^bo@%6(;2)Ewqabz5-xJBHGV9J}h) zHhNVerjX>imKFWe$(5t(LnGg_x;w`J-4+-dyiY%BbC>b`54OIAd7&Gzj>L0J3dZGn zv@KUoHqjQQ>*#s3E@sv>?JB>2{^`-a-hSpXSo~30czQ98P2WFz4tdqz(WUd`6_*$B_6z@>6~IbMJkjFmqg|`;FsN zm)BO96Ji{7`|-T37wI>O3tySLkJv%d@}`~z2&HEkCoQS9L+ZZo%Tbo}mVdo8|5A9~ZDerg&q^gkh-wYZY?wT0$V z>`Ncs5@UdL7MC)G{)`$qX#$$!q39k= z#ng-0Uckc{Cdtny9AOlc#t}3;o_tQkT_^?7e3A?A|c=N=b z;Pe*HDd<~XYlj>rl$v%veO|@YE>0uf^uj#aN@-a_F(bRw{q2UcY_#P|uf@&-rZoz^ zpXR@XfM~u6+^`XjL@COM?FC?q*^v@MPn7)Oe1BXSS6m%xk2P_E7eHmiqV5NSqnv!@ zP-ce3K^$=Ak5IP*8&N$>)8R{yIQpSRx%~kir5K5^sGoRx|h>1SlfjwjL-MMYi zf-~rMm-;vELw>)`v_Dy;d!1fzM)aaN4(D8qA9+!>$Z(!7erU?u)D_qvREWu}bxsEp z>hIT}ElV|ooOgzB6;!1~4pMb)OAAQjq?_@8Z=4T{8zx-=?H?FgJ{U8Wj-FCBl{3VP z23G(PXn1_c4++WZGe=_W%FVW-mf63#uKT+;WONP>idWt2=;pvP{lL0nQgC1JUH39O z1)$crV7br-Jt-Fsh#}JIwxh2I&3raw^*3Nz2-^%^6t%;H3a}Sf1K|(!3PdzxP3X(< z90??7-!6*PVbb=l2y&RDKzkdj{o$ae+kA&vFg^$1e59<8JEEkgPDnXK{3fhuc+Z1} z*C+w|`4}zh9mr{W%{5W@@-!^S|JTz@+nn0k@vZpy-!DfCuD za|6Ff{P-n2XG6PI2TlQ|p?HclDdb7B=1lo<7IF8x#IYKqs2zDrGVa7iH_}Nt(LB(f zw$TDpFGNu z$OjSGBG3L=nd+ygmnyz6Bc&5SP35D_LM#FJMV|1#CDYJ8;v0yf{VQK?7zN z8QUx+^(MkL%O!*0IG+w{TeHI^^aSm@Vl+&DUIN>NbsS9UHP-Rl_Iz}DJ!jgSU>I(S^c$(jZ&?fpb1cnBR}U~Ps~ij zAf_2z;@6R)sQ`0yRCq)`wn#gT8jB~!p^sQ>!}!(fTY>lB?(b0F=zc=DmyhV;qW}=$ zYD1tI-?+yzTSuIChx z-|@!YX^T;CPG!eIw~%(-K+4e2!fy>qZM#ZbXLcU5F3i0=75so01h{lbF1r(nnG?1F z5T%pCAmH+9emTOk?p{hlrJO1%m;MIYX)Z5t{wd09JD%JrRd?LAD6cdp`ccE1@N-E) zW@nGX&@-mwRbNUu0+&r91Ik9q$9*F6gC8U%6y%CtTt18Y;<&R|Hi6(q4X_g>oQ)5l zIADO1e)+wMx{UQ5+V_5!HAKhhfk_;Kk}7>!1!^+v(dPFKP<+?~oIur6vf|ix@IPqP zA2Up9abD&+tbQNud>kuhS2f_+B>=n}$B|v!NFs-K>sOmH#Eu%R$4nh<>0nmMd#pz#aRy0Ug$d7F~m2nqHPYUg<+js3CkC zGEE8Oge_jiP~g}a-5`GDgvZX)EVbp6G=#QU6wiT#`}D1l<1x%u&?LXzmY{AXR9zlP6Z zFo=?+@0ac-$lZUSlhhPOK(Ax`g{M&T7WpmTy8;TN`KX%cQ7pjr3Jvp(5eBo$B(p;y zqfhh*fZBLt;92dLWM+H)*pIR-Hu1z?b=-mSz;Tp^eP0wM&*~s$IT;TsAb^KGEz-YI z8KRffyfP-Qjg|D7(Ki-2<1ptY}AdSVMc~K>xZ%pY9<1`?9AnOGjDP&rqoTc4cO_WYJEhppONgx zUtwf2W3M9q)X}JVt{eS8DZmf&MJa6p*1qIILVBw6)mB3{*55(rddm1)?PcV*C|h;F zThk@L!+`c(HVj}}^_&rZsY$#J7BfVKkJgzms#X*|TGWEU#FV`%OqH>iaV#-({E4i>xc2Rb;XYJY- z3A-_LD5Io7R`Z2FP75zDyw5N`ArmnrhQDjmJ{`zj=HO`E+#k|g)U6?e9%mqhAFX#3 zvq>eJAoa*~eAhTi`frXcI}35$r#w=*nJA9_+zJ-d>YEIg+6Wyi;W zQ!lJ?DK+Q}AB!kO1{!L!1ONME=uVY|;OeqzSPyW!s~mN{S`gmMH0{U~-bXbC@%}<9 zOWLatA?R}5t?A5Wq*{MaQ7P}dOR67Fh9id005@7~q7lJR5FpTh0;jehjdXg7G5g4f z^P8CviDS?!cLJ3^oVtYPL`)7!#y!A;_&xsO4OR>MNQ4_0uKHTl1;P7j3!2cOMubON z3T6pfYcqI`!ol5_5DihhE%>O|s6?aYN-Zm{t6e(PrIfc0iKK-?jYGP+CF13de#@|a zL9n8Nn9%s(J!`FikjfF)lPGfU_)vhK3xg*U!KXS|4%e?Pe5t zOm~p-JW=QfDwD|2a$$)RlvdSe83*`}Ic_s3x*X(R-jsdN`r#GgW2v|s58-IBOf8q; z!snA6lyDvv@g2qN_=?8`Ud_GArV?z%Zp(au+hDbBVa8xM+c&9?`406O=0g8@;w zPr8!14O9)E$qa{R8*^$+U_zFvUD$}jj-CGWELdgU_@Tekb zXMNi(1bprJFF9u)ZHQ{`^X4 zNm5#JMASSqactDLS%WwG1y-V1ERZp2akJdT!S@Eck#z0a8zTw~W7H*nt#MwkeJbT( z`q=iY;Os^OJti#*WBG;~+7h947U7kti(*k_~85BC3iuOk$JVd1RR$Vnj@k8j=}mM z5AUi2-PT;hZWby?x8{<}EQ)2g>=8~)@NMZxN_0|!7yuprKChAHPU*9ohiq#7maUOz z;f!jSVNH>*J9otN$^DROnKqm)$`P{DN5?XeXqUz=Zx9p*#+fPsPh4P5X>k;Yg)sL3 ztsmL)OV^1(YEqyivXVL4Ot-MBV`x|7^0#*!@1uiibIeGVWi-ddZEJ$GWxP{) zn9qqt1vo>Ym=^NF;aJG@p0lUK7!Y#%^wiJFuqgwsBgl&JT$@~7Zf?yCEb9T>&^PVb zQ?e$*Ed8d0W~wd0d?*r54lbzDm;tVS(qq$5i)Y8-Q2@7@qjD*Ioc9rvqDS9dxu^^h znr+t)YGQW6mPOC=F}UEv0PJ=G4<*l6LN9|lL#V*`EN(u!%PUD3Cm3GkpX?#`WS9GD z{D`1}?jXSP>)7GiGmA4X~$D|Z>m%9;Z^+DBh^ zYIku6*IU~}3HE;ZLs!*wBcQV0b86aF^qDv@ZhSl{uAy!3X!67qS`60zIc!J%&?5wZ zF{ht`>h`Ogx1MXmt|e^2l$trt7NS3DFqKSU`H1GF)L-g?@1evL_k2bFr;3^sN)H(i zF64Tf+jcWLa6=ZlIguC`~VL7#WA*CF=86=IdDZI1q=x zJhcTAfhBd6CQ=^YdUptGOgZwK?`kfr3v*%dXBu^MZ|~TBxLHrpVF7h0U!y&R90t2m zj3AuE1?L%0IC<#O;T}@SK7S!^8B;?X8pC{Zqf5-7H8_A)^Vf|HoM4ts!NnR{TbK_7 zHtcMwS~ZprpFP!0wi6a8DfQl4e|6v!2}7sQHr7XFAAKh_Bv&#X-3e=eW%|a94WmiJ6y1PJYA|6*DHw1&ttwKIpa*hx{J4f^*>7)x^Rf- z4Q$7QXT#oi6@W?*-L=3)-#~H#hZIQcBgK%)r;b9BStlH+4$BSD4l{01Tg1pBNKH2p z@74t3eO#J{S-uh+vI_23FU)ZZN!B!)^_MPHbYP}(APPN0@C>b57pVO@OsqcTbc%Y+ zwpToSD5>`aM)F5faW6?(Xg)wpWe#>RS2wM`!seXAp5NIGoZ?AW;kSk8kkIlBd#|h- zhV~&soBv}%e%Xwjw(-sUZ5cwBf2%}1$NqdZX1S%I(Ff+AK>(+y{#3T64YE~phGA(z zD(S)8k*b|y%$ZPk6bvKD5Z0e7qFWE3pJW5d64_+fF%duhELim54@Sh{bW0fXBEYbo z0Kr?;DWXSr0l3wR`$&13f@h4=1S33?(9IDyn&QwDGCm(1BC(TR`mI*fmYnUQd|T4L zaH)i!bz18j-O(oxnw8}6qhUY+H!YS@!A`<r5fe~q7^m{7* zh!|En?)Wiym(o1iX9eJnQ}V|aT?@((^D=NvqrGt?V_qz+F;Mh_sun_}a>y1@MV=-r z4qzx45k>bTiTPzlXUXDLbg&Fh=H2}Jxm{EXa>WxQ5(m!9kMJD}0_lueUmEX_XVn&c zR!F+@L%Z$9D?vHBREbYY&55p;;m#KpsNEH{>{3c{D&8IDnp|ms4(GTgRGO3*6u2L( zM`ggONB{sd`eYK;9oeR+_j)2R^^UH8U0fe?&I@`7`e05 z{NKYFsXPrm{9_eXzTVrBFe`) z3Y7!8bfaHqpTN_r1o)w_b-!Tp>xAN^o|*`kE9Ao2!B$JfZgW=iuBFhTeRv_lN|x%> z2#^(1y_&3Jcd6o+wgIJQMzz7s6+Hc-hd~exE;-v zRLP~OoNS_xcA*E~B-u+tsl*hGD*>(AA@kui(r{C$B21mp(&pVmG~Pna(fj82WQDtB z3L8EwAaDOzw4_S{z1z2ol;%DXVq=>pbP+?IA5kcOKIKb3Beh4G&qnIP&~)$+Cg!YX z1%E2O8@V*wqMDSE`B8K`=Qb6%TL8f5No6)2rfn%whN3War=M-K*xK*LAld)jwuBZC zRL|f5+L#$NUc!1h{;419M9B0|=&r6PE;C~5GSQv+nArfLc|vungwEV5Lc_MzDDUCJ zJ^3o}suX%;emCVMT2Qm(5&?H)#H?uV!9O`5ht1LGu8SD$gnHDma@Sk9y&u|5o28%@ zFe1+Xg1YqVYD?cCii?Jnwf2pI%q-zk6SY0=Hyl`$xp`5UN7ArrAE#nPAF3f}SG0_; zt%in0(4Pi|Y)Mq^pm9`HVNo_)@TqQ;9TsgwMjVbFH8_dH=qw!4%v9El5oa~7J$v$%589vU8i5PdP-jQY zwwK5yyAY_@+jsuqix(|=G_-3~RV!Edf=fN>vovCWAe>DeYqHY(+6cXA@U#h>Otu-9 z(&O3;ktBJVzkYu_le~iJ)A;V zc(I7PsIcflJZLS^y7y5N{6M$sdab}fo6KgLOZe|ikMyDES1DC+IR1rD75!nxD-3pp5+QPKdNMxk#LGQt@ilc117gN%JasG!wAEo7uuVfGLt3oq$Gds zbiE`R;7_jTvBmP?P|dD(Q(;BGaM(v*C*lIMErQJ*fkiy^*|wKOPF1(`@KmS!F@N`s z(UD>$;z^Crsao3%9obxNsq~*ceH!`nC1od3d?1V0Plz?=M-Os1%3^VRmoQ}?2&c)j zJa<%n|C}jHGu>6HztrGIiS;*5YIjyQz`soMtmh~HhMUh_BTG>&wDO`X5+GMT1uMqX zaPuq8E}eW7*Z_9Jlges@cL`~)hyP|gnJmNN=i6j@OMob9Nwrq_rzq3&`^BY#iRLV3 zk#86JwtI7&@fuJ+l-zyiv zFqvnsJ~B`g44eY0Gww*KI!D=y5rG_34%G=SBij71Y}Ne4^{SKWco@9zStEZTR!&_X z8RU9qL{f6Ns#;%%rNg( z+{Qc)nw28kYlU%eG|IqT1>utPg=L7zkU*g1Prck=+{z@kFKF8efBbS%9cGT^Xw#^% zZRZ-RnBV7YEDBNjgP;vgYgRCsHQ*@_=v?(ga)(!}p7urKr!dCm+LWS;vsPzboXJms zhmq(^T8F$!@(Z_VD$B<-Wz6w8RUIYcIXsJDE3&jz(COH|3a^t%9`OxXs?Pj0z)nExZbKS2 zev6b+A}}zDG&gux1Z(U(X)_qGDuAb$>i(0+Jd~UW;S(3;fpjUCh3u$A$px22Su;x^ zpGWv)OLoNEFWr`^Y}-mM`5eH{g#nFjkRbfQ$JI@6Vgq)6!QHX7?UD1j-z^@YoVdVP zP$7BFsGZp7GmD08-nr7bVk42!=3A=5>PW`olS3&{Xx6GvS28d2bkWA7DJYyh%4-xO zJn1hZMzea-ueI&qaCPz^(1)oWdPCsnNZ$oCfrtn~gF=3V;y3B-bJC!(Eo0%=sCQ6C zHgb1w09ldd3isJNe8`D<1Xw~sRZc?U-w)9tN9TC~DMGSeMTv%uHCpL$F_B|=Evl69 z%3^tKabs1<7Le-rvkV@|IKi^&21Kw+p<$$PTJZ0z4T%k4#V(d}npo@z|+WTHWeaAu@y2qY|be;W!Q*N;1$94BXB0(%Z@=<4Dh= zK*NlCpw`aG!c|Q0PUWq&<=;mJCuvQ6$ZIvrUc4AL#{YgYp_r#(E%`KatUTEcI-cl;RxIq-{;y}_K?u{6Ffm|ayg!?w4DCT8DW6tEmG?A z4e-(~dRMdbA%L_JT{QNK{2Bm0s^6xdc}c&!>vLy&(DcI76VRK~wc3g*dKdDx10fjl zR)emRBEPwl1GA}xlbI#6w}Ug}tp@-=P{iBW)ZEU}9b{%{4R#bFzv${C2Z1ew$aT1s zSe2Y5EN#HDzOI%UzRH^BzINt(7UUwrNP^z{5CIOB?xrAb2YW|1es3Z2f8_E*uKzw} zAqV|K;%+BIuB)U9l5ldh1aUHRGP5#Cd4oOK$%THMf-Vv~+Z*_^%=?%>RSm*~8WTA9F0sSuE`>9U!D`kWtzG z+mJGHN~-_C@fQMXu!HkIToADT8>Ktg>OaByZ@T^M`Ny39stAPrKji*5>VLQWA7TiV zk`lk9lex!V_2eXl$p4PdZ{cJPw&4HglFO2ho7;@ToQac*gOiEVlADu>*V3GiiH*mM zjhEGwosYwk=f8-Ob98ezbu_p9D+(f<84QtQWp2j9$;)fT#9?7>!NkeO#>r&HVad+K zYr(<8VZq97ZpHIoMX0!fAyH{+|6jHGE6M^Q%94wlosFG?or#On)C{5$A1f0tCqx@= zR!a*m9xHQmPE)>rw6QSfmv(Y>Fon1i>|knb$>Qv2{ZGeV!1={g<%G!DnOXnyiK@M+ zyA^~%h+Glu=;8gJ9hzVVOAU9^zi6^?^Kx@=aq@C-^Kh{7akKp=k+!9)8zd6{%4B0@ z=J=P~zi#1&7y|*;^lzL(1pLDRv4vm4)zZ}6$yL+I$zF*3uS%f5od1M3Nbp}ykp;U! zD181#{J+J#hNa8D?*5em_TYcIK%jrZmfzI;UxT=rdRkiiGZ2LDUtQ)lrjFK@ko^9) zfcp36;Qy1eIIPSpc)0kOEI2GInK;cjc$iFi__!dRx8miq;N{@qH8cC~(%qb_+`UX) zEyb)MmO`w71kgX$fN1}LlJ37#d)Zk2#fg=jgNc=!iH%j0m5rZ^gP)6=ft8(~m6e?3 z-wbB?o7Mkru^`L;3lqVA2>weMfbjk68Kk^GsujzBl&gQk>@SS}7u&!2;{W0Z5YhiT z$o~l6|Ap&+;rbsT@INB{-{|^Zxc)~7{EvwLH@g0RgA3_DH$0Y(kX4WuWIIy;G^v7Y zh2YE-WF-ID6agM2x`H5Ah|aQlZU6u>_TMiMkdp`5-2&j<<&>o058)8saq(6}-8w*y z5&&|NVw&D7zj}PK3@r1x{R`{<)YsH+2q<`ED2xAriQbaLTIleP4o2p`K&I?I0fi-+ z9%x~I1iE85KXXPShoXe=B86fgARs7W;X&ieiGz;M-RIWjd^c^4r>|et4Ab?yPV-i# zssqnYLr+hXbr!1+1*Zc7PhUzYsCkl5WT{LN^~-sArqN(;z|+_c2FAOrB3Cj5sFjr!c52}t4aP}J#zFav zSimL#B0V;`@V&{8+Kcy2-688;UTIR~AUu%RJ{dkrNoy;vgoK3No7HeEF0cC$27V+N zva~28WSD_OVu1=u5dg36Ev@I-iWHt59NEmW^53q=iG6Ve1!M!}rIsK8rwu_O0Z$aT zunu8{G+F{7iUs$>@6PvK*P|G`I~ZU+`)1rXFtnjV79p-Aevfm!sHi-8783pp;spl!eMn{`G`*y+QV7*o;Yk zlldqs1p4M8`tZB;u%ovI_${GpW{|QHg?lT|gTe zit-Y@bv~SKite^9VI#9*+iRnJ<-;oKk8|!EgnUf+2(=laD)gB%Sz*L5oloo9@qYEd zi1pXCZ^%D}PXJ;L*^IFOSHYg!)QBPw$Lf>KeMEE!lttIJ}b^XH;U zA6fNdusQL@9u2aRSdM!{<(pNovP|hpy@VlG^&dK1&x?(OPxW7Fwl1C?PaDi%9)DYe zCRUL#e>+{vEG(v_|X;Kp2nMl9JF$!eTBk;3spK~ z)E8QEDJ5n)Q;I2p1AwG?Yo8fT*f6#nIc@@3>dxe!t#&$UW9WcC7n@1{RGG(x$}#bG z7~S(R1NnW!N63C4-yV&d0%iEN72pp?Xbbv32*9AZ=?bdD>Yb-gD^NsU$VV6Gl#tyz zELcvB9ft~WZ2QA33hZaT9JwDZlqOl|p#z`{W<&i2IwuD0+J3)_9vQqYz$J0uJL{ku zh5JO30e43nLoL{;ZGZze!)tf{m8U4yuE_LyAcL)l8&wmaGW6>=6BE$Gt86L^{Rhvm z*i=GPq*O{VtQ2keG@MKfO7AsH$cL3Np{9nGfPIMdW5Ux#U*8c0a}tSA>?nyJit+1p z=6EQ>m8*mpiqPvO{=+;eCINB&3cEa=>G-MJ*KNw{Qhc}{t?w|9S>F*|TuW?CGm$7k zwZ# zg0S$|*s%tWEZ!R>NWe#>6Lr|%FHZ+*V_id+ybME%GpP6VcSBl4;YsoI4Zh!j3cm02 z9fGB*sobDD6=Rxr>mf?ybD+VDZjw~uNnV2#0%cUw?PqHVCNA5Wc0f-crch-Xbd|;k za9=Qc&mt9!1Sb?0$&ZGIP4*Q(GBUE!`)Vie_bbxhDt3E&i!r-^5{-ho^SvPuZRzx> zLemu3oY~kRVY+tnLrnA7)YDX*b}=?xvpN+hqL0jIF1nx7qWFeJ}-_hl&aFeJ-?6|&i}HrJA|e>@Kx*-;b* z%noKW8=wWgqI^9+cVsmDqS>}#OlATptv9Q@hyC4JS;wn+|kLOa$TdA9o`zs$bqOxGC5OK6HzyA z*1Uk6CB;d`lnMj?Nf=u_aEBlki zHB3|DriESvS4OFE3=iAlrbay`rg8bAo}S2f^REh->{$9so&rmAJQjzJIgjXO+^@en ze)H|GZpR`InUB8eg#nt`z~Io%7#uXcbjjcn5xx+xUZ=j+>8I>s+5B~2xP&+q z8C@K7Z1=mAEz!q=Vvj9FCiYIvP+H{?RJUWn>SXS2o(*qk(uXk_O&2DVuUkVg*y&U` z^a%I?2Xv4!A^@34vcFh_7Z3bA+o~asjesq%zZmT6yV4#B>bgmdb6G5b9cOl-jc1zf zi~n(n74QBo*LvjL%SDi}lmVH^)1Nc%UrnQ&(|+5p!nY!q z42s>o=<1AZIT6pUTBhT$B0h)f@qK-s%c-1@)c9l+^*327MhlgjVZ4mm=;#nrb6(0( ze6#Rav=(#hMo_BV8VL-Q7PICwHLD{n!K{p2X9e-C+a3kC+uoVz)=`5PlD{{j{!SB5 zcNaQ~Ttct+yHePuw6N4hzQED?9zvNuZ`cvzz$>DM@^deG+cScL=_u1ze^yVwjXih^ z6<33Hf4Q}t)!U!*x{s@y+vn|QBXXDD+F-#$SuLX)NJX6|In5Ks;0%D?K_1QzJ}Aw> z;N{x7!6tCaO*q`dY!QL&ibX=dg0phC%X9cU;>u@yq6E>pkeCbqLxGy^dU4sy%mr5u z0hdRSpzyDSKZDOn{YeKjXg)7p9J_B3zBkcdgEMq#;8!3JS|WI$zob4gY|)@s@p}2Qb(6=b_TqUG zWIe2fRfL0LW&%lJ_i2u;QRzy)6vhsy>c+8QNS4UK+}mCd7_`~|r#`uu99rgHwug`z zEOU#zUIe`#TYC$bt(690g?!Yd5>>0x@6qGD!XHCTbkdnkv z3(oZxz|cupy4?!JVq+H365`ZBmo`94xqvt2QcF!`HCS_WIe#3REMTKba8b+zqF9qY z;|ju~Lp?(#um~N~6lgCcV1 zCB$%-SYf**}YVN8`t<#>BEC~2F9RHCEL$u=phVnB1X z_>(7q3KaP_bInV4J6z6?Y6}{aq1V*tKPM{eCnry_)#PU|_Q^y@{7i?%LJpV$pJ&nqB~9}5Cu z8yOQS;Dqcg4&?MeaL>Fiy8>)iD}k~D)pw+ptf6k2@5;idaJx2~MoTY9N2hrlWSiF# zlM;4sRNnZGokUjH-}n7&!G!1_e0O`J7nfW4wRIu`GBJ)rRTdW%l#w6hOfbt#FtvX= z?*-Qrw)0bw6)`=+C7btX(rWA0#&|Vc))g>xA14@?m3mpvr9g&Y+8^ zfC3TRCiupF?t2P)>=rh7duHGj<}Q$oua3sttEESVOhD|fPxQn*mdzBn)yrE)&}2Tu zHO$?z`EW5mxN~}9#Wr!Sb#clal`Cy}o+~xQuO%_qKSU@g!7cn8yPtM7T_!CMUwV6? zUW0$l8Q9_I|GC0mj-=4rvEK?UCl{0T5ab-&s_)6&zk#3X`V;0!Z~$5F4}0ytO2 zZ}0x2^Q>}lyIp2@(N2R|g@Z&*su~$$IV`WW!K3uYoa#$=wYCZ8Y|&JM)I5=mwokG^ z4xjSd%Nn1wcp!cMVBuPn9#0_0`Xe$NCJAG`nWvd2cM43j3i^f_<)VcRt)90y~6jI za%x2G2e26AdIjF82*QE|uOBODS-<4$=mp~BzI#9n%_<-&4vx6_&Uz#ZCCOydVTH{2 z=||CSVP8(!24pfth!ki2p3mS{X2E=~J3+KoCqT=PcsJFLA$)c3XwYdR?CDhyn^q=O z`Snj6i!AwQReA>{v%6~f{5B+AWG~)eL1iud1Xj~p2{3^xoeTO{Fk|Ft({p3hBYIdE z*ZJRMgR2SjTS3?lYB)38n~EboH8{^h_#1D7)?^het!hN@0SXFg>?NDtKE}$-cFgzl zT5qKBjBzw8U6+@$=73aK)esvqDvcP$%LO^v0XNs@-^S1ER!&;hm5J1d7y#iTNP&Qw zRGrmbzD=YQ-$=_=)HrY54uaco z>c3KGWbi~H=MpwrxJJ)Hd@!eJz)qGymVqi4KLt=mL4E%9uCcx=tD`>xW^@)I>Z4-Z z1Qx00wj0D6HpYnIo5`Z_UuLNG>ckmuSJ}6F(Uj!wU)~v*L+eI0H%>^hMmKa{(${`* zOTEpGzL3c@Ai6kKau0A?e$%=23n19+k{_j_;@SgoJt-W!voZ;*w6@EB9v!ogr;EaL zqiL)auSqS+xyX!p<|PiJLPmH1qap{nlIFY}7tW&&4UmODJ>m8ozPEmw73Rjg=sQy& z`%IN+yLNYl3f_YUoaPW-E@)+164`2&?>62vD-js3t|724?_1YuYsqctpa6fYbfQQb z^lIlJ)rul+aU>UR^qb83m+G63!^{`C+~_nOWulY+KBj_%h^H4c`(kV12`}G%-)1{l zLcROk30gu&5NCxo?uE-db!t!OsiWMKxjEz*?4Vlv*xQPYSqA5iix@` zug)vbybyOk-|H&F++Szb59VhYha(M{={S{Sc=WPXsw2?T5)(~dM+`B1PVlp za)#tF%Z89yE0K}O_1qaB+NiI)CyyPN=(bR$4x}C@UuqE;kd)D)#*(cO%n*E1p$F$F z;grO(RA&i{>S}Lmgw7R1{Ar@s3YWjxE#`7Kh9%>nPI~!vTOd_98mFg)r~8}#+2(7M z3#0@>b{ld{13G@w7Wwc{fL#5a=N97oS>1gePUIkHd#3 zs6~2qF5@f`38YdQx}2Mmn)!$T(5G5$+rzOak^n)Pt1v3A#jC`WKTR?5ju%)mTm_G7 zmf!ALCnB7UH~A*M*$&rO8>t7NsNNh%Yf=|7$J2b+vleUn(ME;*J^UGAXN-&Qx>UiH zflclg{y5%Em!~US<4#Y0Dqxm}=TpHmBv66)j7aunDgr~CBgi9EF#uvn@pnN zdlWRX_w{f8HSPPLpkwumcnUVed^9z%;O=Xe+RLoc5ELV9&38q@l<3$m;z&=7$<4`y z3V2ep~_K^SP#l4IGcKL(_9r>?<+X% zt9PtJO6wes_pGaTScA~O4iw2Dx)7M($Sl+LH~63=VX@3$ybr0yAN?VjNTB04we(_H ztGy_{Re2>{{tGiLCW3*i|I_y-78Ibo-WLHPG$F`#-3va1o$-0P(EKtbYUZuKAvKVc zfG89nMQmv;?|17vZZ68-2*zA|hj)-A^v}Rq*jPhj0ERlQ$w8Ya!?}{w5C90mPn}uJ zH((fU)RaE!YwUwh%`y`1cjj(5c)Suc~hh`(AIlxV61vSBKUVeSDu2EKK;s#0HX*c3xPF)aJ4&vUz%fMg3$3VO4eS=btngLJDJJ zZ3VVmKaQ5fZb8L~TV3aF+LG_ds5&<$u7r-+?#F4~WzN0VWC}5!g|Xq@U%|^%!ZIcR zTG={4!!qWn=&vHgJ`NSK^O46VynDe?=jgAUtgwY-8FfFQKyJu9d$K+dx&^Ax)Pf2! z8Tzv~WEOGi-WQ5Y|K!6WA=DIfqqLr;kpc{KI2Tv^s4qcKZJ5xZHZFk8ag|#qZ z5d*u_yHWaVQh6rgL;y4xMVSylnv6iGV533>qU4N2!b90WMj7*Q@wpXgRT5q8hT(74 zbC#`=ea3&jr{sN)kcCl)YEnmmK7BalY_+m4cYyj7A=N*v0tTwLK>oRM+-DshJDxPp zrpwvjy-vKlyNge%gEwDYR~NDFNP|)g2<#($L0Xp$8weg5k#4jWkE$I5Z>>i|a+V8B97EY;z z$7_wR*V!1YvFcqgIx~YIUab1eUm@}9$(0Dc$4Dhhkqq*Sgm;0AbMqSIQVm;d2?hd; z2`8bNhK9yU+=099>mG@Fz~Kkyx8IYJUI@kWJu_UKq1EGkheBM?-xn9b;=qa$Lz9eg z40y$f8TOg%gRsS=(D@}XCr4Oo+w6kp=9V^GKa29!v&6JQ1IQ7Ac<=)eMvk;ZSo4ai z7{=Dk0|ivbf&y2`%!%#WY<-8C5W=%t+R?eB3|GaPv9*x37>Qbs9Mo z4h)cjg2Kk$p7A2og`Y8KXZibhijmKF$^uMuuTRDS76|tg3R=yFvD~pSIp+BrYqP7F z8Pv{R#MrnnRPefElyca6cAf6WM0k;PE>0@QK@y14^zgI!&xc?5&tm%&==hN#i?*;6 z&3vOk?2F}@nPl7AFmXzmIm%0}qgU-(SOfr?0CGD6SsR(stBjILAh^x}vOMl#h5hsvA9V0YVS1`Bc^f-k{n{?)rbEykSMM2ng0CDIO+a!v`=x&cQWLzWC!p; z_)|A!c})!xTo}-(;T?|DzPqs*m2N7fsl9#eEft%dvm?l_xi(|Vog4@4|LN;21ET7p zy-y5XLyvR}jf5a2UDDm%-QC@Sl+q<4-GYF0cb9ZG(p|oX=idAE{Rj+a_BpfnivRkp zHFSeE=1~|D{YJDA*>e@sP(|Q0>%?->^LeY4&`akPdHGn{+S@X956D!_m+|;&>FZYv zJox_fWc48TMgRV?F{1I_vX5?&rN1ydH#nL}(Zzg(VLeg6G{(SpU4WdR9&)y!K2(`) zuKnk)hgYkC`Me-k#NnaMtl5s~V4|9Kg_a96vpglO5axTNh$+GZ{wdr?Zo89r_isLG zyu(XjW_}(RQlbYHO_v-;OmW)S+U{s)Vw0bIaN-W|J=@bBWWDih2C9y}H*0)5Yxuan z2Clo;4|^AW{_gJe$7YgtYvH1lje0EZ*@6FA)s9eK&-Hk=?YU%0;Otr*gTkKd9u5})3*XJ4-6dbn@pJU4WapMQ1J2c2gz zmHi2EL)j-)TE!h^ljBdPY@k&FgBEVgoJ+#K0td4J^EFYzM zZp_?^h%T9&wIKO5x<2<)T{y8eIrxkC@B)Q|{*o zs2mNv+f?$^6SLwY<>3b4|Z&d&w(Pc*k1T;E@eTb8f0Hf`<6y$!Lw870`%AaGZA z7q;@I5J~5E)Hu=fZS6lg+_I@v=s{Coz4T5&7geFg~gPBG&>K{-SM}l z14GkAio?HN=wo7Z16`8H19@xcPLJO9_*!9eWxlPvpqj@gBPNE=m7-!4W9Ak85vC&E zPTrYJmyFV573OT5X>n@Wi<>u^VStrZQa`#uFK@f#HkUhI#N5~GOk&5CS>FAJ{X%4Q z-p^`1`;BxEu%9)`>bErlN;>##YIcA4TMs63+Wx70i}Lj5V-P0s2m8N<{;hnw*Ezhd zv<~-!e@SPLuGxsu?uh7BkAu147O>tOCr5%p@*5=XEyW^rg<=&+J|F? zp|@X#BsCGMWIhAqv3d282F9-hy@tdm!n_%&Eq*u!4ycp_bnHcgIJBDeY1 zy_mP*V@BcBO;83p=ga+}k3{9!?yxOe(YNXi%xnch#fSkK(VRVYF0R6opI_2QCb>Cn zRA4RouqnF5cWO+inPgWMx$wUrThNl#Jv{jIw*JVqYUV#ZGF*F@OsZPQLd5*%p-*kG zm)7|1kYV|*m#GGT>IRYs)3CUf}Wzzu9JRBF8{f1^+9aZwL5lF#_*4g%Fe)$ z6Hn~q8;AO)q?S9hpH-r$;94t=O`UtH~Tao9po(oPXbP-khv_nk|$40B)|Ye{UdT(y(;iFC-Z5PG|_1 zgJi|Gh@dtEsT$uC)6`5Y=W#f0Q&(3v9?RmzuUL};2!+^_swwSupH?#9*WStGFm_94 zo%zN!srWt29EB$AyiMQ)+e9i|yQcd>8vT1Z1vOjpz&Q9`XE7aAGhK_dS^^N5% z4t?S8G-rcsYUC~ZZeIJ~D?U?GfQq%zF)?YcXop$-Ql@V|;LBybS>k#^YIEu2Ha7c7 zrxsg|qM3C|lQnJ_g7uk9mMZV4s=dt&+GyJUxgeL@aW!^sZmxE9IL)Mv1iJc|n27B{ z5rxL@N&&DNOH0_%;%0QhRvCTEO|GA~F2Aq&BaW?u{IFT6L_r>6?~fyFX0(YqCVw~L zdpuvy7q#AOM3VQVyw#2QgfDKQ|&w)@=>KS@PJEHdxyM1~dP z`n;wl-h-UM8o1{Gp&B?8;QvIwB^*zs_WUvY_O88`qEjpqv(w@y&2vEiXVN#xG%krU z9%UL8D%sbBp@U{IgvG=DS_}*10O9i)qj}VyL@sH)gqCsB|UNA8*7<}Fi*VZDDsiUG4_PqelO6}{@ z0O(UMPVN}pJnEGf|KbEH6eLA~WKhNZVwP(AJV@%L#sJ+1HsNXOt-=9j8Fus*$BrnT zglJG04Ac07?3b3*Ed^yf8r^_a6)7iR@zP?O_FS2V73~moL@|CdN=+IIFR!%5PT=K8 zEUB{R5f<76zyo((1_olj-HJ7wn8K!~2 zT%T)Lihk6Y42?Vj5uFVgvuAft&wlgxFU4&=GE8;IMbpv12eUblNNo+ezxY(OK0|D} z@6GLNcNMxS=5=wMz3-YWs;C%WdPw2weU>fYtD!M$dfdi=9%??EVm)**Pb9^phXRDx zcnu$6l}Kb&(2hMm{LdpDgC&1?`bQoXoT)SEqdiQ-Y^xnAQmCrBjU4gFj+Mey3s%f}m9D)T6>oEKWrDwp~orZKqyQ zfC;``jta{AJI3U1US@p7lZ=mz6~>crNkE3R!M#WSpsAx+=z;Szfk3#i_U{%`;?((W zyok+j=ts$^zNRKpiu$e{N(fBqummO!rSRY1?KMe=P3#oSE0uTa#p$3x??;6+4E30` zt+QN3n=#bbrW%)u4hSp`Qvx_|7d*VpFv`)ZS0vMY;CVUhz=!1Lgk@W%P+3{2aNc@# z?-$dG4De*;Vr-Bv_RqaW^*Y8}nM@AyU9e<{c?q|VvANe)hrX(K_=*;TG+k0qxb`(H zm1+@WB~=InqU5GT9}_ZZRW9Ye?v%&Y+bUReV_~ILG3O;J%Ywp}t*{Wy`QODit6ga> z1?x0Ulo`NKSlCr8OD8zMh`_awhd}YRTiyAxj~|JaJ^+THCITwrk-*nMW$fjpm3WOL z{sB2wl3Ke5C*ObDpWpS#xh}pX_k3-hD-b!8AOKPJFoo#_J;E4BST@}F@M+#lN=mLf z@i4w)kwdNy0i%ZF^}oOXoS8O$GZ!D={Y5q#l9l>GC9Cc>6mg%dFuMe8$h%s#6twp%kJ^0`7ZoYl_nnd!s zJH}wxnWVI|H0i{@5-foYBs!%va6$xunxlXIzdmDpvxoV>p zQY4-wARR)10sLJXfjNKU8j~aExv7knDoS!ROqeHZ@)sAA_|17H<=g1LmjIh3OpzDk z>%BdIZMRs5lF4P?)ftKesHV6;th4rf;#p(V3CDl89$JbUiykiO9QUEg-VrgBlI5Eq zE}N}2#+-jZBC~Au-|kVZ+i(el`=`%|SVIqFHP}cp-FquRN1IOy zmd0lyXYP$7mZ&!iGAr}qSsw0t*AGC!vPb~g^_TE-z%0I`Y*H9qju+f;%G+)X?&YAD zmw?$*5%tv0ux{h0hxzutY|qtbz$>b=nn7_o_xFpLk5~gb00d5sc0@d_>{=^uR3!|a zUr$U>9F89@hs_OCClB4>^2h4Of7jJ=lKfm+?kdxYv3x zk{W)qCI)QV^_*_w-R7rrfGHw4ZU5I+h%)AfrK|HW^ zwzqO$HgBrvm=QUU8MOUep?-cuIL|UH3!%ie_{qz9j`p@^fMa95GxSfLHSR0r%Dv|2 zY5`kbr(5 z@?5Bj&L#y?JnM=M;3Bw3^Hu#jsfGp@XPSe^N`ECrWD>OQvY2oz+xiy2As0rhIP?Pt zIsK~nBbhh!^j5&Vk#HmRDo?CQGxUu%?l1d0teK2Mv4sCktdj8{s;3w?i1qQ=QthcF~ffWy^a(d1(Dg0zu z3OW4g78^UYMRr`o7UMbkE;QeO2n`(r1OLh2osaaDG241v`xl_RwJky20mS%`15H%{>O4T$>XZe$Z5!?rr#8H1#6F+Z^X=H}+kAf>tJe^zmBf^CEjMB;bz zfzO&xOYP6P%KU{PxOwz3cPpm>kxLHE6=oK8yvR;|ek4VTxf*8Ayd;<-8Sl~1yMrh^ zk5^lXHTB&ANED!`Cl?npZXf-Qbl187m~7~U6!%uW(wFI1m5q%Jo9$fr-EyJ~MF-^Y z@bIkl)E^7rdrU=|4N-rq=rEgTz)2_J>DF_~cN?*RFI0n=s#@NaKoNuN(11g-39z1u z4=bhWWvD7f`p7_p)$33`eIO3SDH-q;0>m=F9GC!9px4#pM>HklC%a zym>VOIRd^H0qrfwXMbu;GgwSC4mX+Xec_WnQsdD7gu|6J*~XJR{Zm8D5P=NSu;P)q zDK5jFa4I!3IjM{sqICYa{(QUEZwWBYEw;$`**$>!((_z#2k7zeLf|}f99=V_s`M`# zPTePxAA2Vx-yQFJl^cB|boAHY`;%M%4WNt#i(d9OC-tx4Z4scqA5iAHbiRHMMbNR#5cL z^uowbSL;;7$QJbV^)cp;XwcZSj5XlXJim9U=9Dti4~_RRgGo&8hp`%t7vq6ILqUEW4D1aVa2)m zYQN97`knvQ&yO`~X+Uw5m%YZENf>-sDBhLyn3eML_}w{W9QpR*YZkN`#3>OW;Yldp z7*)yu3vArYu$@?4%_;aL@WVJsk#rqEsn39o*l_IJp)v~_(QSNnbJMkVIhb=VtBeN# z^=d^={3kdQ?p$Fg^Sr6j{mMW^vU^#Q)Gf$jWr;NyGM^Lc7P{#v?Qm7Lj6aew^0G2{@h!}#?a+ag}Fv38$s z6+LspY_G!`i(piAdLt;1_y7Kl8%IrS9^W0$HLT?tcEMhkAk+_QKKadb8mTSfc?DSE z&(t43uP1&aQ+5yHC5OcIHF{(&;~+>UP0Y`0&K(IYV<$)i)XYCUpj=yx4blj~G}=tU zEjQN5lv zeDmM)ndgs?xOMgdpGEl`y1x}h+ zAZA|cLW9#oS{)bomUU}8oQ|$DQxU!Ih-zF7Z1B=}3oiV)YBINY%26Sb7{dTDI znHN8eJy=zMBqCtZi+YV81OGj)y;n~CI*Q5(L9Z?EfXGy=(BehGh;5|L{?liaO2Q+rqCl$Y zvi#JWFC8bHtq|$xw?+JVe*sTh(j2}6N7mW+giIj*cz#p%xlU6sV! zhzsnz1-RcZ>Y#`e@6tsvmQ*IzIO)jlZzvZ_jn33BK!ed1kY9XR`T4;VnaxAhw0Gu% z$FWj<6uta(ab=~3UhbFu2fXJE7wLd={K&Y@O#*-I`K9x7#hq)P>3o`Xn;DKIibD1h zPBJ}B&7y}tYryfu!*XT#yO6C6f;8y3@%`t2OH_-T0D$)^GY6FLRghIW5=U6`gWl|{ zcbAQ%=~uNVj1UBTJBDv0=l6odw(6W85x4opmEI^&fP?0b)XkpR(L(`CKby05IW9!b z+IDBsxj1J*0^4U)qxIUdQ}!z@qtLonL{AydeAsp;G_dVAnPltJ6shjr_@9@+_6S+l z);c?Aqxmp_-)W>!kLSw+w-VxgrPpOjRVQ1Kgt{*s1f9_#uDql~*?hyb+#YD5dL$ME zGP2SPUCyslWfc|b_U!ee7N44_Tq=Zf$2H%nl+`E!DG>UTz#%XujUoX_p>sW9(}|qFzW{JBWPV#4_%DTOt~#48e{DK{0rXS7 zfM`o@OWtgWgRWcK0b2K^Q?_uOZaMxE>4 z#1#i_g>1gBH^L+4?Lc*NB%Pz^SW?*3lv-T52asEwr{%fvTU|rJMu(Ipg#1j*V5+pAeR6y-BL#((Mi@Le-kXSIxD4bkgE|0mNdd+&zuB!s|>(B4shy>XL zL#6jmPvdW0hw9<>M$*3@=6TS*(*e{qvVgmd{E@lQuTOQz?Q7(@2DF+Nt43@_t1aH< z-dY+&Qrdx(xwLpcifI@;U|h1z)LD*~n>g%=beUG>ork6#sp43YxD#Tpo)xzBmp?RF3^LANiL{|y-kRYh(8}N#Tf_mLbyx-GCgi| z+<^-Y5xXqKv{iq)Xb6x?199Y_Q~;o-mZh%VJociETPFWHk{gfAduQTCKvXvZ7beNXlig$qFkUHdwK2!7VBlD(aPT_bK=#p@ zsno!kg4gG7L;~1vHBZXdAR`tW(ZCJN5D*a&v9Ys%D;}TUy97D_Z7ibHXmPfo)Hla> zOnjE+4HiOe>!0?I$hx9z^YRC=qG*M_1mzdU5P69sJ4FZ=O?Nspm;z;4Jc&W7Ei^uWjQ<0zBs_V|UMPpo_N7R9 zQW6bd9=4G@PX|+oNOJS!nn}v(OEU3XJxTXg#eop~&TJfe{{{n!!veLE>7zu|ysd`S zYXfMI6;|0Yy1^#+I?W_%5gP>+a^SJE$4EvLLa(NvMq6_wAzfEeQ=@#@J%$21=CptP z&>{n=GePi?d`9plUlXr3Icc3fPSpyoA@hiXeXG}IzVjAEU zOY|nbf+>QTM>1#vDLk1y3c_QnJ5?aOZW)tpv$({bZ<1eS>{k=_+$allu+|#AOW#y< zZ_>zHJB=LQQ@y_3LYE6({rGr&f3NkIq(8CC5_sQezdlQ!Rs-aayv0)ubzMm6sMe3^ zCx4G7y_gE2vRRpBL*PXyfQ$S<0Qn#cP$Q)*(HrRKhdTD&tsnKvFv<;uWJ$#1Uv8{YK^c z4WJE>{q?^?1hyEL_H>yEdb?5p^|#a`^k{FX`sDJ{m&CO_!cM&k`$0NoJp7Wj9_BVc zxEZ|;z=fLu-&Ml^qQoU4+o~qnq;m2gRCkAFS0*|?3W%B2tDKJ(5f`A1z(pp0Ydm%b z=&_`hH%*iP4w1H8O>~5ZmwEh8lQ%k9138}jx8Egovp4Mi+zm?@B48Im`Xp#@K0pF~ z&VLnhmlIVI^9CI{kg9G>3h0{a_2+H6Gsc;NTBRSg6_xR}2C-;L$1yN4dLF1jT6%Z< z(eT#L#%w{L-BQ=z8Y^$q0>0lF3=;XJ0VKdqeA&gohjgtoKi=>y;6(;dwmg=f(fD5; zh4L3wKB1h_w_UYY_RZO^1&wL(>au~62gtnesD&~L2Ro=Y;Rx5B<>Pauoj!YS5V82j zWhcGCs_EMAd52anW@)xjl;zcM1Y;o3f^_OZ8kFmzfp_0)!N+8;4YNazKdpqL6twg^BrIa}7F_AaE;s6|M(^18p4 znTTjc|HSP`Hn<`NMMH}Eu~G0Fu44*EU&$4ROa{>&xeg>C4MtJ}T*A~19rVsW|=OVMF>;>buR2|qdb zk2Z!1(YLQ7@{@0}t&L6IG(&RmrnjJ=TV`I}oS~p}F|X@xaq=I^Z>H-!a4%PDl5Mp9#4WsC91!ue-jujeZ zjHZG4(7e`D2-U{b9rx&j5rof&PvxdqV=D+K@tjnWba$DkT zvpczv^q%nmo(*3+SVuK_k`YMF&2bHn1$CGSX2I(R(PXi)@fLJKO1%C>jY#>OJVw1Z zdGu1&;=&C0nIky3fKCG8d9QKyhFbEwvIjGp2Zg_2ZYdI-HEE(0^yFoTrqge?6OV}Q zqk>#1iZp{|7~Jr?=_JD|$7um-z@TrwSRnhD*=WUwQA3W&Sm{w}U#YfyEpi7dt0*@0 z#Dwt{tZW6%8|PFUs2gUzWz~5y^CL)%1(OC3sfcAAYG-4WbwLN@{c@eELYfN=gE7JAN{Z&`MWcI z7vW0>y;(6VWixzFi~_g=AaL7!GNOUJc>^nA4D6FF`1oznqUo>d*WrZJJgc7QqEG?B zES;^Qp5JkugcEb24OEsPW^RFKQrCF$!LZ4+RpjVlY%Yu8znj99Z!5~mx`Dz)IcEHy z^@23;z}7z(>eRjX z`1+b-8;$U7@?h>|dm3odwt0)56n{1>lw%d!6NnTK0Z}ITAud|5hUBnYZ2~O;Xv{#* z_`UhNX*3%Y3#Ul`RijP|%<=3Gnm4ym7z(H$xR}T*F!Y!~IaW7NMz0%?*lH8jiXC}A z+}F5?jD^nuhwWof`pVj-{8GLZWA|>#y7MFZYOW9pQJiGj=#Zob?Jrww`j0#$8q4>} zl_QS1N9c$jBk|_%2HiuG(P1!pVVPJE)A(<>c^{83m3yT;KHT*HG`2K05v)}UZ zYS49VU4&X#{UN@{q0;k64T8Ot|O1E+O;BupdY7Bz* z5!T}&l^0J_N-wM!I$FVrykxo4%TQ%?8uQ47EAI?QhMb_VT4jXPkA0bktLawk;>@_|8Niu+_}Ml8=i0|WvIk!oMw+W z#frWz0V?HQ8q$ffkTFgOl}c{dP4E`7IYqlwoZ)%lm^Ck^mNIDHohBJQTkNl_6n(jm z3}<6jFzBPenS27l`;acNgs{833puQv101#_y!yq95~N8Rl4=vZ_1F_f>}2xk#Jmn! z=^dO`!fyZ}QG999V+06Oyk3Pj%De#kt+cJ%OF#A-V+l49=PeLT* zW-|!i6jov9B_$HX5x+!o;7Peo`-1^=-%WV4DIRD^fIWRMsE9*@Yyhhw9zrjbK!xL_ zcxAajK#@&`wA~wZIA3jo%r^V{qxd4Q-foNQ-jtl5UfvsNU2NUv=rNL4Aum2`w!MLg3T4xo(KE1Hl+83+ zjm<_+UnEi3yaVHX zA}5t*3l{5VpQ2TpA2X$9+M<}YMvO(kF@mmX!kJ(i=R=Vl1h96VHfp*5lz*YhFH2@x z9v0#4qIi@-K0}9g-PxluZ@7{EXFx1SvE5}0z5!!XTb+JLF<0VMRSJKT@uNel7OF%m zo!}`?jw|OL+Kuh5`N1j!=F&Y-HtudW&0&{~vhz(m1-Y=L;fJ2`&>^(;@i*`oeR8=! zL8?+1ho?h)#Uyz6 z^M1@%fpx)fwxe<(=X@gZY1fqgESO%H-x({BX54U~t#{gj_s(+XOH+`T;OL))vX1h< zyGyn}rW}!(1T#VT$dn_{*t-aJ&J_KAMRX~!pXfPx!eCP@2nQrAaRk!-03Y+?9sM|r zN=V}VO|yC?@+32?Oj4K=PyL;Cn@E`{I-866my8UOhV)V{l1$!Z{&qwPC=a#MoHvm| zDq6bD6C@=V4~OzjT#7!}lS2O45eGjGrBrqr~ST1hBvK5?!(&60{E1@7XF8OB2*xh!#&E3-hU2A@==NI5ljgwCVP2vbmf zR(xN~Z>~7)!zaV6hLWBzbY_T(BBR1di_9JjQvLjW_*892V8RT2;saSRhai!gNyv6)t0!$U(JJcj}=Z7pX75e@qeO}u>rP>nZ3@!gYy&3dP zN$cyaNHS;5rsCAo|P|M}e%P6^LuMZxPr%Qbb4X zn2d2Sx(ILbeTzSoc4R%_u@2;2n1lkR$x1w#oWURs3i}1}Hu&4ZOq&skX&d%T#t{na zZhbH=Y)vXq+Txl3w=M_K zkBY+NFqSiJe@(7y$FuY^%==Vcr}#(CgO>{v&`b4G-`R+mJ=9B2P!8P-rOXNhed+Xc`-F0C0JUMKTQc_s^5LUuq$nIWQS=(<>7#98OB#*KwiIOU4 z-!np~WMR$Pl$9AHSdk9Ma|qGJx>Zb}a^FSWFg&Ti$QD+F-#ghZ$X1gmC^zwjbda-a z@^~c!BvE6dqXXyN{DrT3Vc$yxvBdpuXHRP^g-bAcXdDFwyW7f3DM?gPLEX?vkjQsA zFc~#Bn~+7=S>%6Y^u!0`98YjE*C67()bSVsZ_W{rhL~3cqm_Mp_}!g{t}L9m;lpS~>&G(iE~`5*xOXqGPiKbBD?6i4y=aUz4v*0BRMB z(Ky*sonnQO0(l;5bHknv3uToKf}(;;_Jb%hQa)M$;aUA;f>!~d3L)fNbdPz#pieH8 zYoh5dJ0OX!qO5^R@>EQfP~dI4|4372_+a9mNG9#&E7yp5&s;HNKm!|%umSP z?1!USJFf2lUuBouo>z&1%^ni4r|tFJHj-i#i;jR20(Qr?=97tPN}!Zb&f~!w4Ua`3 zmXipmOGGpsS%Hy0MZ+wy7`F<6VOn^O(?Pf>Z|QNLIC5uOie+24%1hs583Ht8-Pe+7 zss1o9IcqD1sgnNay%T37K|g;7w!JU0(+xEQ{Rg2iogIvF0t}_ihBBxA7jJT2Z!A%@ zZMPvlA%D&TO3z%{wrq+X!Mvx)v?NY8zfHGCB^+Khbn508$7LZH_E2^6xdoX_P_(Xh zK9#oXXH(-^h|w?NjyE!oPcBV*S}k7ez!(>`hL0NMTEPI$$Up=(c+zDtp5cyVzlGdY z2U&AK)YRmH3QLl_`I2~euivT`@-plel^dqVLwzxS9$J=kVQ91>XxZJS{%A_FtL*9I zZ~=Nnt`JF?J*Z1*--#8FCv)-X0x6u&75;DpV_kwQG$)`@ zzyUcVZ2>W3nJ$EWO+q6kio@pKoLWemy`8xr#|oj02D8`)&8ga#>)B9%%HXL*KBN|LT zea9P;&&{1cxHS$jp423q8Tz2P_k}|<QMFNCGnk%z6)Pq(ic65g0hyNC+|?}g1USV ztS54}|Dehl^YCuH1vVu}83*6`3V&*7`k=A+tEPR_n9nSr?+EMaW-^v{06>q?TAP0{ z9&%C;=XEDlapPjkOCh82Sy~^Sy&y{=-cvIND~@rMN8U_ z4d;Pz4e_@IrwpNDVC)3s<(&Lq&GG#C|IVc<77x1vBQE;`--*5;Yojutq|lOZiwX{9 zNZu_x`a25G&kEIUp#6z*X-kL$$NcLtGxaOCc579+cz7nWGCN+Y_zx1TCKnoDFpSQx z@%GIarfT0?hY^sMr~^75QhsY4@3DkU5^_-tVc(4T=6O5hUfYT-qRW>CFwUhZFrhhY z5o4bTXP)2w`U`SF1tYde}jl~pSXtj?{!hB;z z`R+Zu@AxYr_5667`sDfwQUl1g2`x31INSb7WdGhE571LZsEtD-g^glVlJCQqz8U07 zyxV0{jSH7G_x!_JNzQo0MLM=VlcD;8hk*7ctonP&*{{W4;2m4e5d|+2Ok>mz4?Jy=h-%wbrtTy^Tk2zyd7+8)tTnn#3zT*jNsyvKQ4 z(mC_iE1v?tpn%r$)2BiTntgag(aze+OkgBk%gR~-4*o#hMSl1|VW}0fqAd;MPRHc; zk2Myf8FjpUy+NZEVjn*u0c!q0mm<8#Ci^~F(C|@22imky^Cf-BAim8f_|hpjIrX^F zgF$GvtL=^pUpE^Y#I2e~j!jKCTBfANq!(4sb@aX0cH+mF7i|DzKuAUlXb~)69^OH^!y1{-~X_j`@3t07)$yAArm`d@j{u{W2mN_a(z>mcfiVJbZAgn8p#8lY|QdcLa;~Bzzz05w@ZBe3n z>=Tr|)TjK<5XXU01v{W!4l>ZQ0fby+Rls%G-Kb-LYTfvsU-rb5?Qk6q#Stq?7|J;8 zo1{IBmT;ob$52_EA6AZ5Q5Y)$zsODG8(urW6MO;JEUL0J?;E8tx{VZ!`kW@{xE)Zss)wtPwqGR!(TI zMc9u;DH%02074E=Ng-&|s|@))4-}l0Dpn1Dh-Q$O2_BM-$`7bV$jJMUR5O+q2qeGl zP@>@f3+GCLR<;tdfOg1cDGg7w$7|#aa876@`6b~!@c~!Vq5(VApM}58>BZB?%hs5U zc~>tT=M~vWrp4l4zZ^dh5LrPIy+L=pVyeEW?fs>5sC7Y&eNaO3SNRFulKQ z$@8X+&9b(@G3vlYMCdsCx7Zwr|3055W{VCzhLWg3S{#ZZnq(}G=LaLgWGj-^r&2vb zmo^kxQ&(4ahEi8oBjbT1dD|6XKKJCDwEXz|#b?%7@^kx}E3JWx%G!=x zdinZ_f|kachVmwtX2Kq-5t-E+wwZhWUZ&JKXq ziT{Sr+xAMHs`Iyom|BbPh6pbP+t=F7PkuMHINjnwJQRXQEiO}KRP`A@)Grj3X({`; ztQ-e?oOy~n3Ib7J+6d;B%^y8qZOt9kyP6kqarEs!dlz7N;x!felP19Qbk^&<1yt46 zc_fj%1-rh_Kh;zcZ5QWZQG`jBU7FO2rWb50_PiGz8m^vsH=l+rTc7WCf4_`D%xmA= z?`+K&xgEa$_k$#!#D%}1Gl{APDy?wi4>d;Xcj;0|U2M=N z;Q9?c&gB8^RlO!6Fd;BuzOn32#`ru45#Mv$!pM<@fq}5FK`^dm7+!={}0puJzxL; literal 0 HcmV?d00001 diff --git a/src/SoleModels.jl b/src/SoleModels.jl index e9936df..e064dc6 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -11,6 +11,7 @@ using SoleLogics: ⊤, ¬, ∧ using FunctionWrappers: FunctionWrapper using StatsBase using ThreadSafeDicts +using Lazy include("utils.jl") @@ -54,19 +55,19 @@ export minify, isminifiable include("utils/minify.jl") export AbstractFeature, - NamedFeature, ExternalFWDFeature + Feature, ExternalFWDFeature export propositions -export computefeature, parsecondition -export frames +export parsecondition +export modalities -# Definitions for logical datasets (i.e., logisets) -include("datasets/base/main.jl") +export Logiset, SupportedLogiset, slicedataset, concatdatasets -# include("datasets/scalar-datasets/main.jl") +# Definitions for logical datasets (i.e., logisets) +include("logisets/base/main.jl") -# export ninstances, nframes, frames, nfeatures +# export ninstances, nmodalities, modalities, nfeatures # export get_ontology, # get_interval_ontology @@ -79,11 +80,11 @@ include("datasets/base/main.jl") # UnivariateSoftMin, UnivariateSoftMax, # MultivariateFeature -# export DimensionalFeature, AbstractUnivariateFeature, +# export VarFeature, AbstractUnivariateFeature, # UnivariateNamedFeature, # UnivariateFeature -# include("datasets/dimensional-datasets/main.jl") +# include("logisets/dimensional-logisets/main.jl") # using .DimensionalDatasets: parsecondition diff --git a/src/datasets/base/check.jl b/src/datasets/base/check.jl deleted file mode 100644 index e1eb991..0000000 --- a/src/datasets/base/check.jl +++ /dev/null @@ -1,160 +0,0 @@ - - -@inline function check( - p::Proposition{<:ScalarCondition}, - X::AbstractScalarLogiset{W}, - i_instance::Integer, - w::W, -) where {W<:AbstractWorld} - c = atom(p) - apply_test_operator(SoleModels.test_operator(c), X[i_instance, w, SoleModels.feature(c)], SoleModels.threshold(c)) -end - - -# TODO fix: SyntaxTree{A} and SyntaxTree{B} are different, but they should not be. -# getindex(::AbstractDictionary{I, T}, ::I) --> T -# keys(::AbstractDictionary{I, T}) --> AbstractIndices{I} -# isassigned(::AbstractDictionary{I, T}, ::I) --> Bool -# TODO fix? -# hasformula(memo_structure::AbstractDict{F}, φ::SyntaxTree) where {F<:AbstractFormula} = haskey(memo_structure, SoleLogics.tree(φ)) -hasformula(memo_structure::AbstractDict{F}, φ::AbstractFormula) where {F<:AbstractFormula} = haskey(memo_structure, φ) -hasformula(memo_structure::AbstractDict{SyntaxTree}, φ::AbstractFormula) = haskey(memo_structure, SoleLogics.tree(φ)) - -function check( - φ::SoleLogics.AbstractFormula, - X::AbstractLogiset{W,<:AbstractFeature,<:Number,FR}, - i_instance::Integer; - initialworld::Union{Nothing,W,AbstractVector{<:W}} = SoleLogics.initialworld(X, i_instance), - # use_memo::Union{Nothing,AbstractVector{<:AbstractDict{<:F,<:T}}} = nothing, - # use_memo::Union{Nothing,AbstractVector{<:AbstractDict{<:F,<:WorldSet{W}}}} = nothing, - use_memo::Union{Nothing,AbstractVector{<:AbstractDict{<:F,<:WorldSet}}} = nothing, - # memo_max_height = Inf, -) where {W<:AbstractWorld,T<:Bool,FR<:AbstractMultiModalFrame{W,T},F<:SoleLogics.AbstractFormula} - - @assert SoleLogics.isglobal(φ) || !isnothing(initialworld) "Cannot check non-global formula with no initialworld(s): $(syntaxstring(φ))." - - memo_structure = begin - if isnothing(use_memo) - ThreadSafeDict{SyntaxTree,WorldSet{W}}() - else - use_memo[i_instance] - end - end - - # forget_list = Vector{SoleLogics.FNode}() - # hasmemo(::AbstractLogiset) = false - # hasmemo(X)TODO - - # φ = normalize(φ; profile = :modelchecking) # TODO normalize formula and/or use a dedicate memoization structure that normalizes functions - - fr = frame(X, i_instance) - - # TODO avoid using when memo is nothing - if !hasformula(memo_structure, φ) - for ψ in unique(SoleLogics.subformulas(φ)) - # @show ψ - # @show syntaxstring(ψ) - # if height(ψ) > memo_max_height - # push!(forget_list, ψ) - # end - if !hasformula(memo_structure, ψ) - tok = token(ψ) - memo_structure[ψ] = begin - if tok isa SoleLogics.AbstractOperator - collect(SoleLogics.collateworlds(fr, tok, map(f->memo_structure[f], children(ψ)))) - elseif tok isa Proposition - filter(w->check(tok, X, i_instance, w), collect(allworlds(fr))) - else - error("Unexpected token encountered in _check: $(typeof(tok))") - end - end - end - # @show syntaxstring(ψ), memo_structure[ψ] - end - end - - # # All the worlds where a given formula is valid are returned. - # # Then, internally, memoization-regulation is applied - # # to forget some formula thus freeing space. - # fcollection = deepcopy(memo(X)) - # for h in forget_list - # k = fhash(h) - # if hasformula(memo_structure, k) - # empty!(memo(X, k)) # Collection at memo(X)[k] is erased - # pop!(memo(X), k) # Key k is deallocated too - # end - # end - - ret = begin - if isnothing(initialworld) - length(memo_structure[φ]) > 0 - else - initialworld in memo_structure[φ] - end - end - - return ret -end - -############################################################################################ - -# function compute_chained_threshold( -# φ::SoleLogics.AbstractFormula, -# X::SupportedScalarLogiset{V,W,FR}, -# i_instance; -# use_memo::Union{Nothing,AbstractVector{<:AbstractDict{F,T}}} = nothing, -# ) where {V<:Number,W<:AbstractWorld,T<:Bool,FR<:AbstractMultiModalFrame{W,T},F<:SoleLogics.AbstractFormula} - -# @assert SoleLogics.isglobal(φ) "TODO expand code to specifying a world, defaulted to an initialworld. Cannot check non-global formula: $(syntaxstring(φ))." - -# memo_structure = begin -# if isnothing(use_memo) -# ThreadSafeDict{SyntaxTree,V}() -# else -# use_memo[i_instance] -# end -# end - -# # φ = normalize(φ; profile = :modelchecking) # TODO normalize formula and/or use a dedicate memoization structure that normalizes functions - -# fr = frame(X, i_instance) - -# if !hasformula(memo_structure, φ) -# for ψ in unique(SoleLogics.subformulas(φ)) -# if !hasformula(memo_structure, ψ) -# tok = token(ψ) -# memo_structure[ψ] = begin -# if tok isa AbstractRelationalOperator && length(children(φ)) == 1 && height(φ) == 1 -# featcond = atom(token(children(φ)[1])) -# if tok isa DiamondRelationalOperator -# # (L) f > a <-> max(acc) > a -# onestep_accessible_aggregation(X, i_instance, w, relation(tok), feature(featcond), existential_aggregator(test_operator(featcond))) -# elseif tok isa BoxRelationalOperator -# # [L] f > a <-> min(acc) > a <-> ! (min(acc) <= a) <-> ¬ (f <= a) -# onestep_accessible_aggregation(X, i_instance, w, relation(tok), feature(featcond), universal_aggregator(test_operator(featcond))) -# else -# error("Unexpected operator encountered in onestep_collateworlds: $(typeof(tok))") -# end -# else -# TODO -# end -# end -# end -# # @show syntaxstring(ψ), memo_structure[ψ] -# end -# end - -# # # All the worlds where a given formula is valid are returned. -# # # Then, internally, memoization-regulation is applied -# # # to forget some formula thus freeing space. -# # fcollection = deepcopy(memo(X)) -# # for h in forget_list -# # k = fhash(h) -# # if hasformula(memo_structure, k) -# # empty!(memo(X, k)) # Collection at memo(X)[k] is erased -# # pop!(memo(X), k) # Key k is deallocated too -# # end -# # end - -# return memo_structure[φ] -# end diff --git a/src/datasets/base/conditions.jl b/src/datasets/base/conditions.jl deleted file mode 100644 index 20f3388..0000000 --- a/src/datasets/base/conditions.jl +++ /dev/null @@ -1,42 +0,0 @@ - -using SoleLogics: AbstractAlphabet -using Random -import SoleLogics: negation, propositions - -import Base: isequal, hash, in, isfinite, length - -""" - abstract type AbstractCondition{F<:AbstractFeature} end - -Abstract type for representing conditions that can be interpreted and evaluated -on worlds of instances of a logical dataset. In logical contexts, -these are wrapped into `Proposition`s. - -See also -[`Proposition`](@ref), -[`syntaxstring`](@ref), -[`ScalarMetaCondition`](@ref), -[`ScalarCondition`](@ref). -""" -abstract type AbstractCondition{F<:AbstractFeature} end - -function syntaxstring(c::AbstractCondition; kwargs...) - error("Please, provide method syntaxstring(::$(typeof(c)); kwargs...)." * - " Note that this value must be unique.") -end - -function Base.show(io::IO, c::AbstractCondition) - # print(io, "Feature of type $(typeof(c))\n\t-> $(syntaxstring(c))") - print(io, "$(typeof(c)): $(syntaxstring(c))") - # print(io, "$(syntaxstring(c))") -end - -Base.isequal(a::AbstractCondition, b::AbstractCondition) = syntaxstring(a) == syntaxstring(b) # nameof(x) == nameof(feature) -Base.hash(a::AbstractCondition) = Base.hash(syntaxstring(a)) - -function parsecondition( - expression::String; - kwargs... -) - error("Please, provide method parsecondition(expression::$(typeof(expression)); kwargs...).") -end diff --git a/src/datasets/base/features.jl b/src/datasets/base/features.jl deleted file mode 100644 index 4e2a260..0000000 --- a/src/datasets/base/features.jl +++ /dev/null @@ -1,113 +0,0 @@ -import Base: isequal, hash, show -import SoleLogics: syntaxstring - -""" - abstract type AbstractFeature{U} end - -Abstract type for features of worlds of -[Kripke structures](https://en.wikipedia.org/wiki/Kripke_structure_(model_checking). - -See also [`featvaltype`](@ref), [`computefeature`](@ref), [`AbstractWorld`](@ref). -""" -abstract type AbstractFeature{U} end - -""" - featvaltype(::Type{<:AbstractFeature{U}}) where {U} = U - featvaltype(::AbstractFeature{U}) where {U} = U - -Return the type returned by the feature. - -See also [`AbstractWorld`](@ref). -""" -featvaltype(::Type{<:AbstractFeature{U}}) where {U} = U -featvaltype(::AbstractFeature{U}) where {U} = U - -""" - computefeature(f::AbstractFeature{U}, channel; kwargs...)::U where {U} - -Compute a feature on a channel (i.e., world reading) of an instance. - -See also [`AbstractFeature`](@ref). -""" -function computefeature(f::AbstractFeature{U}, channel; kwargs...) where {U} - error("Please, provide method computefeature(::$(typeof(f)), channel::$(typeof(channel)); kwargs...)::U.") -end - -function syntaxstring(f::AbstractFeature; kwargs...) - error("Please, provide method syntaxstring(::$(typeof(f)); kwargs...)." - * " Note that this value must be unique.") -end - -@inline (f::AbstractFeature)(args...) = computefeature(f, args...) - -function Base.show(io::IO, f::AbstractFeature) - # print(io, "Feature of type $(typeof(f))\n\t-> $(syntaxstring(f))") - print(io, "$(typeof(f)): $(syntaxstring(f))") - # print(io, "$(syntaxstring(f))") -end - -Base.isequal(a::AbstractFeature, b::AbstractFeature) = syntaxstring(a) == syntaxstring(b) -Base.hash(a::AbstractFeature) = Base.hash(syntaxstring(a)) - -############################################################################################ - -""" - struct PropositionFeature{U} <: AbstractFeature{U} - name::String - end - -A feature that identifies a propositional letter. - -See also [`AbstractFeature`](@ref). -""" -struct PropositionFeature{U<:Proposition} <: AbstractFeature{U} - proposition::U -end -function computefeature(f::PropositionFeature{U}, channel; kwargs...) where {U} - error("Please, provide method computefeature(::$(typeof(f)), channel::$(typeof(channel)); kwargs...)::U.") -end - -featurename(f::PropositionFeature) = string(f.proposition) - -############################################################################################ - -""" - struct NamedFeature{U} <: AbstractFeature{U} - name::String - end - -A feature solely identified by its name. - -See also [`AbstractFeature`](@ref). -""" -struct NamedFeature{U} <: AbstractFeature{U} - name::String -end -function computefeature(f::NamedFeature, channel) - @error "Can't intepret NamedFeature on any structure at all." -end -featurename(f::NamedFeature) = f.name - -############################################################################################ - -""" - struct ExternalFWDFeature{U} <: AbstractFeature{U} - name::String - fwd::Any - end - -A feature encoded explicitly as (a slice of) an FWD structure (see `AbstractFeatureLookupSet`). - -See also -[`AbstractFeatureLookupSet`](@ref), [`AbstractFeature`](@ref). -""" -struct ExternalFWDFeature{U} <: AbstractFeature{U} - name::String - fwd::Any -end -function computefeature(f::ExternalFWDFeature, channel) - @error "Can't intepret ExternalFWDFeature on any structure at all." -end -featurename(f::ExternalFWDFeature) = f.name - -############################################################################################ diff --git a/src/datasets/base/generic-supporting-datasets.jl b/src/datasets/base/generic-supporting-datasets.jl deleted file mode 100644 index 1b5c4e7..0000000 --- a/src/datasets/base/generic-supporting-datasets.jl +++ /dev/null @@ -1,81 +0,0 @@ - -struct GenericSupportingDataset{ - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - M<:AbstractVector{<:AbstractDict{<:AbstractFormula,Vector{W}}}, -} <: SupportingDataset{W,FR} - - memo::M - - function GenericSupportingDataset{W,FR,M}( - memo :: M, - ) where {W<:AbstractWorld,FR<:AbstractFrame{W,Bool},M<:AbstractVector{<:AbstractDict{<:AbstractFormula,Vector{W}}}} - new{W,FR,M}(memo) - end - - function GenericSupportingDataset( - fd :: Logiset{V,W,FR}, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, ninstances(fd)) - for i_instance in 1:ninstances(fd) - memo[i_instance] = ThreadSafeDict{AbstractFormula,Vector{W}}() - end - GenericSupportingDataset{W,FR,typeof(memo)}(memo) - end -end - -usesmemo(X::GenericSupportingDataset) = true - -Base.size(X::GenericSupportingDataset) = () -capacity(X::GenericSupportingDataset) = Inf -nmemoizedvalues(X::GenericSupportingDataset) = sum(length.(X.d)) - -function _slice_dataset(X::GFSD, inds::AbstractVector{<:Integer}, args...; kwargs...) where {GFSD<:GenericSupportingDataset} - GFSD(X.w0, _slice_dataset(X.memo[inds], args...; kwargs...)) -end - -hasnans(X::GenericSupportingDataset) = false # TODO double check that this is intended - -isminifiable(X::GenericSupportingDataset) = false # TODO double check that this is intended - -struct ChainedFeaturedSupportingDataset{ - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - M<:AbstractVector{<:AbstractDict{<:AbstractFormula,V}}, -} <: SupportingDataset{W,FR} - - w0::W - - memo::M - - function ChainedFeaturedSupportingDataset{V,W,FR,M}( - memo :: M, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},M<:AbstractVector{<:AbstractDict{<:AbstractFormula,V}}} - new{V,W,FR,M}(memo) - end - - function ChainedFeaturedSupportingDataset( - fd :: Logiset{V,W,FR}, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, ninstances(fd)) - for i_instance in 1:ninstances(fd) - memo[i_instance] = ThreadSafeDict{AbstractFormula,Vector{W}}() - end - ChainedFeaturedSupportingDataset{V,W,FR,typeof(memo)}(memo) - end -end - -usesmemo(X::ChainedFeaturedSupportingDataset) = true - -Base.size(X::ChainedFeaturedSupportingDataset) = () -capacity(X::ChainedFeaturedSupportingDataset) = Inf -nmemoizedvalues(X::ChainedFeaturedSupportingDataset) = sum(length.(X.d)) - -function _slice_dataset(X::CFSD, inds::AbstractVector{<:Integer}, args...; kwargs...) where {CFSD<:ChainedFeaturedSupportingDataset} - CFSD(X.w0, _slice_dataset(X.memo[inds], args...; kwargs...)) -end - -hasnans(X::ChainedFeaturedSupportingDataset) = false # TODO double check that this is intended - -isminifiable(X::ChainedFeaturedSupportingDataset) = false # TODO double check that this is intended diff --git a/src/datasets/base/generic-supports.jl b/src/datasets/base/generic-supports.jl deleted file mode 100644 index dbabb3f..0000000 --- a/src/datasets/base/generic-supports.jl +++ /dev/null @@ -1,108 +0,0 @@ - -############################################################################################ -############################################################################################ -############################################################################################ - -struct GenericRelationalSupport{ - V, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - D<:AbstractArray{Dict{W,VV}, 3} where VV<:Union{V,Nothing}, -} <: AbstractRelationalSupport{V,W,FR} - - d :: D - - function GenericRelationalSupport{V,W,FR}(d::D) where {V,W<:AbstractArray,FR<:AbstractFrame{W,Bool},D<:AbstractArray{V,2}} - new{V,W,FR,D}(d) - end - - function GenericRelationalSupport(fd::Logiset{V,W,FR}, perform_initialization = false) where {V,W,FR<:AbstractFrame{W,Bool}} - _nfeatsnaggrs = nfeatsnaggrs(fd) - _fwd_rs = begin - if perform_initialization - _fwd_rs = Array{Dict{W,Union{V,Nothing}}, 3}(undef, ninstances(fd), _nfeatsnaggrs, nrelations(fd)) - fill!(_fwd_rs, nothing) - else - Array{Dict{W,V}, 3}(undef, ninstances(fd), _nfeatsnaggrs, nrelations(fd)) - end - end - GenericRelationalSupport{V,W,FR}(_fwd_rs) - end -end - -# default_fwd_rs_type(::Type{<:AbstractWorld}) = GenericRelationalSupport # TODO implement similar pattern used for fwd - -function hasnans(support::GenericRelationalSupport) - # @show any(map(d->(any(_isnan.(collect(values(d))))), support.d)) - any(map(d->(any(_isnan.(collect(values(d))))), support.d)) -end - -ninstances(support::GenericRelationalSupport) = size(support, 1) -nfeatsnaggrs(support::GenericRelationalSupport) = size(support, 2) -nrelations(support::GenericRelationalSupport) = size(support, 3) -capacity(support::GenericRelationalSupport) = Inf -nmemoizedvalues(support::GenericRelationalSupport) = sum(length.(support.d)) - -@inline function Base.getindex( - support :: GenericRelationalSupport{V,W}, - i_instance :: Integer, - w :: W, - i_featsnaggr :: Integer, - i_relation :: Integer -) where {V,W<:AbstractWorld} - support.d[i_instance, i_featsnaggr, i_relation][w] -end -Base.size(support::GenericRelationalSupport, args...) = size(support.d, args...) - -fwd_rs_init_world_slice(support::GenericRelationalSupport{V,W}, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) where {V,W} = - support.d[i_instance, i_featsnaggr, i_relation] = Dict{W,V}() -@inline function Base.setindex!(support::GenericRelationalSupport{V,W}, threshold::V, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {V,W} - support.d[i_instance, i_featsnaggr, i_relation][w] = threshold -end -function _slice_dataset(support::GenericRelationalSupport{V,W,FR}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {V,W,FR} - GenericRelationalSupport{V,W,FR}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) -end - -############################################################################################ - -# Note: the global support is world-agnostic -struct GenericGlobalSupport{V,D<:AbstractArray{V,2}} <: AbstractGlobalSupport{V} - d :: D - - function GenericGlobalSupport{V,D}(d::D) where {V,D<:AbstractArray{V,2}} - new{V,D}(d) - end - function GenericGlobalSupport{V}(d::D) where {V,D<:AbstractArray{V,2}} - GenericGlobalSupport{V,D}(d) - end - - function GenericGlobalSupport(fd::Logiset{V}) where {V} - @assert worldtype(fd) != OneWorld "TODO adjust this note: note that you should not use a global support when not using global decisions" - _nfeatsnaggrs = nfeatsnaggrs(fd) - GenericGlobalSupport{V}(Array{V,2}(undef, ninstances(fd), _nfeatsnaggrs)) - end -end - -capacity(support::GenericGlobalSupport) = prod(size(support.d)) -nmemoizedvalues(support::GenericGlobalSupport) = sum(support.d) - -# default_fwd_gs_type(::Type{<:AbstractWorld}) = GenericGlobalSupport # TODO implement similar pattern used for fwd - -function hasnans(support::GenericGlobalSupport) - # @show any(_isnan.(support.d)) - any(_isnan.(support.d)) -end - -ninstances(support::GenericGlobalSupport) = size(support, 1) -nfeatsnaggrs(support::GenericGlobalSupport) = size(support, 2) -Base.getindex( - support :: GenericGlobalSupport, - i_instance :: Integer, - i_featsnaggr :: Integer) = support.d[i_instance, i_featsnaggr] -Base.size(support::GenericGlobalSupport{V}, args...) where {V} = size(support.d, args...) - -Base.setindex!(support::GenericGlobalSupport{V}, threshold::V, i_instance::Integer, i_featsnaggr::Integer) where {V} = - support.d[i_instance, i_featsnaggr] = threshold -function _slice_dataset(support::GenericGlobalSupport{V}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {V} - GenericGlobalSupport{V}(if return_view == Val(true) @view support.d[inds,:] else support.d[inds,:] end) -end diff --git a/src/datasets/base/logiset-interface.jl b/src/datasets/base/logiset-interface.jl deleted file mode 100644 index f288e39..0000000 --- a/src/datasets/base/logiset-interface.jl +++ /dev/null @@ -1,123 +0,0 @@ -using SoleLogics: AbstractKripkeStructure, AbstractInterpretationSet, AbstractFrame -using SoleLogics: TruthValue -import SoleLogics: alphabet, frame, check -import SoleLogics: accessibles, allworlds, nworlds, initialworld -import SoleLogics: worldtype, frametype - -""" - abstract type AbstractLogiset{ - W<:AbstractWorld, - F<:AbstractFeature, - T<:TruthValue, - FR<:AbstractFrame{W,T}, - } <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{F},T,FR}} end - -Abstract type for logisets, that is, logical datasets for -symbolic learning where each instance is a -[Kripke structure](https://en.wikipedia.org/wiki/Kripke_structure_(model_checking)) -on features. Conditions (see [`AbstractCondition`](@ref)), and logical formulas -with conditional letters can be checked on (worlds of) instances of the dataset. - -Logisets have an associated alphabet, set of features and set of relations. - -See also -[`AbstractCondition`](@ref), -[`AbstractFeature`](@ref), -[`AbstractKripkeStructure`](@ref), -[`AbstractInterpretationSet`](@ref). -""" -abstract type AbstractLogiset{ - W<:AbstractWorld, - V, - F<:AbstractFeature{V}, - T<:TruthValue, - FR<:AbstractFrame{W,T}, -} <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:F},T,FR}} end - -function evaluatecondition( - X::AbstractLogiset{W,V,F,T}, - i_instance, - w::W, - c::AbstractCondition, -)::T where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue} - error("Please, provide method evaluatecondition(X::$(typeof(X)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), c::$(typeof(c))).") -end - -function check( - f::AbstractFormula, - X::AbstractLogiset{W,V,F,T}, - i_instance, - w::W, -)::T where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue} - # TODO implement once for all. - error("Please, provide method check(f::$(typeof(f)), X::$(typeof(X)), i_instance::$(typeof(i_instance)), w::$(typeof(w))).") -end - -function displaystructure(X::AbstractLogiset; kwargs...)::String - error("Please, provide method displaystructure(X::$(typeof(X)); kwargs...)::String.") -end - -function check( - p::Proposition{A}, - X::AbstractLogiset{W,V,F,T}, - i_instance, - w::W, -)::T where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue,A<:AbstractCondition} - cond = atom(p) - evaluatecondition(X, i_instance, w, cond) -end - -function Base.show(io::IO, X::AbstractLogiset; kwargs...) - println(io, displaystructure(X; kwargs...)) -end - -############################################################################################ - -featvaltype(::Type{<:AbstractLogiset{W,V}}) where {W<:AbstractWorld,V} = V -featvaltype(d::AbstractLogiset) = featvaltype(typeof(d)) - -featuretype(::Type{<:AbstractLogiset{W,V,F}}) where {W<:AbstractWorld,V,F<:AbstractFeature} = F -featuretype(d::AbstractLogiset) = featuretype(typeof(d)) - -function features(X::AbstractLogiset) - return error("Please, provide method features(::$(typeof(X))).") -end - -function featvalue( - X::AbstractLogiset{W}, - i_instance, - w::W, - f::AbstractFeature, -) where {W<:AbstractWorld} - error("Please, provide method featvalue(::$(typeof(X)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), f::$(typeof(f))).") -end - -isminifiable(::AbstractLogiset) = false - -function initialworld( - X::AbstractLogiset{W,V,F,T}, - i_instance -) where {W<:AbstractWorld,V,F<:AbstractFeature{V},T<:TruthValue} - error("Please, provide method initialworld(::$(typeof(X)), i_instance::$(typeof(i_instance))).") -end - -representatives(X::AbstractLogiset, i_instance, args...) = representatives(frame(X, i_instance), args...) - -############################################################################################ -# Helpers -############################################################################################ - -function findfeature(X::AbstractLogiset, feature::AbstractFeature) - id = findfirst(x->(Base.isequal(x, feature)), features(X)) - if isnothing(id) - error("Could not find feature $(feature) in AbstractLogiset of type $(typeof(X)).") - end - id -end -function findrelation(X::AbstractLogiset, relation::AbstractRelation) - id = findfirst(x->x==relation, relations(X)) - if isnothing(id) - error("Could not find relation $(relation) in AbstractLogiset of type $(typeof(X)).") - end - id -end diff --git a/src/datasets/base/main.jl b/src/datasets/base/main.jl deleted file mode 100644 index f611662..0000000 --- a/src/datasets/base/main.jl +++ /dev/null @@ -1,51 +0,0 @@ -import SoleLogics: frame - -using SoleLogics: OneWorld, Interval, Interval2D -using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame -using SoleLogics: X, Y, Z -using SoleLogics: AbstractWorld, IdentityRel -import SoleLogics: syntaxstring - -import SoleData: ninstances, nfeatures -import SoleData: hasnans, _slice_dataset - -# Features to be computed on worlds of dataset instances -include("features.jl") - -# Conditions on the features, to be wrapped in Proposition's -include("conditions.jl") - -export check, accessibles, allworlds, representatives, initialworld - -# Interface for representative accessibles, for optimized model checking on specific frames -include("representatives.jl") - -# Logical datasets, where the instances are Kripke models with conditional alphabets -include("logiset-interface.jl") - -# Logical dataset based on a lookup -include("logiset.jl") - -# include("supported-logiset.jl") - -# export nframes, frames, frame, -# displaystructure, -# MultiFrameLogiset, -# worldtypes - -# # Multiframe version of logisets, for representing multimodal datasets -# include("multiframe-logiset.jl") # TODO define interface - -# const ActiveMultiFrameLogiset = MultiFrameLogiset{<:AbstractLogiset} -# const ActiveMultiFrameScalarLogiset = MultiFrameScalarLogiset{<:AbstractActiveScalarLogiset} - - - -# include("generic-supporting-datasets.jl") -# include("generic-supports.jl") - - -# # -# # TODO figure out which convert function works best: -# convert(::Type{<:MultiFrameLogiset{T}}, X::MD) where {T,MD<:AbstractLogiset{T}} = MultiFrameLogiset{MD}([X]) -# convert(::Type{<:MultiFrameLogiset}, X::AbstractLogiset) = MultiFrameLogiset([X]) diff --git a/src/datasets/base/multiframe-logiset.jl b/src/datasets/base/multiframe-logiset.jl deleted file mode 100644 index 2fbc1b0..0000000 --- a/src/datasets/base/multiframe-logiset.jl +++ /dev/null @@ -1,94 +0,0 @@ -""" - struct MultiFrameLogiset{MD<:AbstractLogiset} - frames :: Vector{<:MD} - end - -A multiframe logical dataset; this structure is useful for representing -multimodal datasets in logical terms. - -See also -[`AbstractLogiset`](@ref), -[`minify`](@ref). -""" -struct MultiFrameLogiset{MD<:AbstractLogiset} - - frames :: Vector{<:MD} - - function MultiFrameLogiset{MD}(X::MultiFrameLogiset{MD}) where {MD<:AbstractLogiset} - MultiFrameLogiset{MD}(X.frames) - end - function MultiFrameLogiset{MD}(Xs::AbstractVector) where {MD<:AbstractLogiset} - Xs = collect(Xs) - @assert length(Xs) > 0 && length(unique(ninstances.(Xs))) == 1 "Can't create an empty MultiFrameLogiset or with mismatching number of instances (nframes: $(length(Xs)), frame_sizes: $(ninstances.(Xs)))." - new{MD}(Xs) - end - function MultiFrameLogiset{MD}() where {MD<:AbstractLogiset} - new{MD}(MD[]) - end - function MultiFrameLogiset{MD}(X::MD) where {MD<:AbstractLogiset} - MultiFrameLogiset{MD}(MD[X]) - end - function MultiFrameLogiset(Xs::AbstractVector{<:MD}) where {MD<:AbstractLogiset} - MultiFrameLogiset{MD}(Xs) - end - function MultiFrameLogiset(X::MD) where {MD<:AbstractLogiset} - MultiFrameLogiset{MD}(X) - end -end - -frames(X::MultiFrameLogiset) = X.frames - -Base.iterate(X::MultiFrameLogiset, state=1) = state > length(X) ? nothing : (get_instance(X, state), state+1) -Base.length(X::MultiFrameLogiset) = ninstances(X) -Base.push!(X::MultiFrameLogiset, f::AbstractLogiset) = push!(frames(X), f) - -Base.size(X::MultiFrameLogiset) = map(size, frames(X)) - -frame(X::MultiFrameLogiset, i_frame::Integer) = nframes(X) > 0 ? frames(X)[i_frame] : error("MultiFrameLogiset has no frame!") -nframes(X::MultiFrameLogiset) = length(frames(X)) -ninstances(X::MultiFrameLogiset) = ninstances(frame(X, 1)) - -# max_channel_size(X::MultiFrameLogiset) = map(max_channel_size, frames(X)) # TODO: figure if this is useless or not. Note: channel_size doesn't make sense at this point. -nfeatures(X::MultiFrameLogiset) = map(nfeatures, frames(X)) # Note: used for safety checks in fit_tree.jl -# nrelations(X::MultiFrameLogiset) = map(nrelations, frames(X)) # TODO: figure if this is useless or not -nfeatures(X::MultiFrameLogiset, i_frame::Integer) = nfeatures(frame(X, i_frame)) -nrelations(X::MultiFrameLogiset, i_frame::Integer) = nrelations(frame(X, i_frame)) -worldtype(X::MultiFrameLogiset, i_frame::Integer) = worldtype(frame(X, i_frame)) -worldtypes(X::MultiFrameLogiset) = Vector{Type{<:AbstractWorld}}(worldtype.(frames(X))) - -get_instance(X::MultiFrameLogiset, i_frame::Integer, idx_i::Integer, args...) = get_instance(frame(X, i_frame), idx_i, args...) -# get_instance(X::MultiFrameLogiset, idx_i::Integer, args...) = get_instance(frame(X, i), idx_i, args...) # TODO should slice across the frames! - -_slice_dataset(X::MultiFrameLogiset{MD}, inds::AbstractVector{<:Integer}, args...; kwargs...) where {MD<:AbstractLogiset} = - MultiFrameLogiset{MD}(Vector{MD}(map(frame->_slice_dataset(frame, inds, args...; kwargs...), frames(X)))) - -function displaystructure(Xs::MultiFrameLogiset; indent_str = "") - out = "$(typeof(Xs))" # * "\t\t\t$(Base.summarysize(Xs) / 1024 / 1024 |> x->round(x, digits=2)) MBs" - for (i_frame, X) in enumerate(frames(Xs)) - if i_frame == nframes(Xs) - out *= "\n$(indent_str)└ " - else - out *= "\n$(indent_str)├ " - end - out *= "[$(i_frame)] " - # \t\t\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t(worldtype: $(worldtype(X)))" - out *= displaystructure(X; indent_str = indent_str * (i_frame == nframes(Xs) ? " " : "│ ")) * "\n" - end - out -end - -hasnans(Xs::MultiFrameLogiset) = any(hasnans.(frames(Xs))) - -isminifiable(::MultiFrameLogiset) = true - -function minify(Xs::MultiFrameLogiset) - if !any(map(isminifiable, frames(Xs))) - if !all(map(isminifiable, frames(Xs))) - @error "Cannot perform minification with frames of types $(typeof.(frames(Xs))). Please use a minifiable format (e.g., SupportedScalarLogiset)." - else - @warn "Cannot perform minification on some of the frames provided. Please use a minifiable format (e.g., SupportedScalarLogiset) ($(typeof.(frames(Xs))) were used instead)." - end - end - Xs, backmap = zip([!isminifiable(X) ? minify(X) : (X, identity) for X in frames(Xs)]...) - Xs, backmap -end diff --git a/src/datasets/base/supported-logiset.jl b/src/datasets/base/supported-logiset.jl deleted file mode 100644 index 1e8a07f..0000000 --- a/src/datasets/base/supported-logiset.jl +++ /dev/null @@ -1,58 +0,0 @@ - -############################################################################################ -# Explicit modal dataset with support -########################################################################################### - -# The lookup table (fwd) in a featured modal dataset provides a quick answer on the truth of -# propositional decisions; as for answering modal decisions (e.g., ⟨L⟩ (minimum(A2) ≥ 10) ) -# with an fwd, one must enumerate the accessible worlds, compute the truth on each world, -# and aggregate the answer (by means of all/any). This process is costly; instead, it is -# sometimes more convenient to initially spend more time computing the truth of any decision, -# and store this information in a *support* lookup table. Similarly, one can decide to deploy -# memoization on this table (instead of computing everything at the beginning, compute it on -# the fly and store it for later calls). -# -# We define an abstract type for explicit modal dataset with support lookup tables -# remove: abstract type ExplicitModalDatasetWithSupport{V,W,FR} <: AbstractActiveScalarLogiset{W,V,FT,Bool,FR} end -# And an abstract type for support lookup tables -abstract type AbstractSupport{V,W} end - -function nonnothingshare(support::AbstractSupport) - (isinf(capacity(support)) ? NaN : nmemoizedvalues(support)/capacity(support)) -end -# -# In general, one can use lookup (with or without memoization) for any decision, even the -# more complex ones, for example: -# ⟨G⟩ (minimum(A2) ≥ 10 ∧ (⟨O⟩ (maximum(A3) > 2) ∨ (minimum(A1) < 0))) -# -# In practice, decision trees only ask about simple decisions such as ⟨L⟩ (minimum(A2) ≥ 10), -# or ⟨G⟩ (maximum(A2) ≤ 50). Because the global operator G behaves differently from other -# relations, it is natural to differentiate between global and relational support tables: -# -abstract type AbstractRelationalSupport{V,W,FR<:AbstractFrame} <: AbstractSupport{V,W} end -abstract type AbstractGlobalSupport{V} <: AbstractSupport{V,W where W<:AbstractWorld} end -# -# Be an *fwd_rs* an fwd relational support, and a *fwd_gs* an fwd global support, -# for simple support tables like these, it is convenient to store, again, modal *gamma* values. -# Similarly to fwd, gammas are basically values on the verge of truth, that can straightforwardly -# anser simple modal questions. -# Consider the decision (w ⊨ f ⋈ a) on the i-th instance, for a given feature f, -# world w, relation R and test operator ⋈, and let gamma (γ) be: -# - fwd_rs[i, f, a, R, w] if R is a regular relation, or -# - fwd_gs[i, f, a] if R is the global relation G, -# where a = aggregator(⋈). In this context, γ is the unique value for which w ⊨ f ⋈ γ holds and: -# - if aggregator(⋈) = minimum: ∀ a > γ: (w ⊨ f ⋈ a) does not hold -# - if aggregator(⋈) = maximum: ∀ a < γ: (w ⊨ f ⋈ a) does not hold -# -# Let us define the world type-agnostic implementations for fwd_rs and fwd_gs (note that any fwd_gs -# is actually inherently world agnostic); world type-specific implementations can be defined -# in a similar way. - -############################################################################################ -############################################################################################ - -isminifiable(::Union{AbstractRelationalSupport,AbstractGlobalSupport}) = true - -function minify(support::Union{AbstractRelationalSupport,AbstractGlobalSupport}) - minify(support.d) #TODO improper -end diff --git a/src/datasets/dimensional-datasets/main.jl b/src/datasets/dimensional-datasets/main.jl deleted file mode 100644 index 92f0833..0000000 --- a/src/datasets/dimensional-datasets/main.jl +++ /dev/null @@ -1,66 +0,0 @@ -module DimensionalDatasets - -import SoleLogics: worldtype - -using SoleModels.utils - -# Feature brackets -const UVF_OPENING_BRACKET = "[" -const UVF_CLOSING_BRACKET = "]" -# Default prefix for variables -const UVF_VARPREFIX = "V" - -export UnivariateMin, UnivariateMax, - UnivariateSoftMin, UnivariateSoftMax, - MultivariateFeature - -# Features for dimensional datasets -include("dimensional-features.jl") - -export parsecondition - -# Conditions on features for dimensional datasets -include("parse-dimensional-condition.jl") - -# Concrete type for ontologies -include("ontology.jl") # TODO frame inside the ontology? - -export DimensionalLogiset, Logiset, SupportedScalarLogiset - -# Dataset structures -include("datasets/main.jl") - -const GenericModalDataset = Union{AbstractDimensionalDataset,AbstractLogiset,MultiFrameLogiset} - -# TODO? -include("gamma-access.jl") - -# Dimensional ontologies -include("dimensional-ontologies.jl") - -using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame -using SoleLogics: X, Y, Z - -# Representatives for dimensional frames -include("representatives/Full0DFrame.jl") -include("representatives/Full1DFrame.jl") -include("representatives/Full1DFrame+IA.jl") -include("representatives/Full1DFrame+RCC.jl") -include("representatives/Full2DFrame.jl") - -_st_featop_abbr(f::UnivariateMin, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) ⪴" -_st_featop_abbr(f::UnivariateMax, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) ⪳" -_st_featop_abbr(f::UnivariateSoftMin, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) $("⪴" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" -_st_featop_abbr(f::UnivariateSoftMax, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) $("⪳" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" - -_st_featop_abbr(f::UnivariateMin, ::typeof(<); kwargs...) = "$(variable_name(f; kwargs...)) ⪶" -_st_featop_abbr(f::UnivariateMax, ::typeof(>); kwargs...) = "$(variable_name(f; kwargs...)) ⪵" -_st_featop_abbr(f::UnivariateSoftMin, ::typeof(<); kwargs...) = "$(variable_name(f; kwargs...)) $("⪶" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" -_st_featop_abbr(f::UnivariateSoftMax, ::typeof(>); kwargs...) = "$(variable_name(f; kwargs...)) $("⪵" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" - -_st_featop_abbr(f::UnivariateMin, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) ↘" -_st_featop_abbr(f::UnivariateMax, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) ↗" -_st_featop_abbr(f::UnivariateSoftMin, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) $("↘" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" -_st_featop_abbr(f::UnivariateSoftMax, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) $("↗" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" - -end diff --git a/src/logisets/base/check.jl b/src/logisets/base/check.jl new file mode 100644 index 0000000..0da497e --- /dev/null +++ b/src/logisets/base/check.jl @@ -0,0 +1,115 @@ + +function check( + φ::SoleLogics.AbstractFormula, + X::MultiLogiset, + i_modality::Integer, + i_instance::Integer, + args...; + kwargs..., +) + check(φ, modality(X, i_modality), i_instance, args...; kwargs...) +end + +function check( + φ::SoleLogics.AbstractFormula, + X::AbstractLogiset, + i_instance::Integer; + kwargs... +) + check(tree(φ), X, i_instance; kwargs...) +end + +function check( + φ::SoleLogics.SyntaxTree, + X::AbstractLogiset{W,U}, + i_instance::Integer, + w::Union{Nothing,W,AbstractVector{<:W}} = nothing; + use_memo::Union{Nothing,AbstractMemoset{W},AbstractVector{<:AbstractDict{<:F,<:WorldSet}}} = nothing, + perform_normalization::Bool = true, + memo_max_height::Union{Nothing,Int} = nothing, +) where {W<:AbstractWorld,U,F<:SoleLogics.AbstractFormula} + + if isnothing(w) + # w = SoleLogics.initialworld(X, i_instance) + w = nothing + elseif w isa AbstractVector + w = w[i_instance] + end + @assert SoleLogics.isglobal(φ) || !isnothing(w) "Cannot check non-global formula with no initialworld(s): $(syntaxstring(φ))." + + setformula(memo_structure::AbstractDict{<:AbstractFormula}, φ::AbstractFormula, val) = memo_structure[SoleLogics.tree(φ)] = val + readformula(memo_structure::AbstractDict{<:AbstractFormula}, φ::AbstractFormula) = memo_structure[SoleLogics.tree(φ)] + hasformula(memo_structure::AbstractDict{<:AbstractFormula}, φ::AbstractFormula) = haskey(memo_structure, SoleLogics.tree(φ)) + + setformula(memo_structure::AbstractMemoset, φ::AbstractFormula, val) = Base.setindex!(memo_structure, i_instance, SoleLogics.tree(φ), val) + readformula(memo_structure::AbstractMemoset, φ::AbstractFormula) = Base.getindex(memo_structure, i_instance, SoleLogics.tree(φ)) + hasformula(memo_structure::AbstractMemoset, φ::AbstractFormula) = haskey(memo_structure, i_instance, SoleLogics.tree(φ)) + + X, memo_structure = begin + if X isa SupportedLogiset && usesfullmemo(X) + if !isnothing(use_memo) + @warn "Dataset of type $(typeof(X)) uses full memoization, " * + "but a memoization structure was provided to check(...)." + end + base(X), fullmemo(X) + elseif isnothing(use_memo) + X, ThreadSafeDict{SyntaxTree,WorldSet{W}}() + elseif use_memo isa AbstractMemoset + X, use_memo[i_instance] + else + X, use_memo[i_instance] + end + end + + # if X isa SupportedLogiset + # X = base(X) + # end + + if !isnothing(memo_max_height) + forget_list = Vector{SoleLogics.SyntaxTree}() + end + + if perform_normalization + φ = normalize(φ; profile = :modelchecking) + end + + fr = frame(X, i_instance) + + if !hasformula(memo_structure, φ) + for ψ in unique(SoleLogics.subformulas(φ)) + # @show ψ + if !isnothing(memo_max_height) && height(ψ) > memo_max_height + push!(forget_list, ψ) + end + if !hasformula(memo_structure, ψ) + tok = token(ψ) + setformula(memo_structure, ψ, begin + if tok isa SoleLogics.AbstractOperator + collect(SoleLogics.collateworlds(fr, tok, map(f->readformula(memo_structure, f), children(ψ)))) + elseif tok isa Proposition + filter(w->check(tok, X, i_instance, w), collect(allworlds(fr))) + else + error("Unexpected token encountered in _check: $(typeof(tok))") + end + end) + end + # @show syntaxstring(ψ), readformula(memo_structure, ψ) + end + end + + if !isnothing(memo_max_height) + for ψ in forget_list + delete!(memo_structure, ψ) + end + end + + ret = begin + if isnothing(w) + length(readformula(memo_structure, φ)) > 0 + else + w in readformula(memo_structure, φ) + end + end + + return ret +end diff --git a/src/logisets/base/conditions.jl b/src/logisets/base/conditions.jl new file mode 100644 index 0000000..8f3a330 --- /dev/null +++ b/src/logisets/base/conditions.jl @@ -0,0 +1,128 @@ + +using SoleLogics: AbstractAlphabet +using Random +import SoleLogics: negation, propositions + +import Base: isequal, hash, in, isfinite, length + +""" + abstract type AbstractCondition{F<:AbstractFeature} end + +Abstract type for representing conditions that can be interpreted and evaluated +on worlds of instances of a logical dataset. In logical contexts, +these are wrapped into `Proposition`s. + +See also +[`Proposition`](@ref), +[`syntaxstring`](@ref), +[`ScalarMetaCondition`](@ref), +[`ScalarCondition`](@ref). +""" +abstract type AbstractCondition{F<:AbstractFeature} end + +# Check a condition (e.g, on a world of a logiset instance) +function checkcondition(c::AbstractCondition, args...; kwargs...) + error("Please, provide method checkcondition(::$(typeof(c)), " * + join(map(t->"::$(t)", typeof.(args)), ", ") * "; kwargs...). " * + "Note that this value must be unique.") +end + +# function checkcondition( +# c::AbstractCondition, +# X::AbstractLogiset{W,U,F}, +# i_instance::Integer, +# w::W, +# ) where {W<:AbstractWorld,U,F<:AbstractFeature} +# error("Please, provide method checkcondition(c::$(typeof(c)), X::$(typeof(X)), i_instance::$(typeof(i_instance)), w::$(typeof(w))).") +# end + +function syntaxstring(c::AbstractCondition; kwargs...) + error("Please, provide method syntaxstring(::$(typeof(c)); kwargs...). " * + "Note that this value must be unique.") +end + +function Base.show(io::IO, c::AbstractCondition) + # print(io, "Feature of type $(typeof(c))\n\t-> $(syntaxstring(c))") + print(io, "$(typeof(c)): $(syntaxstring(c))") + # print(io, "$(syntaxstring(c))") +end + +Base.isequal(a::AbstractCondition, b::AbstractCondition) = syntaxstring(a) == syntaxstring(b) # nameof(x) == nameof(feature) +Base.hash(a::AbstractCondition) = Base.hash(syntaxstring(a)) + +function parsecondition( + C::Type{<:AbstractCondition}, + expression::String; + kwargs... +) + error("Please, provide method parsecondition(::$(Type{C}), expression::$(typeof(expression)); kwargs...).") +end + +############################################################################################ + +""" + struct ValueCondition{F<:AbstractFeature} <: AbstractCondition{F} + feature::F + end + +A condition which yields a truth value equal to the value of a feature. + +See also [`AbstractFeature`](@ref). +""" +struct ValueCondition{F<:AbstractFeature} <: AbstractCondition{F} + feature::F +end + +checkcondition(c::ValueCondition, args...; kwargs...) = featvalue(c.feature, args...; kwargs...) + +syntaxstring(c::ValueCondition; kwargs...) = string(c.feature) + +function parsecondition( + ::Type{ValueCondition}, + expression::String; + featuretype = Feature, + kwargs... +) + ValueCondition(featuretype(expression)) +end + +############################################################################################ + +""" + struct FunctionalCondition{F<:AbstractFeature} <: AbstractCondition{F} + feature::F + f::F + end + +A condition which yields a truth value equal to the value of a function. + +See also [`AbstractFeature`](@ref). +""" +struct FunctionalCondition{F<:AbstractFeature} <: AbstractCondition{F} + feature::F + f::Function +end + +checkcondition(c::FunctionalCondition, args...; kwargs...) = (c.f)(featvalue(c.feature, args...; kwargs...)) + +syntaxstring(c::FunctionalCondition; kwargs...) = "$(c.f)($(c.feature))" + +function parsecondition( + ::Type{FunctionalCondition}, + expression::String; + featuretype = Feature, + kwargs... +) + r = Regex("^\\s*(\\w+)\\(\\s*(\\w+)\\s*\\)\\s*\$") + slices = match(r, expression) + + @assert !isnothing(slices) && length(slices) == 2 "Could not parse FunctionalCondition from " * + "expression $(repr(expression))." + + slices = string.(slices) + + feature = featuretype(slices[1]) + f = eval(Meta.parse(slices[2])) + + FunctionalCondition(feature, f) +end diff --git a/src/logisets/base/features.jl b/src/logisets/base/features.jl new file mode 100644 index 0000000..95693f4 --- /dev/null +++ b/src/logisets/base/features.jl @@ -0,0 +1,81 @@ +import Base: isequal, hash, show +import SoleLogics: syntaxstring + +""" + abstract type AbstractFeature end + +Abstract type for features of worlds of +[Kripke structures](https://en.wikipedia.org/wiki/Kripke_structure_(model_checking). + +See also [`featvaltype`](@ref), [`AbstractWorld`](@ref). +""" +abstract type AbstractFeature end + +function syntaxstring(f::AbstractFeature; kwargs...) + error("Please, provide method syntaxstring(::$(typeof(f)); kwargs...)." + * " Note that this value must be unique.") +end + +function Base.show(io::IO, f::AbstractFeature) + # print(io, "Feature of type $(typeof(f))\n\t-> $(syntaxstring(f))") + print(io, "$(typeof(f)): $(syntaxstring(f))") + # print(io, "$(syntaxstring(f))") +end + +Base.isequal(a::AbstractFeature, b::AbstractFeature) = syntaxstring(a) == syntaxstring(b) +Base.hash(a::AbstractFeature) = Base.hash(syntaxstring(a)) + +function parsefeature( + F::Type{<:AbstractFeature}, + expression::String; + kwargs... +) + error("Please, provide method parsefeature(::$(F), expression::$(typeof(expression)); kwargs...).") +end + +############################################################################################ + +""" + struct Feature{A} <: AbstractFeature + atom::A + end + +A feature solely identified by an atom (e.g., a string with its name, +a tuple of strings, etc.) + +See also [`AbstractFeature`](@ref). +""" +struct Feature{A} <: AbstractFeature + atom::A +end + +syntaxstring(f::Feature; kwargs...) = string(f.atom) + +function parsefeature( + ::Type{Feature}, + expression::String; + kwargs... +) + Feature(expression) +end + +############################################################################################ + +""" + struct ExternalFWDFeature <: AbstractFeature + name::String + fwd::Any + end + +A feature encoded explicitly as (a slice of) an FWD structure (see `AbstractFeatureLookupSet`). + +See also +[`AbstractFeatureLookupSet`](@ref), [`AbstractFeature`](@ref). +""" +struct ExternalFWDFeature <: AbstractFeature + name::String + fwd::Any +end +syntaxstring(f::ExternalFWDFeature; kwargs...) = f.name + +############################################################################################ diff --git a/src/logisets/base/logiset.jl b/src/logisets/base/logiset.jl new file mode 100644 index 0000000..ae0db3e --- /dev/null +++ b/src/logisets/base/logiset.jl @@ -0,0 +1,350 @@ +using SoleLogics: AbstractKripkeStructure, AbstractInterpretationSet, AbstractFrame +using SoleLogics: TruthValue +import SoleLogics: alphabet, frame, check +import SoleLogics: accessibles, allworlds, nworlds, initialworld +import SoleLogics: worldtype, frametype + +""" + abstract type AbstractLogiset{ + W<:AbstractWorld, + U, + F<:AbstractFeature, + FR<:AbstractFrame{W}, + } <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:F},T where T<:TruthValue,FR}} end + +Abstract type for logisets, that is, logical datasets for +symbolic learning where each instance is a +[Kripke structure](https://en.wikipedia.org/wiki/Kripke_structure_(model_checking)) +associating feature values to each world. +Conditions (see [`AbstractCondition`](@ref)), and logical formulas +with conditional letters can be checked on worlds of instances of the dataset. + +See also +[`AbstractCondition`](@ref), +[`AbstractFeature`](@ref), +[`AbstractKripkeStructure`](@ref), +[`AbstractInterpretationSet`](@ref). +""" +abstract type AbstractLogiset{ + W<:AbstractWorld, + U, + F<:AbstractFeature, + FR<:AbstractFrame{W}, +} <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:F},T where T<:TruthValue,FR}} end + +function featvalue( + X::AbstractLogiset{W}, + i_instance::Integer, + w::W, + f::AbstractFeature, +) where {W<:AbstractWorld} + error("Please, provide method featvalue(::$(typeof(X)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), f::$(typeof(f))).") +end + +function frame(X::AbstractLogiset, i_instance::Integer) + error("Please, provide method frame(::$(typeof(X)), i_instance::$(typeof(i_instance))).") +end + +function ninstances(X::AbstractLogiset) + error("Please, provide method ninstances(::$(typeof(X))).") +end + +function allfeatvalues( + X::AbstractLogiset, + i_instance, +) + error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance))).") +end + +function allfeatvalues( + X::AbstractLogiset, + i_instance, + f, +) + error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), f::$(typeof(f))).") +end + +function instances( + X::AbstractLogiset, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false); + kwargs... +) + error("Please, provide method instances(::$(typeof(X))).") +end + +function concatdatasets(Xs::AbstractLogiset...) + error("Please, provide method concatdatasets(X...::$(typeof(Xs))).") +end + +function displaystructure(X::AbstractLogiset; kwargs...)::String + error("Please, provide method displaystructure(X::$(typeof(X)); kwargs...)::String.") +end + +isminifiable(::AbstractLogiset) = false + +usesfullmemo(::AbstractLogiset) = false + +function allfeatvalues(X::AbstractLogiset) + unique([allfeatvalues(X, i_instance) for i_instance in 1:ninstances(X)]) +end + +hasnans(::AbstractLogiset) = any.(isnan, allfeatvalues(X)) + +############################################################################################ + +function featvalue( + f::AbstractFeature, + X::AbstractLogiset{W}, + i_instance::Integer, + w::W, +) where {W<:AbstractWorld} + featvalue(X, i_instance, w, f) +end + +function check( + p::Proposition{<:AbstractCondition}, + X::AbstractLogiset{W}, + i_instance::Integer, + w::W; + kwargs... +) where {W<:AbstractWorld} + checkcondition(atom(p), X, i_instance, w; kwargs...) +end + +function Base.show(io::IO, X::AbstractLogiset; kwargs...) + println(io, displaystructure(X; kwargs...)) +end + +worldtype(::Type{<:AbstractLogiset{W}}) where {W<:AbstractWorld} = W +worldtype(X::AbstractLogiset) = worldtype(typeof(X)) + +featvaltype(::Type{<:AbstractLogiset{W,U}}) where {W<:AbstractWorld,U} = U +featvaltype(X::AbstractLogiset) = featvaltype(typeof(X)) + +featuretype(::Type{<:AbstractLogiset{W,U,F}}) where {W<:AbstractWorld,U,F<:AbstractFeature} = F +featuretype(X::AbstractLogiset) = featuretype(typeof(X)) + +frametype(::Type{<:AbstractLogiset{W,U,F,FR}}) where {W<:AbstractWorld,U,F<:AbstractFeature,FR<:AbstractFrame} = FR +frametype(X::AbstractLogiset) = frametype(typeof(X)) + +representatives(X::AbstractLogiset, i_instance::Integer, args...) = representatives(frame(X, i_instance), args...) + +############################################################################################ + + +# """ +# abstract type AbstractBaseLogiset{ +# W<:AbstractWorld, +# U, +# F<:AbstractFeature, +# FR<:AbstractFrame{W}, +# } <: AbstractLogiset{W,U,F,FR} end + +# (Base) logisets can be associated to support logisets that perform memoization in order +# to speed up model checking times. + +# See also +# [`SuportedLogiset`](@ref), +# [`AbstractLogiset`](@ref). +# """ +# abstract type AbstractBaseLogiset{ +# W<:AbstractWorld, +# U, +# F<:AbstractFeature, +# FR<:AbstractFrame{W}, +# } <: AbstractLogiset{W,U,F,FR} end + + +""" + struct ExplicitBooleanLogiset{ + W<:AbstractWorld, + F<:AbstractFeature, + FR<:AbstractFrame{W}, + } <: AbstractLogiset{W,Bool,F,FR} + + d :: Vector{Tuple{Dict{W,Vector{F}},FR}} + + end + +A logiset where the features are boolean, and where each instance associates to each world +the set of features with `true`. + +See also +[`AbstractLogiset`](@ref). +""" +struct ExplicitBooleanLogiset{ + W<:AbstractWorld, + F<:AbstractFeature, + FR<:AbstractFrame{W}, + D<:AbstractVector{<:Tuple{<:Dict{<:W,<:Vector{<:F}},<:FR}} +} <: AbstractLogiset{W,Bool,F,FR} + + d :: D + +end + +ninstances(X::ExplicitBooleanLogiset) = length(X.d) + +function featvalue( + X::ExplicitBooleanLogiset{W}, + i_instance::Integer, + w::W, + f::AbstractFeature, +) where {W<:AbstractWorld} + Base.in(f, X.d[i_instance][1][w]) +end + + +function frame( + X::ExplicitBooleanLogiset{W}, + i_instance::Integer, +) where {W<:AbstractWorld} + X.d[i_instance][2] +end + +function instances( + X::ExplicitBooleanLogiset, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false); + kwargs... +) + ExplicitBooleanLogiset(if return_view == Val(true) @views X.d[inds] else X.d[inds] end) +end + +function concatdatasets(Xs::ExplicitBooleanLogiset...) + ExplicitBooleanLogiset(vcat([X.d for X in Xs]...)) +end + +function displaystructure(X::ExplicitBooleanLogiset; indent_str = "", include_ninstances = true) + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) + out = "ExplicitBooleanLogiset ($(humansize(X)))\n" + out *= indent_str * "├ " * padattribute("worldtype:", worldtype(X)) * "\n" + out *= indent_str * "├ " * padattribute("featvaltype:", featvaltype(X)) * "\n" + out *= indent_str * "├ " * padattribute("featuretype:", featuretype(X)) * "\n" + out *= indent_str * "├ " * padattribute("frametype:", frametype(X)) * "\n" + if include_ninstances + out *= indent_str * "├ " * padattribute("# instances:", ninstances(X)) * "\n" + end + out *= indent_str * "└ " * padattribute("# world density (countmap):", "$(countmap([nworlds(X, i_instance) for i_instance in 1:ninstances(X)]))") * "\n" + out +end + +function allfeatvalues(X::ExplicitBooleanLogiset) + [true, false] +end + +function allfeatvalues( + X::ExplicitBooleanLogiset, + i_instance +) + [true, false] +end + +function allfeatvalues( + X::ExplicitBooleanLogiset, + i_instance, + feature, +) + [true, false] +end + +hasnans(X::ExplicitBooleanLogiset) = false + +# TODO "show plot" method + + +""" + struct ExplicitLogiset{ + W<:AbstractWorld, + U, + F<:AbstractFeature, + FR<:AbstractFrame{W}, + } <: AbstractLogiset{W,U,F,FR} + + d :: Vector{Tuple{Dict{W,Dict{F,U}},FR}} + + end + +A logiset where the features are boolean, and where each instance associates to each world +the set of features with `true`. + +See also +[`AbstractLogiset`](@ref). +""" +struct ExplicitLogiset{ + W<:AbstractWorld, + U, + F<:AbstractFeature, + FR<:AbstractFrame{W}, + D<:AbstractVector{<:Tuple{<:Dict{<:W,<:Dict{<:F,<:U}},<:FR}} +} <: AbstractLogiset{W,U,F,FR} + + d :: D + +end + +ninstances(X::ExplicitLogiset) = length(X.d) + +function featvalue( + X::ExplicitLogiset{W}, + i_instance::Integer, + w::W, + f::AbstractFeature, +) where {W<:AbstractWorld} + X.d[i_instance][1][w][f] +end + + +function frame( + X::ExplicitLogiset{W}, + i_instance::Integer, +) where {W<:AbstractWorld} + X.d[i_instance][2] +end + +function instances( + X::ExplicitLogiset, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false); + kwargs... +) + ExplicitLogiset(if return_view == Val(true) @views X.d[inds] else X.d[inds] end) +end + +function concatdatasets(Xs::ExplicitLogiset...) + ExplicitBooleanLogiset(vcat([X.d for X in Xs]...)) +end + +function displaystructure(X::ExplicitLogiset; indent_str = "", include_ninstances = true) + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) + out = "ExplicitBooleanLogiset ($(humansize(X)))\n" + out *= indent_str * "├ " * padattribute("worldtype:", "$(worldtype(X))") * "\n" + out *= indent_str * "├ " * padattribute("featvaltype:", "$(featvaltype(X))") * "\n" + out *= indent_str * "├ " * padattribute("featuretype:", "$(featuretype(X))") * "\n" + out *= indent_str * "├ " * padattribute("frametype:", "$(frametype(X))") * "\n" + if include_ninstances + out *= indent_str * "├ " * padattribute("# instances:", "$(ninstances(X))") * "\n" + end + out *= indent_str * "└ " * padattribute("# world density (countmap):", "$(countmap([nworlds(X, i_instance) for i_instance in 1:ninstances(X)]))") * "\n" + out +end + + +# TODO "show plot" method + + +function allfeatvalues( + X::ExplicitLogiset{W}, + i_instance, +) where {W<:AbstractWorld} + unique(collect(Iterators.flatten(values.(values(d[i_instance][1]))))) +end + +function allfeatvalues( + X::ExplicitLogiset{W}, + i_instance, + feature, +) where {W<:AbstractWorld} + unique([ch[feature] for ch in values(d[i_instance][1])]) +end diff --git a/src/logisets/base/main.jl b/src/logisets/base/main.jl new file mode 100644 index 0000000..2b27297 --- /dev/null +++ b/src/logisets/base/main.jl @@ -0,0 +1,44 @@ +import SoleLogics: frame + +using SoleLogics: OneWorld, Interval, Interval2D +using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame +using SoleLogics: X, Y, Z +using SoleLogics: AbstractWorld, IdentityRel +import SoleLogics: syntaxstring + +import SoleData: ninstances +import SoleData: hasnans, instances + +# Features to be computed on worlds of dataset instances +include("features.jl") + +# Conditions on the features, to be wrapped in Proposition's +include("conditions.jl") + +export check, accessibles, allworlds, representatives, initialworld + +# Interface for representative accessibles, for optimized model checking on specific frames +include("representatives.jl") + +export ninstances, featvalue, displaystructure, isminifiable, minify + +# Logical datasets, where the instances are Kripke models with conditional alphabets +include("logiset.jl") + +include("memosets.jl") + +include("supported-logiset.jl") + +export MultiLogiset, modalities, worldtypes, nmodalities + +# Multiframe version of logisets, for representing multimodal datasets +include("multilogiset.jl") + +# Model checking algorithms for logisets and multilogisets +include("check.jl") + +include("scalar-conditions/main.jl") + +# # TODO figure out which convert function works best: +# convert(::Type{<:MultiLogiset{T}}, X::MD) where {T,MD<:AbstractLogiset{T}} = MultiLogiset{MD}([X]) +# convert(::Type{<:MultiLogiset}, X::AbstractLogiset) = MultiLogiset([X]) diff --git a/src/logisets/base/memosets.jl b/src/logisets/base/memosets.jl new file mode 100644 index 0000000..2d93eb7 --- /dev/null +++ b/src/logisets/base/memosets.jl @@ -0,0 +1,165 @@ +""" + abstract type AbstractMemoset{ + W<:AbstractWorld, + U, + F<:AbstractFeature, + FR<:AbstractFrame, + } <: AbstractLogiset{W,U,F,FR} end + +Abstract type for memoization structures to be used when checking +formulas on logisets. + +See also +[`Memoset`](@ref), +[`SuportedLogiset`](@ref), +[`AbstractLogiset`](@ref). +""" +abstract type AbstractMemoset{ + W<:AbstractWorld, + U, + F<:AbstractFeature, + FR<:AbstractFrame, +} <: AbstractLogiset{W,U,F,FR} end +# } <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:F},T where T<:TruthValue,FR where FR<:AbstractFrame}} end + +function capacity(Xm::AbstractMemoset) + error("Please, provide method capacity(::$(typeof(Xm))).") +end + +function nmemoizedvalues(Xm::AbstractMemoset) + error("Please, provide method nmemoizedvalues(::$(typeof(Xm))).") +end + +function nonnothingshare(Xm::AbstractMemoset) + (isinf(capacity(Xm)) ? NaN : nmemoizedvalues(Xm)/capacity(Xm)) +end + +function displaystructure(Xm::AbstractMemoset; indent_str = "", include_ninstances = true) + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) + pieces = [] + push!(pieces, "") + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") + push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") + push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + if include_ninstances + push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") + end + push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") + + return "Memoset ($(humansize(Xm)))" * + join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" +end + +############################################################################################ + +""" +A generic, full memoization structure that works for any *crisp* logic; +For each instance of a dataset, +this structure associates formulas to the set of worlds where the +formula holds; it was introduced by Emerson-Clarke for the +well-known model checking algorithm for CTL*. + +# Examples +TODO add example showing that checking is faster when using this structure. + +See also +[`SuportedLogiset`](@ref), +[`AbstractMemoset`](@ref), +[`AbstractLogiset`](@ref). +""" +struct Memoset{ + W<:AbstractWorld, + D<:AbstractVector{<:AbstractDict{<:AbstractFormula,<:WorldSet{W}}}, +} <: AbstractMemoset{W,U where U,F where F<:AbstractFeature,FR where FR<:AbstractFrame{W}} + + d :: D + + function Memoset{W,D}( + d::D + ) where {W,D<:AbstractVector{<:AbstractDict{<:AbstractFormula,<:Union{<:AbstractVector{W},Nothing}}}} + new{W,D}(d) + end + + function Memoset( + d::D + ) where {W,D<:AbstractVector{<:AbstractDict{<:AbstractFormula,<:Union{<:AbstractVector{W},Nothing}}}} + new{W,D}(d) + end + + function Memoset( + X::AbstractLogiset{W,U,F,FR}, + perform_initialization = false, + ) where {W,U,F<:AbstractFeature,FR<:AbstractFrame{W}} + d = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i in 1:ninstances(X)] + D = typeof(d) + Memoset{W,D}(d) + end +end + +ninstances(Xm::Memoset) = length(Xm.d) + +capacity(Xm::Memoset) = Inf +nmemoizedvalues(Xm::Memoset) = sum(length.(Xm.d)) + +@inline function Base.haskey( + Xm :: Memoset{W}, + i_instance :: Integer, + f :: AbstractFormula, +) where {W<:AbstractWorld} + haskey(Xm.d[i_instance], f) +end + +@inline function Base.getindex( + Xm :: Memoset{W}, + i_instance :: Integer, +) where {W<:AbstractWorld} + Xm.d[i_instance] +end +@inline function Base.getindex( + Xm :: Memoset{W}, + i_instance :: Integer, + f :: AbstractFormula, +) where {W<:AbstractWorld} + Xm.d[i_instance][f] +end +@inline function Base.setindex!( + Xm :: Memoset{W}, + i_instance :: Integer, + f :: AbstractFormula, + ws :: WorldSet{W}, +) where {W} + Xm.d[i_instance][f] = ws +end + +function check( + f::AbstractFormula, + Xm::Memoset{W}, + i_instance::Integer, + w::W; + kwargs... +) where {W<:AbstractWorld} + w in Base.getindex(Xm, i_instance, f) +end + +function instances( + Xm::Memoset, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false); + kwargs... +) + Memoset(if return_view == Val(true) @view Xm.d[inds] else Xm.d[inds] end) +end + +function concatdatasets(Xms::Memoset...) + Memoset(vcat([Xm.d for Xm in Xms]...)) +end + +usesfullmemo(::Memoset) = true +fullmemo(Xm::Memoset) = Xm + +hasnans(::Memoset) = false + +# Base.size(::Memoset) = () + +############################################################################################ diff --git a/src/logisets/base/multilogiset.jl b/src/logisets/base/multilogiset.jl new file mode 100644 index 0000000..d03f3f1 --- /dev/null +++ b/src/logisets/base/multilogiset.jl @@ -0,0 +1,166 @@ +""" + struct MultiLogiset{L<:AbstractLogiset} + modalities :: Vector{L} + end + +A logical dataset composed of different +[modalities](https://en.wikipedia.org/wiki/Multimodal_learning)); +this structure is useful for representing multimodal datasets in logical terms. + +See also +[`AbstractLogiset`](@ref), +[`minify`](@ref). +""" +struct MultiLogiset{L<:AbstractLogiset} + + modalities :: Vector{L} + + function MultiLogiset{L}(X::MultiLogiset{L}) where {L<:AbstractLogiset} + MultiLogiset{L}(X.modalities) + end + function MultiLogiset{L}(X::AbstractVector) where {L<:AbstractLogiset} + X = collect(X) + @assert length(X) > 0 && length(unique(ninstances.(X))) == 1 "Can't create an empty MultiLogiset or with mismatching number of instances (nmodalities: $(length(X)), modality_sizes: $(ninstances.(X)))." + new{L}(X) + end + function MultiLogiset{L}() where {L<:AbstractLogiset} + new{L}(L[]) + end + function MultiLogiset{L}(X::L) where {L<:AbstractLogiset} + MultiLogiset{L}(L[X]) + end + function MultiLogiset(X::AbstractVector{L}) where {L<:AbstractLogiset} + MultiLogiset{L}(X) + end + function MultiLogiset(X::L) where {L<:AbstractLogiset} + MultiLogiset{L}(X) + end +end + +modalities(X::MultiLogiset) = X.modalities + +Base.length(X::MultiLogiset) = length(nmodalities(X)) +Base.push!(X::MultiLogiset, f::AbstractLogiset) = push!(modalities(X), f) +Base.getindex(X::MultiLogiset, i_modality::Integer) = modalities(X)[i_modality] +Base.iterate(X::MultiLogiset, state=1) = state > nmodalities(X) ? nothing : (modality(X, state), state+1) + +modalitytype(::Type{<:MultiLogiset{L}}) where {L<:AbstractLogiset} = L +modalitytype(X::MultiLogiset) = modalitytype(typeof(X)) + +modality(X::MultiLogiset, i_modality::Integer) = modalities(X)[i_modality] +nmodalities(X::MultiLogiset) = length(modalities(X)) +ninstances(X::MultiLogiset) = ninstances(modality(X, 1)) + +worldtype(X::MultiLogiset, i_modality::Integer) = worldtype(modality(X, i_modality)) +worldtypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(worldtype.(modalities(X))) + +featvaltype(X::MultiLogiset, i_modality::Integer) = featvaltype(modality(X, i_modality)) +featvaltypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(featvaltype.(modalities(X))) + +featuretype(X::MultiLogiset, i_modality::Integer) = featuretype(modality(X, i_modality)) +featuretypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(featuretype.(modalities(X))) + +frametype(X::MultiLogiset, i_modality::Integer) = frametype(modality(X, i_modality)) +frametypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(frametype.(modalities(X))) + +function instances( + X::MultiLogiset{L}, + inds::AbstractVector{<:Integer}, + args...; + kwargs... +) where {L<:AbstractLogiset} + MultiLogiset{L}(Vector{L}(map(modality->instances(modality, inds, args...; kwargs...), modalities(X)))) +end + +function concatdatasets(Xs::MultiLogiset...) + MultiLogiset([ + concatdatasets([modalities(X)[i_mod] for X in Xs]...) for i_mod in 1:nmodalities(Xs) + ]) +end + +function Base.show(io::IO, X::MultiLogiset; kwargs...) + println(io, displaystructure(X; kwargs...)) +end + +function displaystructure(X::MultiLogiset; indent_str = "", include_ninstances = true) + out = "MultiLogiset($(humansize(X)))\n" + if include_ninstances + out *= indent_str * "├ # instances:\t$(ninstances(X))\n" + end + out *= indent_str * "├ modalitytype:\t$(modalitytype(X))\n" + out *= indent_str * "├ # modalities:\t$(nmodalities(X))\n" + for (i_modality, mod) in enumerate(modalities(X)) + if i_modality == nmodalities(X) + out *= "$(indent_str)└ " + else + out *= "$(indent_str)├ " + end + out *= "[$(i_modality)] " + # \t\t\t$(humansize(mod))\t(worldtype: $(worldtype(mod)))" + out *= displaystructure(mod; indent_str = indent_str * (i_modality == nmodalities(X) ? " " : "│ "), include_ninstances = false) * "" + end + out +end + + +function featvalue( + X::MultiLogiset, + i_modality::Integer, + i_instance::Integer, + w::W, + f::AbstractFeature; + kwargs... +) where {W<:AbstractWorld} + featvalue(modality(X, i_modality), i_instance, w, f) +end + +function check( + p::Proposition{<:AbstractCondition}, + X::MultiLogiset, + i_modality::Integer, + i_instance::Integer, + w::W; + kwargs... +) where {W<:AbstractWorld} + check(p, modality(X, i_modality), i_instance, w; kwargs...) +end + + +hasnans(X::MultiLogiset) = any(hasnans.(modalities(X))) + +isminifiable(X::MultiLogiset) = any(isminifiable.(modalities(X))) + +function minify(X::MultiLogiset) + if !any(map(isminifiable, modalities(X))) + if !all(map(isminifiable, modalities(X))) + @error "Cannot perform minification with modalities of types $(typeof.(modalities(X))). Please use a minifiable format (e.g., SupportedScalarLogiset)." + else + @warn "Cannot perform minification on some of the modalities provided. Please use a minifiable format (e.g., SupportedScalarLogiset) ($(typeof.(modalities(X))) were used instead)." + end + end + X, backmap = zip([!isminifiable(X) ? minify(X) : (X, identity) for X in modalities(X)]...) + X, backmap +end + +############################################################################################ + +function featvalue( + f::AbstractFeature, + X::MultiLogiset, + i_modality::Integer, + i_instance::Integer, + w::W; + kwargs... +) where {W<:AbstractWorld} + featvalue(X, i_modality, i_instance, w, f) +end + +# TODO remove: + +# Base.size(X::MultiLogiset) = map(size, modalities(X)) + +# # max_channel_size(X::MultiLogiset) = map(max_channel_size, modalities(X)) # TODO: figure if this is useless or not. Note: channel_size doesn't make sense at this point. +# nfeatures(X::MultiLogiset) = map(nfeatures, modalities(X)) # Note: used for safety checks in fit_tree.jl +# # nrelations(X::MultiLogiset) = map(nrelations, modalities(X)) # TODO: figure if this is useless or not +# nfeatures(X::MultiLogiset, i_modality::Integer) = nfeatures(modality(X, i_modality)) +# nrelations(X::MultiLogiset, i_modality::Integer) = nrelations(modality(X, i_modality)) diff --git a/src/datasets/base/representatives.jl b/src/logisets/base/representatives.jl similarity index 82% rename from src/datasets/base/representatives.jl rename to src/logisets/base/representatives.jl index d5764b0..08507b3 100644 --- a/src/datasets/base/representatives.jl +++ b/src/logisets/base/representatives.jl @@ -1,4 +1,4 @@ -using SoleLogics: AbstractMultiModalFrame, AbstractRelation, accessibles +using SoleLogics: AbstractFrame, AbstractMultiModalFrame, AbstractRelation, accessibles """ function representatives( @@ -31,7 +31,15 @@ function representatives( # Dispatch on feature/aggregator pairs fr::AbstractMultiModalFrame{W}, w::W, r::AbstractRelation, - mc::AbstractCondition + ::AbstractCondition ) where {W<:AbstractWorld} accessibles(fr, w, r) end + +function representatives( + fr::AbstractFrame{W}, + w::W, + ::AbstractCondition +) where {W<:AbstractWorld} + accessibles(fr, w) +end diff --git a/src/datasets/scalar-datasets/canonical-scalar-conditions.jl b/src/logisets/base/scalar-conditions/canonical-conditions.jl similarity index 100% rename from src/datasets/scalar-datasets/canonical-scalar-conditions.jl rename to src/logisets/base/scalar-conditions/canonical-conditions.jl diff --git a/src/datasets/scalar-datasets/scalar-conditions.jl b/src/logisets/base/scalar-conditions/conditions.jl similarity index 64% rename from src/datasets/scalar-datasets/scalar-conditions.jl rename to src/logisets/base/scalar-conditions/conditions.jl index 74a51f9..69d683d 100644 --- a/src/datasets/scalar-datasets/scalar-conditions.jl +++ b/src/logisets/base/scalar-conditions/conditions.jl @@ -1,6 +1,8 @@ ############################################################################################ +const DEFAULT_SCALARCOND_FEATTYPE = SoleModels.VarFeature + """ struct ScalarMetaCondition{F<:AbstractFeature,O<:TestOperator} <: AbstractCondition{F} feature::F @@ -13,7 +15,7 @@ of an instance of a logical dataset. A test operator is a binary mathematical relation, comparing the computed feature value and an external threshold value (see `ScalarCondition`). A metacondition can also be used for representing the infinite set of conditions that arise with a free threshold -(see `UnboundedExplicitConditionalAlphabet`): \${min(V1) ≥ a, a ∈ ℝ}\$. +(see `UnboundedScalarConditions`): \${min(V1) ≥ a, a ∈ ℝ}\$. See also [`AbstractCondition`](@ref), @@ -121,9 +123,86 @@ test_operator(c::ScalarCondition) = test_operator(metacond(c)) negation(c::ScalarCondition) = ScalarCondition(negation(metacond(c)), threshold(c)) +function checkcondition(c::ScalarCondition, args...; kwargs...) + apply_test_operator(test_operator(c), featvalue(feature(c), args...; kwargs...), threshold(c)) +end + syntaxstring(m::ScalarCondition; threshold_decimals = nothing, kwargs...) = "$(_syntaxstring_metacondition(metacond(m); kwargs...)) $((isnothing(threshold_decimals) ? threshold(m) : round(threshold(m); digits=threshold_decimals)))" +function parsecondition( + ::Type{C}, + expression::String; + featuretype::Union{Nothing,Type} = nothing, + featvaltype::Union{Nothing,Type} = nothing, + kwargs... +) where {C<:ScalarCondition} + if isnothing(featvaltype) + featvaltype = DEFAULT_VARFEATVALTYPE + @warn "Please, specify a type for the feature values (featvaltype = ...). " * + "$(featvaltype) will be used, but note that this may raise type errors. " * + "(expression = $(repr(expression)))" + end + if isnothing(featuretype) + featuretype = DEFAULT_SCALARCOND_FEATTYPE + @warn "Please, specify a feature type (featuretype = ...). " * + "$(featuretype) will be used. " * + "(expression = $(repr(expression)))" + end + _parsecondition(C{featvaltype,featuretype}, expression; kwargs...) +end + +function parsecondition( + ::Type{C}, + expression::String; + featuretype::Union{Nothing,Type} = nothing, + kwargs... +) where {U,C<:ScalarCondition{U}} + if isnothing(featuretype) + featuretype = DEFAULT_SCALARCOND_FEATTYPE + @warn "Please, specify a feature type (featuretype = ...). " * + "$(featuretype) will be used. " * + "(expression = $(repr(expression)))" + end + _parsecondition(C{featuretype}, expression; kwargs...) +end + +function parsecondition( + ::Type{C}, + expression::String; + featuretype::Union{Nothing,Type} = nothing, + kwargs... +) where {U,F,C<:ScalarCondition{U,F}} + @assert isnothing(featuretype) || featuretype == F "Cannot parse condition of type $(C) with " * + "featuretype = $(featuretype). (expression = $(repr(expression)))" + _parsecondition(C, expression; kwargs...) +end + +function _parsecondition( + ::Type{C}, + expression::String; + kwargs... +) where {U,F,C<:ScalarCondition{U,F}} + r = Regex("^\\s*(\\S+)\\s+([^\\s\\d]+)\\s*(\\S+)\\s*\$") + slices = match(r, expression) + + @assert !isnothing(slices) && length(slices) == 3 "Could not parse ScalarCondition from " * + "expression $(repr(expression))." + + slices = string.(slices) + + feature = parsefeature(F, slices[1]; featvaltype = U, kwargs...) + test_operator = eval(Meta.parse(slices[2])) + threshold = eval(Meta.parse(slices[3])) + + condition = ScalarCondition(feature, test_operator, threshold) + # if !(condition isa C) + # @warn "Could not parse expression $(repr(expression)) as condition of type $(C); " * + # " $(typeof(condition)) was used." + # end + condition +end + ############################################################################################ ############################################################################################ ############################################################################################ @@ -141,7 +220,7 @@ See also abstract type AbstractConditionalAlphabet{C<:ScalarCondition} <: AbstractAlphabet{C} end """ - struct UnboundedExplicitConditionalAlphabet{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} + struct UnboundedScalarConditions{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} metaconditions::Vector{<:ScalarMetaCondition} end @@ -150,85 +229,85 @@ For example, if `metaconditions = [ScalarMetaCondition(UnivariateMin(1), ≥)]`, the alphabet represents the (infinite) set: \${min(V1) ≥ a, a ∈ ℝ}\$. See also -[`BoundedExplicitConditionalAlphabet`](@ref), +[`BoundedScalarConditions`](@ref), [`ScalarCondition`](@ref), [`ScalarMetaCondition`](@ref), [`AbstractAlphabet`](@ref). """ -struct UnboundedExplicitConditionalAlphabet{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} +struct UnboundedScalarConditions{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} metaconditions::Vector{<:ScalarMetaCondition} - function UnboundedExplicitConditionalAlphabet{C}( + function UnboundedScalarConditions{C}( metaconditions::Vector{<:ScalarMetaCondition} ) where {C<:ScalarCondition} new{C}(metaconditions) end - function UnboundedExplicitConditionalAlphabet( + function UnboundedScalarConditions( features :: AbstractVector{C}, test_operators :: AbstractVector, ) where {C<:ScalarCondition} metaconditions = [ScalarMetaCondition(f, t) for f in features for t in test_operators] - UnboundedExplicitConditionalAlphabet{C}(metaconditions) + UnboundedScalarConditions{C}(metaconditions) end end -Base.isfinite(::Type{<:UnboundedExplicitConditionalAlphabet}) = false +Base.isfinite(::Type{<:UnboundedScalarConditions}) = false -function Base.in(p::Proposition{<:ScalarCondition}, a::UnboundedExplicitConditionalAlphabet) +function Base.in(p::Proposition{<:ScalarCondition}, a::UnboundedScalarConditions) fc = atom(p) idx = findfirst(mc->mc == metacond(fc), a.metaconditions) return !isnothing(idx) end """ - struct BoundedExplicitConditionalAlphabet{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} + struct BoundedScalarConditions{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} grouped_featconditions::Vector{Tuple{<:ScalarMetaCondition,Vector}} end A finite alphabet of conditions, grouped by (a finite set of) metaconditions. See also -[`UnboundedExplicitConditionalAlphabet`](@ref), +[`UnboundedScalarConditions`](@ref), [`ScalarCondition`](@ref), [`ScalarMetaCondition`](@ref), [`AbstractAlphabet`](@ref). """ # Finite alphabet of conditions induced from a set of metaconditions -struct BoundedExplicitConditionalAlphabet{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} +struct BoundedScalarConditions{C<:ScalarCondition} <: AbstractConditionalAlphabet{C} grouped_featconditions::Vector{<:Tuple{ScalarMetaCondition,Vector}} - function BoundedExplicitConditionalAlphabet{C}( + function BoundedScalarConditions{C}( grouped_featconditions::Vector{<:Tuple{ScalarMetaCondition,Vector}} ) where {C<:ScalarCondition} new{C}(grouped_featconditions) end - function BoundedExplicitConditionalAlphabet{C}( + function BoundedScalarConditions{C}( metaconditions::Vector{<:ScalarMetaCondition}, thresholds::Vector{<:Vector}, ) where {C<:ScalarCondition} length(metaconditions) != length(thresholds) && - error("Can't instantiate BoundedExplicitConditionalAlphabet with mismatching" * - " number of `metaconditions` and `thresholds`" * - " ($(metaconditions) != $(thresholds)).") + error("Can't instantiate BoundedScalarConditions with mismatching " * + "number of `metaconditions` and `thresholds` " * + "($(metaconditions) != $(thresholds)).") grouped_featconditions = collect(zip(metaconditions, thresholds)) - BoundedExplicitConditionalAlphabet{C}(grouped_featconditions) + BoundedScalarConditions{C}(grouped_featconditions) end - function BoundedExplicitConditionalAlphabet( + function BoundedScalarConditions( features :: AbstractVector{C}, test_operators :: AbstractVector, thresholds :: Vector ) where {C<:ScalarCondition} metaconditions = [ScalarMetaCondition(f, t) for f in features for t in test_operators] - BoundedExplicitConditionalAlphabet{C}(metaconditions, thresholds) + BoundedScalarConditions{C}(metaconditions, thresholds) end end -function propositions(a::BoundedExplicitConditionalAlphabet) +function propositions(a::BoundedScalarConditions) Iterators.flatten( map( ((mc,thresholds),)->map( @@ -239,7 +318,7 @@ function propositions(a::BoundedExplicitConditionalAlphabet) ) |> collect end -function Base.in(p::Proposition{<:ScalarCondition}, a::BoundedExplicitConditionalAlphabet) +function Base.in(p::Proposition{<:ScalarCondition}, a::BoundedScalarConditions) fc = atom(p) grouped_featconditions = a.grouped_featconditions idx = findfirst(((mc,thresholds),)->mc == metacond(fc), grouped_featconditions) diff --git a/src/datasets/scalar-datasets/main.jl b/src/logisets/base/scalar-conditions/main.jl similarity index 65% rename from src/datasets/scalar-datasets/main.jl rename to src/logisets/base/scalar-conditions/main.jl index bb90b3f..aea4e21 100644 --- a/src/datasets/scalar-datasets/main.jl +++ b/src/logisets/base/scalar-conditions/main.jl @@ -3,28 +3,31 @@ export inverse_test_operator, dual_test_operator, apply_test_operator, TestOperator +# Features for (multi)variate data +include("var-features.jl") + # Test operators to be used for comparing features and threshold values include("test-operators.jl") # Alphabets of conditions on the features, to be used in logical datasets -include("scalar-conditions.jl") +include("conditions.jl") export MixedFeature, CanonicalFeature, canonical_geq, canonical_leq export canonical_geq_95, canonical_geq_90, canonical_geq_85, canonical_geq_80, canonical_geq_75, canonical_geq_70, canonical_geq_60, canonical_leq_95, canonical_leq_90, canonical_leq_85, canonical_leq_80, canonical_leq_75, canonical_leq_70, canonical_leq_60 -# TODO remove? # Types for representing common associations between features and operators -include("canonical-scalar-conditions.jl") +include("canonical-conditions.jl") const MixedFeature = Union{AbstractFeature,CanonicalFeature,Function,Tuple{TestOperator,Function},Tuple{TestOperator,AbstractFeature}} include("random.jl") -############################################################################################ -############################################################################################ -############################################################################################ +include("representatives.jl") +include("scalar-memosets.jl") -include("active-scalar-logiset.jl") # TODO sort out and precisely define interface +export UnivariateMin, UnivariateMax, + UnivariateSoftMin, UnivariateSoftMax, + MultivariateFeature diff --git a/src/datasets/scalar-datasets/random.jl b/src/logisets/base/scalar-conditions/random.jl similarity index 96% rename from src/datasets/scalar-datasets/random.jl rename to src/logisets/base/scalar-conditions/random.jl index 5048fce..0d3ab8d 100644 --- a/src/datasets/scalar-datasets/random.jl +++ b/src/logisets/base/scalar-conditions/random.jl @@ -3,7 +3,7 @@ import Base: rand """ function Base.rand( rng::AbstractRNG, - a::BoundedExplicitConditionalAlphabet; + a::BoundedScalarConditions; metaconditions::Union{Nothing,ScalarMetaCondition,AbstractVector{<:ScalarMetaCondition}} = nothing, feature::Union{Nothing,AbstractFeature,AbstractVector{<:AbstractFeature}} = nothing, test_operator::Union{Nothing,TestOperator,AbstractVector{<:TestOperator}} = nothing, @@ -21,14 +21,14 @@ is limited to those with `test_operator`. TODO Examples See also -[`BoundedExplicitConditionalAlphabet`](@ref), +[`BoundedScalarConditions`](@ref), [`ScalarCondition`](@ref), [`ScalarMetaCondition`](@ref), [`AbstractAlphabet'](@ref). """ function Base.rand( rng::AbstractRNG, - a::BoundedExplicitConditionalAlphabet; + a::BoundedScalarConditions; metaconditions::Union{Nothing,ScalarMetaCondition,AbstractVector{<:ScalarMetaCondition}} = nothing, features::Union{Nothing,AbstractFeature,AbstractVector{<:AbstractFeature}} = nothing, test_operators::Union{Nothing,TestOperator,AbstractVector{<:TestOperator}} = nothing, diff --git a/src/datasets/scalar-datasets/representatives.jl b/src/logisets/base/scalar-conditions/representatives.jl similarity index 94% rename from src/datasets/scalar-datasets/representatives.jl rename to src/logisets/base/scalar-conditions/representatives.jl index 8bbe138..27a6e8e 100644 --- a/src/datasets/scalar-datasets/representatives.jl +++ b/src/logisets/base/scalar-conditions/representatives.jl @@ -23,9 +23,9 @@ function representatives( # Dispatch on feature/aggregator pairs fr::AbstractMultiModalFrame{W}, w::W, r::AbstractRelation, - mc::ScalarMetaCondition + c::ScalarMetaCondition ) where {W<:AbstractWorld} - representatives(fr, w, r, feature(mc), existential_aggregator(test_operator(mc))) + representatives(fr, w, r, feature(c), existential_aggregator(test_operator(c))) end # Fallbacks to `accessibles` diff --git a/src/logisets/base/scalar-conditions/scalar-memosets.jl b/src/logisets/base/scalar-conditions/scalar-memosets.jl new file mode 100644 index 0000000..31f15f9 --- /dev/null +++ b/src/logisets/base/scalar-conditions/scalar-memosets.jl @@ -0,0 +1,262 @@ +""" +A full memoization structure used for checking formulas of scalar conditions on +datasets with scalar features. This structure is the equivalent to [`Memoset`](@ref), +but with scalar features some important optimizations can be done. + +TODO explain + +See also +[`Memoset`](@ref), +[`SuportedLogiset`](@ref), +[`AbstractLogiset`](@ref). +""" +struct ScalarMemoset{ + W<:AbstractWorld, + U, + FR<:AbstractFrame{W}, + D<:AbstractVector{<:AbstractDict{<:AbstractFormula,U}}, +} <: AbstractMemoset{W,U,F where F<:AbstractFeature,FR} + + d :: D + + function ScalarMemoset{W,U,FR,D}( + d::D + ) where {W<:AbstractWorld,U,FR<:AbstractFrame{W},D<:AbstractVector{<:AbstractDict{<:AbstractFormula,U}}} + new{W,U,FR,D}(d) + end + + function ScalarMemoset( + X::AbstractLogiset{W,U,F,FR}, + perform_initialization = false, + ) where {W<:AbstractWorld,U,F<:AbstractFeature,FR<:AbstractFrame{W}} + d = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i in 1:ninstances(X)] + D = typeof(d) + ScalarMemoset{W,U,FR,D}(d) + end +end + +ninstances(Xm::ScalarMemoset) = length(Xm.d) + +capacity(Xm::ScalarMemoset) = Inf +nmemoizedvalues(Xm::ScalarMemoset) = sum(length.(Xm.d)) + + +@inline function Base.haskey( + Xm :: ScalarMemoset, + i_instance :: Integer, + f :: AbstractFormula, +) + haskey(Xm.d[i_instance], f) +end + +@inline function Base.getindex( + Xm :: ScalarMemoset, + i_instance :: Integer, +) + Xm.d[i_instance] +end +@inline function Base.getindex( + Xm :: ScalarMemoset, + i_instance :: Integer, + f :: AbstractFormula, +) + Xm.d[i_instance][f] +end +@inline function Base.setindex!( + Xm :: ScalarMemoset, + i_instance :: Integer, + f :: AbstractFormula, + threshold :: U, +) where {U} + Xm.d[i_instance][f] = threshold +end + +function check( + f::AbstractFormula, + Xm::ScalarMemoset{W}, + i_instance::Integer, + w::W; + kwargs... +) where {W<:AbstractWorld} + error("TODO implement") +end + +function instances( + Xm::ScalarMemoset, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false); + kwargs... +) + ScalarMemoset(if return_view == Val(true) @view Xm.d[inds] else Xm.d[inds] end) +end + +function concatdatasets(Xms::ScalarMemoset...) + ScalarMemoset(vcat([Xm.d for Xm in Xms]...)) +end + +usesfullmemo(::ScalarMemoset) = true +fullmemo(Xm::ScalarMemoset) = Xm + +hasnans(::ScalarMemoset) = false + + +############################################################################################ + +""" +Abstract type for one-step memoization structure for checking formulas of type `⟨R⟩ (f ⋈ t)`. +""" +abstract type AbstractScalarOneStepMemoset{U,W,F<:AbstractFeature,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,F,FR} end + +# Access inner structure +function innerstruct(memoset::AbstractScalarOneStepMemoset) + error("Please, provide method innerstruct(::$(typeof(memoset))).") +end + +isminifiable(::AbstractScalarOneStepMemoset) = true + +function minify(memoset::AbstractScalarOneStepMemoset) + minify(innerstruct(memoset)) +end + +""" +Abstract type for one-step memoization structure for checking formulas of type `⟨R⟩ (f ⋈ t)`, +for a generic relation `R`. +""" +abstract type AbstractScalarOneStepRelationalMemoset{U,W,FR<:AbstractFrame{W}} <: AbstractScalarOneStepMemoset{W,U,F where F<:AbstractFeature,FR} end +""" +Abstract type for one-step memoization structure for checking "global" formulas +of type `⟨G⟩ (f ⋈ t)`. +""" +abstract type AbstractScalarOneStepGlobalMemoset{W,U} <: AbstractScalarOneStepMemoset{W,U,F where F<:AbstractFeature,FR<:AbstractFrame{W}} end + +############################################################################################ + +""" +A generic, one-step memoization structure used for checking specific formulas +of scalar conditions on +datasets with scalar features. The formulas are of type ⟨R⟩ (f ⋈ t) + +TODO explain + +See also +[`Memoset`](@ref), +[`SuportedLogiset`](@ref), +[`AbstractLogiset`](@ref). +""" +struct ScalarOneStepRelationalMemoset{ + W<:AbstractWorld, + U, + FR<:AbstractFrame{W}, + D<:AbstractArray{<:AbstractDict{W,VV}, 3} where VV<:Union{U,Nothing}, +} <: AbstractScalarOneStepRelationalMemoset{W,U,FR} + + d :: D + + function ScalarOneStepRelationalMemoset{W,U,FR}(d::D) where {W<:AbstractWorld,U,FR<:AbstractFrame{W},D<:AbstractArray{U,2}} + new{W,U,FR,D}(d) + end + + function ScalarOneStepRelationalMemoset( + X::AbstractLogiset{W,U,F,FR}, + # TODO: metaconditions, relations, + perform_initialization = false + ) where {W,U,F<:AbstractFeature,FR<:AbstractFrame{W}} + _nfeatsnaggrs = nfeatsnaggrs(X) + _fwd_rs = begin + if perform_initialization + _fwd_rs = Array{Dict{W,Union{U,Nothing}}, 3}(undef, ninstances(X), _nfeatsnaggrs, nrelations(X)) + fill!(_fwd_rs, nothing) + else + Array{Dict{W,U}, 3}(undef, ninstances(X), _nfeatsnaggrs, nrelations(X)) + end + end + ScalarOneStepRelationalMemoset{W,U,FR}(_fwd_rs) + end +end + +# default_fwd_rs_type(::Type{<:AbstractWorld}) = ScalarOneStepRelationalMemoset # TODO implement similar pattern used for fwd + +function hasnans(memoset::ScalarOneStepRelationalMemoset) + # @show any(map(d->(any(_isnan.(collect(values(d))))), memoset.d)) + any(map(d->(any(_isnan.(collect(values(d))))), memoset.d)) +end + +innerstruct(memoset::ScalarOneStepRelationalMemoset) = memoset.d +ninstances(memoset::ScalarOneStepRelationalMemoset) = size(memoset, 1) +nfeatsnaggrs(memoset::ScalarOneStepRelationalMemoset) = size(memoset, 2) +nrelations(memoset::ScalarOneStepRelationalMemoset) = size(memoset, 3) +capacity(memoset::ScalarOneStepRelationalMemoset) = Inf +nmemoizedvalues(memoset::ScalarOneStepRelationalMemoset) = sum(length.(memoset.d)) + +@inline function Base.getindex( + memoset :: ScalarOneStepRelationalMemoset{W,U}, + i_instance :: Integer, + w :: W, + i_featsnaggr :: Integer, + i_relation :: Integer +) where {W<:AbstractWorld,U} + memoset.d[i_instance, i_featsnaggr, i_relation][w] +end +Base.size(memoset::ScalarOneStepRelationalMemoset, args...) = size(memoset.d, args...) + +fwd_rs_init_world_slice(memoset::ScalarOneStepRelationalMemoset{W,U}, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) where {W,U} = + memoset.d[i_instance, i_featsnaggr, i_relation] = Dict{W,U}() +@inline function Base.setindex!(memoset::ScalarOneStepRelationalMemoset{W,U}, threshold::U, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {W,U} + memoset.d[i_instance, i_featsnaggr, i_relation][w] = threshold +end +function instances(memoset::ScalarOneStepRelationalMemoset{W,U,FR}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {W,U,FR} + ScalarOneStepRelationalMemoset{W,U,FR}(if return_view == Val(true) @view memoset.d[inds,:,:] else memoset.d[inds,:,:] end) +end + +############################################################################################ + +# Note: the global memoset is world-agnostic +struct ScalarOneStepGlobalMemoset{ + W<:AbstractWorld, + U, + D<:AbstractArray{U,2} +} <: AbstractScalarOneStepGlobalMemoset{W,U} + + d :: D + + function ScalarOneStepGlobalMemoset{W,U,D}(d::D) where {U,D<:AbstractArray{U,2}} + new{W,U,D}(d) + end + function ScalarOneStepGlobalMemoset{W,U}(d::D) where {U,D<:AbstractArray{U,2}} + ScalarOneStepGlobalMemoset{W,U,D}(d) + end + + function ScalarOneStepGlobalMemoset(X::AbstractLogiset{W,U}) where {W<:AbstractWorld,U} + @assert worldtype(X) != OneWorld "TODO adjust this note: note that you should not use a global memoset when not using global decisions" + _nfeatsnaggrs = nfeatsnaggrs(X) + ScalarOneStepGlobalMemoset{W,U}(Array{U,2}(undef, ninstances(X), _nfeatsnaggrs)) + end +end + +capacity(memoset::ScalarOneStepGlobalMemoset) = prod(size(memoset.d)) +nmemoizedvalues(memoset::ScalarOneStepGlobalMemoset) = sum(memoset.d) +innerstruct(memoset::ScalarOneStepGlobalMemoset) = memoset.d + +# default_fwd_gs_type(::Type{<:AbstractWorld}) = ScalarOneStepGlobalMemoset # TODO implement similar pattern used for fwd + +function hasnans(memoset::ScalarOneStepGlobalMemoset) + # @show any(_isnan.(memoset.d)) + any(_isnan.(memoset.d)) +end + +ninstances(memoset::ScalarOneStepGlobalMemoset) = size(memoset, 1) +nfeatsnaggrs(memoset::ScalarOneStepGlobalMemoset) = size(memoset, 2) +Base.getindex( + memoset :: ScalarOneStepGlobalMemoset, + i_instance :: Integer, + i_featsnaggr :: Integer) = memoset.d[i_instance, i_featsnaggr] +Base.size(memoset::ScalarOneStepGlobalMemoset{U}, args...) where {U} = size(memoset.d, args...) + +Base.setindex!(memoset::ScalarOneStepGlobalMemoset{U}, threshold::U, i_instance::Integer, i_featsnaggr::Integer) where {U} = + memoset.d[i_instance, i_featsnaggr] = threshold +function instances(memoset::ScalarOneStepGlobalMemoset{U}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {U} + ScalarOneStepGlobalMemoset{U}(if return_view == Val(true) @view memoset.d[inds,:] else memoset.d[inds,:] end) +end + +abstract type FeaturedMemoset{U<:Number,W<:AbstractWorld,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,F where F<:AbstractFeature,FR} end + diff --git a/src/datasets/scalar-datasets/test-operators.jl b/src/logisets/base/scalar-conditions/test-operators.jl similarity index 99% rename from src/datasets/scalar-datasets/test-operators.jl rename to src/logisets/base/scalar-conditions/test-operators.jl index 7d242ed..6e4c194 100644 --- a/src/datasets/scalar-datasets/test-operators.jl +++ b/src/logisets/base/scalar-conditions/test-operators.jl @@ -18,9 +18,9 @@ the (binary) test operator function. """ @inline function apply_test_operator( operator::TestOperator, - featval::T, - threshold::T -) where {T} + featval::T1, + threshold::T2 +) where {T1, T2} operator(featval, threshold) end diff --git a/src/datasets/dimensional-datasets/dimensional-features.jl b/src/logisets/base/scalar-conditions/var-features.jl similarity index 51% rename from src/datasets/dimensional-datasets/dimensional-features.jl rename to src/logisets/base/scalar-conditions/var-features.jl index 3873f91..e1a9547 100644 --- a/src/datasets/dimensional-datasets/dimensional-features.jl +++ b/src/logisets/base/scalar-conditions/var-features.jl @@ -1,33 +1,56 @@ -import SoleModels: AbstractFeature, computefeature +import SoleModels: AbstractFeature using SoleData: AbstractDimensionalChannel, channelvariable import Base: isequal, hash, show import SoleLogics: syntaxstring +# Feature brackets +const UVF_OPENING_BRACKET = "[" +const UVF_CLOSING_BRACKET = "]" +# Default prefix for variables +const UVF_VARPREFIX = "V" + +""" + abstract type VarFeature{U} <: AbstractFeature end + +Abstract type for feature functions that can be applied on (multi)variate data. + +See also [`featvaltype`](@ref), [`computefeature`](@ref). +""" +abstract type VarFeature{U} <: AbstractFeature end + +DEFAULT_VARFEATVALTYPE = Real + + +""" + featvaltype(::Type{<:VarFeature{U}}) where {U} = U + featvaltype(::VarFeature{U}) where {U} = U + +Return the output type of the feature function. + +See also [`AbstractWorld`](@ref). """ - abstract type DimensionalFeature{U<:Real} <: AbstractFeature{U} end - -Abstract type for dimensional features, -representing functions that can be computed on dimensional, geometrical worlds. -Dimensional worlds are geometric entity that live in a *dimensional* context; -for example, an `Interval` of a time series. -As an example of a dimensional feature, consider min(V1), -which computes the minimum for variable 1 for a given world. -The value of a feature for a given world can be then evaluated in a `Condition`, - such as: min(V1) >= 10. - -See also [`Interval`](@ref), [`Interval2D`](@ref), -[`GeometricalWorld`](@ref), [`AbstractFeature`](@ref). +featvaltype(::Type{<:VarFeature{U}}) where {U} = U +featvaltype(::VarFeature{U}) where {U} = U + +""" + computefeature(f::VarFeature{U}, channel; kwargs...)::U where {U} + +Compute a feature on a channel (i.e., world reading) of an instance. + +See also [`VarFeature`](@ref). """ -abstract type DimensionalFeature{U<:Real} <: AbstractFeature{U} end +function computefeature(f::VarFeature{U}, channel; kwargs...) where {U} + error("Please, provide method computefeature(::$(typeof(f)), channel::$(typeof(channel)); kwargs...)::U.") +end -# const DimensionalFeatureFunction = FunctionWrapper{Number,Tuple{AbstractArray{<:Number}}} +@inline (f::AbstractFeature)(args...) = computefeature(f, args...) ############################################################################################ """ - struct MultivariateFeature{U} <: DimensionalFeature{U} + struct MultivariateFeature{U} <: VarFeature{U} f::Function end @@ -40,12 +63,12 @@ and "resembling a horse" may require a computation involving all variables. See also [`Interval`](@ref), [`Interval2D`](@ref), [`AbstractUnivariateFeature`](@ref), -[`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). +[`VarFeature`](@ref), [`AbstractFeature`](@ref). """ -struct MultivariateFeature{U} <: DimensionalFeature{U} +struct MultivariateFeature{U} <: VarFeature{U} f::Function end -function computefeature(f::MultivariateFeature{U}, channel::AbstractDimensionalChannel{T})::U where {U<:Real,T} +function computefeature(f::MultivariateFeature{U}, channel::AbstractDimensionalChannel{T})::U where {U,T} (f.f(channel)) end syntaxstring(f::MultivariateFeature, args...; kwargs...) = "$(f.f)" @@ -53,7 +76,7 @@ syntaxstring(f::MultivariateFeature, args...; kwargs...) = "$(f.f)" ############################################################################################ """ - abstract type AbstractUnivariateFeature{U} <: DimensionalFeature{U} end + abstract type AbstractUnivariateFeature{U} <: VarFeature{U} end A dimensional feature represented by the application of a function to a single variable of a dimensional channel. @@ -63,9 +86,9 @@ how much red a `Interval2D` world, when interpreted on an image, contains. See also [`Interval`](@ref), [`Interval2D`](@ref), [`UnivariateFeature`](@ref), -[`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). +[`VarFeature`](@ref), [`AbstractFeature`](@ref). """ -abstract type AbstractUnivariateFeature{U} <: DimensionalFeature{U} end +abstract type AbstractUnivariateFeature{U} <: VarFeature{U} end i_variable(f::AbstractUnivariateFeature) = f.i_variable @@ -110,10 +133,14 @@ function featurename(f::AbstractFeature; kwargs...) error("Please, provide method featurename(::$(typeof(f)); kwargs...).") end -function syntaxstring(f::AbstractUnivariateFeature; kwargs...) +function syntaxstring( + f::AbstractUnivariateFeature; + opening_bracket::String = UVF_OPENING_BRACKET, + closing_bracket::String = UVF_CLOSING_BRACKET, + kwargs... +) n = variable_name(f; kwargs...) - "" - "$(featurename(f))$UVF_OPENING_BRACKET$n$UVF_CLOSING_BRACKET" + "$(featurename(f))$opening_bracket$n$closing_bracket" end ############################################################################################ @@ -132,13 +159,13 @@ how much red a `Interval2D` world, when interpreted on an image, contains. See also [`Interval`](@ref), [`Interval2D`](@ref), [`AbstractUnivariateFeature`](@ref), -[`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). +[`VarFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateFeature{U} <: AbstractUnivariateFeature{U} i_variable::Integer f::Function end -function computefeature(f::UnivariateFeature{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} +function computefeature(f::UnivariateFeature{U}, channel::AbstractDimensionalChannel{T}) where {U,T} (f.f(SoleBase.vectorize(channelvariable(channel, f.i_variable));))::U end featurename(f::UnivariateFeature) = string(f.f) @@ -154,7 +181,7 @@ A univariate feature solely identified by its name and reference variable. See also [`Interval`](@ref), [`Interval2D`](@ref), [`AbstractUnivariateFeature`](@ref), -[`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). +[`VarFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateNamedFeature{U} <: AbstractUnivariateFeature{U} i_variable::Integer @@ -167,8 +194,6 @@ featurename(f::UnivariateNamedFeature) = f.name ############################################################################################ -############################################################################################ - """ struct UnivariateMin{U} <: AbstractUnivariateFeature{U} i_variable::Integer @@ -180,7 +205,7 @@ See also [`Interval`](@ref), [`Interval2D`](@ref), [`AbstractUnivariateFeature`](@ref), [`UnivariateMax`](@ref), -[`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). +[`VarFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateMin{U} <: AbstractUnivariateFeature{U} i_variable::Integer @@ -188,9 +213,9 @@ struct UnivariateMin{U} <: AbstractUnivariateFeature{U} return new{U}(i_variable) end function UnivariateMin(i_variable::Integer) - @warn "Please specify the type of the feature for UnivariateMin." * - " For example: UnivariateMin{Float64}($(i_variable))." - return UnivariateMin{Real}(i_variable) + @warn "Please specify the feature value type for UnivariateMin. " * + "For example: UnivariateMin{Float64}($(i_variable))." + return UnivariateMin{DEFAULT_VARFEATVALTYPE}(i_variable) end end function computefeature(f::UnivariateMin{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} @@ -209,7 +234,7 @@ See also [`Interval`](@ref), [`Interval2D`](@ref), [`AbstractUnivariateFeature`](@ref), [`UnivariateMin`](@ref), -[`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). +[`VarFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateMax{U} <: AbstractUnivariateFeature{U} i_variable::Integer @@ -217,9 +242,9 @@ struct UnivariateMax{U} <: AbstractUnivariateFeature{U} return new{U}(i_variable) end function UnivariateMax(i_variable::Integer) - @warn "Please specify the type of the feature for UnivariateMax." * - " For example: UnivariateMax{Float64}($(i_variable))." - return UnivariateMax{Real}(i_variable) + @warn "Please specify the feature value type for UnivariateMax. " * + "For example: UnivariateMax{Float64}($(i_variable))." + return UnivariateMax{DEFAULT_VARFEATVALTYPE}(i_variable) end end function computefeature(f::UnivariateMax{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} @@ -242,7 +267,7 @@ See also [`Interval`](@ref), [`Interval2D`](@ref), [`AbstractUnivariateFeature`](@ref), [`UnivariateMin`](@ref), -[`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). +[`VarFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateSoftMin{U,T<:AbstractFloat} <: AbstractUnivariateFeature{U} i_variable::Integer @@ -272,7 +297,7 @@ See also [`Interval`](@ref), [`Interval2D`](@ref), [`AbstractUnivariateFeature`](@ref), [`UnivariateMax`](@ref), -[`DimensionalFeature`](@ref), [`AbstractFeature`](@ref). +[`VarFeature`](@ref), [`AbstractFeature`](@ref). """ struct UnivariateSoftMax{U,T<:AbstractFloat} <: AbstractUnivariateFeature{U} i_variable::Integer @@ -303,3 +328,140 @@ end is_collapsing_univariate_feature(f::Union{UnivariateMin,UnivariateMax,UnivariateSoftMin,UnivariateSoftMax}) = true is_collapsing_univariate_feature(f::UnivariateFeature) = (f.f in [minimum, maximum, mean]) + +_st_featop_abbr(f::UnivariateMin, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) ⪴" +_st_featop_abbr(f::UnivariateMax, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) ⪳" +_st_featop_abbr(f::UnivariateSoftMin, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) $("⪴" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" +_st_featop_abbr(f::UnivariateSoftMax, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) $("⪳" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" + +_st_featop_abbr(f::UnivariateMin, ::typeof(<); kwargs...) = "$(variable_name(f; kwargs...)) ⪶" +_st_featop_abbr(f::UnivariateMax, ::typeof(>); kwargs...) = "$(variable_name(f; kwargs...)) ⪵" +_st_featop_abbr(f::UnivariateSoftMin, ::typeof(<); kwargs...) = "$(variable_name(f; kwargs...)) $("⪶" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" +_st_featop_abbr(f::UnivariateSoftMax, ::typeof(>); kwargs...) = "$(variable_name(f; kwargs...)) $("⪵" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" + +_st_featop_abbr(f::UnivariateMin, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) ↘" +_st_featop_abbr(f::UnivariateMax, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) ↗" +_st_featop_abbr(f::UnivariateSoftMin, ::typeof(≤); kwargs...) = "$(variable_name(f; kwargs...)) $("↘" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" +_st_featop_abbr(f::UnivariateSoftMax, ::typeof(≥); kwargs...) = "$(variable_name(f; kwargs...)) $("↗" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" + +############################################################################################ + +import SoleModels: parsefeature + +using StatsBase + +""" +Syntaxstring aliases for specific features +""" +const BASE_FEATURE_ALIASES = Dict{String,Union{Type,Function}}( + # + "minimum" => UnivariateMin, + "min" => UnivariateMin, + "maximum" => UnivariateMax, + "max" => UnivariateMax, + # + "avg" => StatsBase.mean, + "mean" => StatsBase.mean, +) + +function parsefeature( + ::Type{F}, + expression::String; + featvaltype::Union{Nothing,Type} = nothing, + kwargs... +) where {F<:VarFeature} + if isnothing(featvaltype) + featvaltype = DEFAULT_VARFEATVALTYPE + @warn "Please, specify a type for the feature values (featvaltype = ...). " * + "$(featvaltype) will be used, but note that this may raise type errors. " * + "(expression = $(repr(expression)))" + end + _parsefeature(F{featvaltype}, expression; kwargs...) +end + +function parsefeature( + ::Type{F}, + expression::String; + featvaltype::Union{Nothing,Type} = nothing, + kwargs... +) where {U,F<:VarFeature{U}} + @assert isnothing(featvaltype) || featvaltype == U "Cannot parse feature of type $(F) with " * + "featvaltype = $(featvaltype). (expression = $(repr(expression)))" + _parsefeature(F, expression; kwargs...) +end + +function _parsefeature( + ::Type{F}, + expression::String; + opening_bracket::String = UVF_OPENING_BRACKET, + closing_bracket::String = UVF_CLOSING_BRACKET, + custom_feature_aliases = Dict{String,Union{Type,Function}}(), + variable_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, + variable_name_prefix::Union{Nothing,String} = nothing, + kwargs... +) where {U,F<:VarFeature{U}} + @assert isnothing(variable_names_map) || isnothing(variable_name_prefix) "" * + "Cannot parse variable with both variable_names_map and variable_name_prefix. " * + "(expression = $(repr(expression)))" + + featvaltype = U + + @assert length(opening_bracket) == 1 || length(closing_bracket) + "Brackets must be single-character strings! " * + "$(repr(opening_bracket)) and $(repr(closing_bracket)) encountered." + + featdict = merge(BASE_FEATURE_ALIASES, custom_feature_aliases) + + variable_name_prefix = isnothing(variable_name_prefix) && + isnothing(variable_names_map) ? UVF_VARPREFIX : variable_name_prefix + variable_name_prefix = isnothing(variable_name_prefix) ? "" : variable_name_prefix + + r = Regex("^\\s*(\\w+)\\s*\\$(opening_bracket)\\s*$(variable_name_prefix)(\\S+)\\s*\\$(closing_bracket)\\s*\$") + slices = match(r, expression) + + # Assert for malformed strings (e.g. "123.4250.2") + @assert !isnothing(slices) && length(slices) == 2 "Could not parse variable " * + "feature from expression $(repr(expression))." + + slices = string.(slices) + (_feature, _variable) = (slices[1], slices[2]) + + feature = begin + i_var = begin + if isnothing(variable_names_map) + parse(Int, _variable) + elseif variable_names_map isa Union{AbstractDict,AbstractVector} + i_var = findfirst(variable_names_map, variable) + @assert !isnothing(i_var) "Could not find variable $variable in the " * + "specified map. ($(@show variable_names_map))" + else + error("Unexpected variable_names_map of type $(typeof(variable_names_map)) " * + "encountered.") + end + end + if haskey(featdict, _feature) + # If it is a known feature get it as + # a type (e.g., `UnivariateMin`), or Julia function (e.g., `minimum`). + feat_or_fun = featdict[_feature] + # If it is a function, wrap it into a UnivariateFeature + # otherwise, it is a feature, and it is used as a constructor. + if feat_or_fun isa Function + UnivariateFeature{featvaltype}(i_var, feat_or_fun) + else + feat_or_fun{featvaltype}(i_var) + end + else + # If it is not a known feature, interpret it as a Julia function, + # and wrap it into a UnivariateFeature. + f = eval(Meta.parse(_feature)) + UnivariateFeature{featvaltype}(i_var, f) + end + end + + # if !(feature isa F) + # @warn "Could not parse expression $(repr(expression)) as feature of type $(F); " * + # " $(typeof(feature)) was used." + # end + + return feature +end diff --git a/src/logisets/base/supported-logiset.jl b/src/logisets/base/supported-logiset.jl new file mode 100644 index 0000000..de2d4fc --- /dev/null +++ b/src/logisets/base/supported-logiset.jl @@ -0,0 +1,209 @@ +""" +A logiset associated to a number of cascading memoization structures, that are used +when checking formulas. + +# Examples +TODO add example showing that checking is faster when using this structure. + +See also +[`SuportedLogiset`](@ref), +[`AbstractMemoset`](@ref), +[`AbstractLogiset`](@ref). +""" +struct SupportedLogiset{ + L<:AbstractLogiset, + N, + MS<:NTuple{N,AbstractMemoset}, +} <: AbstractLogiset{W where W<:AbstractWorld,U where U,F where F<:AbstractFeature,FR where FR<:AbstractFrame{W where W<:AbstractWorld}} + + # Core dataset + base :: L + # Support structures + supports :: MS + + function SupportedLogiset( + base::L, + supports::_MS + ) where {L<:AbstractLogiset,_N,_MS<:NTuple{_N,<:Union{<:AbstractVector{<:AbstractDict},<:AbstractMemoset,<:SupportedLogiset}}} + @assert !(base isa SupportedLogiset) "Cannot instantiate SupportedLogiset " * + "with a SupportedLogiset base." + if length(supports) == 0 + supports = (Memoset(base),) + end + supports = Tuple(vcat(map(supp->begin + if supp isa AbstractVector{<:AbstractDict{<:AbstractFormula,<:WorldSet}} + [Memoset(supp)] + elseif supp isa SupportedLogiset + @assert base == SoleModels.base(supp) "Cannot inherit supports from " * + "SupportedLogiset with different base." + collect(SoleModels.supports(supp)) + else + [supp] + end + end, supports)...)) + + @assert !(any(isa.(supports, SupportedLogiset))) "Cannot instantiate " * + "SupportedLogiset with a SupportedLogiset support." + for (i_supp,supp) in enumerate(supports) + @assert worldtype(base) <: worldtype(supp) "Cannot instantiate " * + "SupportedLogiset with unsupported worldtypes for $(i_supp)-th support: " * + "$(worldtype(base)) <: $(worldtype(supp)) should hold." + @assert featvaltype(base) <: featvaltype(supp) "Cannot instantiate " * + "SupportedLogiset with unsupported featvaltypes for $(i_supp)-th support: " * + "$(featvaltype(base)) <: $(featvaltype(supp)) should hold." + @assert featuretype(base) <: featuretype(supp) "Cannot instantiate " * + "SupportedLogiset with unsupported featuretypes for $(i_supp)-th support: " * + "$(featuretype(base)) <: $(featuretype(supp)) should hold." + @assert frametype(base) <: frametype(supp) "Cannot instantiate " * + "SupportedLogiset with unsupported frametypes for $(i_supp)-th support: " * + "$(frametype(base)) <: $(frametype(supp)) should hold." + end + _fullmemo = usesfullmemo.(supports) + @assert sum(_fullmemo) <= 1 "Cannot instantiate SupportedLogiset with " * + "more than one full memoization set. $(sum(_fullmemo)) were provided." * + "$(@show typeof.(supports))" + + @assert all((!).(_fullmemo)) || (last(_fullmemo) && all((!).(_fullmemo[1:end-1]))) "" * + "Please, provide cascading supports so that the full memoization set appears " * + "last. $(@show typeof.(supports))" + + N = length(supports) + MS = typeof(supports) + new{L,N,MS}(base, supports) + end + + function SupportedLogiset( + base::AbstractLogiset, + supports::AbstractVector{<:Union{AbstractVector{<:AbstractDict},AbstractMemoset,SupportedLogiset}} + ) + SupportedLogiset(base, Tuple(supports)) + end + + # Helper + function SupportedLogiset( + base::AbstractLogiset, + supports::Union{<:AbstractVector{<:AbstractDict},<:AbstractMemoset,<:SupportedLogiset}... + ) + SupportedLogiset(base, supports) + end +end + +base(X::SupportedLogiset) = X.base +supports(X::SupportedLogiset) = X.supports + +nsupports(X::SupportedLogiset) = length(X.supports) + +capacity(X::SupportedLogiset) = sum(capacity.(supports(X))) +nmemoizedvalues(X::SupportedLogiset) = sum(nmemoizedvalues.(supports(X))) + + +function featvalue( + X::SupportedLogiset, + i_instance::Integer, + w::W, + f::AbstractFeature, +) where {W<:AbstractWorld} + featvalue(base(X), i_instance, w, f) +end + +frame(X::SupportedLogiset, i_instance::Integer) = frame(base(X), i_instance) + +ninstances(X::SupportedLogiset) = ninstances(base(X)) + +function allfeatvalues( + X::SupportedLogiset, +) + allfeatvalues(base(X)) +end + +function allfeatvalues( + X::SupportedLogiset, + i_instance::Integer, +) + allfeatvalues(base(X), i_instance) +end + +function allfeatvalues( + X::SupportedLogiset, + i_instance::Integer, + feature::AbstractFeature, +) + allfeatvalues(base(X), i_instance, feature) +end + + +usesfullmemo(X::SupportedLogiset) = usesfullmemo(last(supports(X))) +fullmemo(X::SupportedLogiset) = usesfullmemo(X) ? last(supports(X)) : error("This " * + "dataset does not have a full memoization set.") + +isminifiable(X::SupportedLogiset) = isminifiable(base(X)) && all(isminifiable.(supports(X))) + +function minify(X::SL) where {SL<:SupportedLogiset} + (new_sl, new_supports...), backmap = + minify([ + base(X), + supports(X)..., + ]) + + X = SL( + new_sl, + new_supports, + ) + X, backmap +end + +hasnans(X::SupportedLogiset) = hasnans(base(X)) || any(hasnans.(supports(X))) + +function instances( + X::SupportedLogiset, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false); + kwargs... +) + _instances = (X)->instances(X, inds, return_view; kwargs...) + SupportedLogiset( + _instances(base(X)), + [_instances(supp) for supp in supports(X)]..., + ) +end + +function concatdatasets(Xs::SupportedLogiset...) + SupportedLogiset( + concatdatasets([base(X) for X in Xs]), + [concatdatasets([supports(X)[i_supp] for X in Xs]...) for i_supp in 1:nsupports(Xs)]..., + ) +end + +function displaystructure(X::SupportedLogiset; indent_str = "", include_ninstances = true) + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))-1) + pieces = [] + push!(pieces, " \n") + if include_ninstances + push!(pieces, " " * padattribute("# instances:", "$(ninstances(X))\n")) + end + push!(pieces, " " * padattribute("usesfullmemo:", "$(usesfullmemo(X))\n")) + push!(pieces, " base:\n") + push!(pieces, " " * displaystructure(base(X); indent_str = "$(indent_str)│ ", include_ninstances = false)) + push!(pieces, " " * padattribute("# supports:", "$(nsupports(X))\n")) + for (i_supp,supp) in enumerate(supports(X)) + push!(pieces, "[$(i_supp)] $(displaystructure(supp; indent_str = (i_supp == nsupports(X) ? "$(indent_str) " : "$(indent_str)│ "), include_ninstances = false))\n") + end + return "SupportedLogiset ($(humansize(X)))" * + join(pieces, "$(indent_str)├", "$(indent_str)└") * "\n" +end + +# ############################################################################################ + +# Base.getindex(X::SupportedLogiset, args...) = Base.getindex(base(X), args...)::featvaltype(X) +# Base.size(X::SupportedLogiset) = (size(base(X)), size(support(X))) +# features(X::SupportedLogiset) = features(base(X)) +# grouped_featsaggrsnops(X::SupportedLogiset) = grouped_featsaggrsnops(base(X)) +# grouped_featsnaggrs(X::SupportedLogiset) = grouped_featsnaggrs(base(X)) +# nfeatures(X::SupportedLogiset) = nfeatures(base(X)) +# nrelations(X::SupportedLogiset) = nrelations(base(X)) +# ninstances(X::SupportedLogiset) = ninstances(base(X)) +# relations(X::SupportedLogiset) = relations(base(X)) +# fwd(X::SupportedLogiset) = fwd(base(X)) +# worldtype(X::SupportedLogiset{V,W}) where {V,W} = W + +# usesmemo(X::SupportedLogiset) = usesmemo(support(X)) diff --git a/src/datasets/dimensional-datasets/parse-dimensional-condition.jl b/src/logisets/dimensional-logisets/_parse-dimensional-condition.jl similarity index 82% rename from src/datasets/dimensional-datasets/parse-dimensional-condition.jl rename to src/logisets/dimensional-logisets/_parse-dimensional-condition.jl index 6fc5957..4adc58f 100644 --- a/src/datasets/dimensional-datasets/parse-dimensional-condition.jl +++ b/src/logisets/dimensional-logisets/_parse-dimensional-condition.jl @@ -31,12 +31,11 @@ const BASE_FEATURE_ALIASES = Dict{String,Union{Type,Function}}( "mean" => StatsBase.mean, ) -DEFAULT_FEATVALTYPE = Float64 - """ parsecondition( + ::Type{ScalarCondition{VarFeature}}, expression::String; - featvaltype::Type = $(DEFAULT_FEATVALTYPE), + featvaltype::Type = $(DEFAULT_VARFEATVALTYPE), opening_bracket::String = $(repr(UVF_OPENING_BRACKET)), closing_bracket::String = $(repr(UVF_CLOSING_BRACKET)), custom_feature_aliases = Dict{String,Union{Type,Function}}(), @@ -79,10 +78,10 @@ parsing of the variable name; please, refer to `variable_name` for their behavio # Examples ```julia-repl -julia> syntaxstring(SoleModels.parsecondition("min[V1] <= 32")) +julia> syntaxstring(SoleModels.parsecondition(ScalarCondition{VarFeature}, "min[V1] <= 32")) "min[V1] <= 32.0" -julia> syntaxstring(parseformulatree("min[V1] <= 15 ∧ max[V1] >= 85"; proposition_parser=(x)->parsecondition(x; featvaltype = Int64,))) +julia> syntaxstring(parseformulatree("min[V1] <= 15 ∧ max[V1] >= 85"; proposition_parser=(x)->parsecondition(ScalarCondition{VarFeature}, x; featvaltype = Int64,))) "min[V1] <= 15 ∧ max[V1] >= 85" ``` @@ -94,6 +93,7 @@ See also [`syntaxstring`](@ref). """ function parsecondition( + ::Type{ScalarCondition{VarFeature}}, expression::String; featvaltype::Union{Nothing,Type} = nothing, opening_bracket::String = UVF_OPENING_BRACKET, @@ -103,19 +103,19 @@ function parsecondition( variable_name_prefix::Union{Nothing,String} = nothing, ) @assert isnothing(variable_names_map) || isnothing(variable_name_prefix) "" * - "Cannot parse variable with both variable_names_map and variable_name_prefix." * - " (expression = $(repr(expression)))" + "Cannot parse variable with both variable_names_map and variable_name_prefix. " * + "(expression = $(repr(expression)))" if isnothing(featvaltype) - featvaltype = DEFAULT_FEATVALTYPE - @warn "Please, specify a type for the feature values (featvaltype = ...)." * - " $(featvaltype) will be used, but note that this may raise type errors." * - " (expression = $(repr(expression)))" + featvaltype = DEFAULT_VARFEATVALTYPE + @warn "Please, specify a type for the feature values (featvaltype = ...). " * + "$(featvaltype) will be used, but note that this may raise type errors. " * + "(expression = $(repr(expression)))" end @assert length(opening_bracket) == 1 || length(closing_bracket) - "Brackets must be single-character strings!" * - " $(repr(opening_bracket)) and $(repr(closing_bracket)) encountered." + "Brackets must be single-character strings! " * + "$(repr(opening_bracket)) and $(repr(closing_bracket)) encountered." featdict = merge(BASE_FEATURE_ALIASES, custom_feature_aliases) @@ -133,12 +133,13 @@ function parsecondition( r = Regex("^\\s*(\\w+)\\s*\\$(opening_bracket)\\s*$(variable_name_prefix)(\\S+)\\s*\\$(closing_bracket)\\s*([^\\s\\d]+)\\s*(\\S+)\\s*\$") # r = Regex("^\\s*(\\w+)\\s*\\$(opening_bracket)\\s*$(variable_name_prefix)(\\S+)\\s*\\$(closing_bracket)\\s*(\\S+)\\s+(\\S+)\\s*\$") - slices = string.(match(r, expression)) + slices = match(r, expression) # Assert for malformed strings (e.g. "123.4250.2") - @assert length(slices) == 4 "Could not parse condition from" * - " expression $(repr(expression))." + @assert !isnothing(slices) && length(slices) == 4 "Could not parse condition from " * + "expression $(repr(expression))." + slices = string.(slices) (slices[1], slices[2], slices[3], slices[4]) end @@ -146,9 +147,9 @@ function parsecondition( if isconcretetype(featvaltype) threshold = tryparse(featvaltype, _threshold) if isnothing(threshold) - error("Could not parse condition from" * - " expression `$expression`: could not parse" * - " $(repr(_threshold)) as $(featvaltype)") + error("Could not parse condition from " * + "expression `$expression`: could not parse " * + "$(repr(_threshold)) as $(featvaltype)") end threshold, featvaltype else @@ -156,13 +157,13 @@ function parsecondition( # threshold = isnothing(threshold) ? tryparse(Int, _threshold) : threshold threshold = isnothing(threshold) ? tryparse(Float64, _threshold) : threshold if threshold isa featvaltype - @warn "Please, specify a concrete type for the feature values" * - " (featvaltype = ...); $(typeof(threshold)) was inferred." + @warn "Please, specify a concrete type for the feature values " * + "(featvaltype = ...); $(typeof(threshold)) was inferred." else - error("Could not correctly infer feature value type from" * - " threshold $(repr(_threshold)) ($(typeof(threshold)) was inferred)." * - " Please, specify a concrete type for the feature values" * - " (featvaltype = ...).") + error("Could not correctly infer feature value type from " * + "threshold $(repr(_threshold)) ($(typeof(threshold)) was inferred). " * + "Please, specify a concrete type for the feature values " * + "(featvaltype = ...).") end threshold, typeof(threshold) end @@ -175,8 +176,8 @@ function parsecondition( elseif variable_names_map isa Union{AbstractDict,AbstractVector} findfirst(variable_names_map, variable) else - error("Unexpected variable_names_map of type $(typeof(variable_names_map))" * - " encountered.") + error("Unexpected variable_names_map of type $(typeof(variable_names_map)) " * + "encountered.") end end if haskey(featdict, _feature) @@ -199,7 +200,6 @@ function parsecondition( end test_operator = eval(Meta.parse(_test_operator)) - metacond = ScalarMetaCondition(feature, test_operator) - return ScalarCondition(metacond, threshold) + return ScalarCondition(feature, test_operator, threshold) end diff --git a/src/datasets/base/logiset.jl b/src/logisets/dimensional-logisets/active-logiset.jl similarity index 91% rename from src/datasets/base/logiset.jl rename to src/logisets/dimensional-logisets/active-logiset.jl index 3592e1f..c1a92f9 100644 --- a/src/datasets/base/logiset.jl +++ b/src/logisets/dimensional-logisets/active-logiset.jl @@ -69,7 +69,7 @@ end # # The most generic featstruct structure is a matrix of dictionaries of size (ninstances × nfeatures) # struct GenericFWD{ # V, -# F<:AbstractFeature{V}, +# F<:AbstractFeature, # W<:AbstractWorld, # FR<:AbstractFrame{W}, # D<:AbstractVector{<:AbstractDict{<:W,<:AbstractVector{<:V}}} @@ -117,7 +117,7 @@ end # end # # A function for slicing the dataset -# function _slice_dataset(featstruct::GenericFWD{V}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {V} +# function instances(featstruct::GenericFWD{V}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {V} # GenericFWD{V}(if return_view == Val(true) @view featstruct.d[inds] else featstruct.d[inds] end, featstruct.nfeatures) # end @@ -141,8 +141,8 @@ It stores the feature values in a lookup table. struct Logiset{ V, W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - F<:AbstractFeature{V}, + FR<:AbstractFrame{W}, + F<:AbstractFeature, FWD<:AbstractFeatureLookupSet{V,FR}, } <: AbstractLogiset{W,V,F,Bool,FR} @@ -165,7 +165,7 @@ struct Logiset{ ; allow_no_instances = false, initialworld = nothing, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FWD<:AbstractFeatureLookupSet{V,FR},F<:AbstractFeature{V}} + ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W},FWD<:AbstractFeatureLookupSet{V,FR},F<:AbstractFeature} features = collect(features) ty = "Logiset{$(V),$(W),$(FR),$(F)}" @assert allow_no_instances || ninstances(featstruct) > 0 "Can't instantiate $(ty) with no instance. (featstruct's type $(typeof(featstruct)))" @@ -190,7 +190,7 @@ struct Logiset{ features :: AbstractVector{<:AbstractFeature}, args...; kwargs... - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FWD<:AbstractFeatureLookupSet{V,FR}} + ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W},FWD<:AbstractFeatureLookupSet{V,FR}} features = collect(features) F = Union{typeof.(features)...} features = Vector{F}(features) @@ -201,7 +201,7 @@ struct Logiset{ featstruct :: AbstractFeatureLookupSet{V,FR}, args...; kwargs... - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} + ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} Logiset{V,W,FR}(featstruct, args...; kwargs...) end @@ -209,7 +209,7 @@ struct Logiset{ featstruct :: AbstractFeatureLookupSet{V,FR}, args...; kwargs..., - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} + ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} Logiset{V,W}(featstruct, args...; kwargs...) end end @@ -234,15 +234,15 @@ nrelations(X::Logiset) = length(relations(X)) ninstances(X::Logiset) = ninstances(featstruct(X)) worldtype(X::Logiset{V,W}) where {V,W<:AbstractWorld} = W -frame(X::Logiset, i_instance) = frame(featstruct(X), i_instance) +frame(X::Logiset, i_instance::Integer) = frame(featstruct(X), i_instance) initialworld(X::Logiset) = X.initialworld -function initialworld(X::Logiset, i_instance) +function initialworld(X::Logiset, i_instance::Integer) initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_instance] : initialworld(X) end -function _slice_dataset(X::Logiset, inds::AbstractVector{<:Integer}, args...; kwargs...) +function instances(X::Logiset, inds::AbstractVector{<:Integer}, args...; kwargs...) Logiset( - _slice_dataset(featstruct(X), inds, args...; kwargs...), + instances(featstruct(X), inds, args...; kwargs...), features(X), relations(X), initialworld = initialworld(X) @@ -250,7 +250,7 @@ function _slice_dataset(X::Logiset, inds::AbstractVector{<:Integer}, args...; kw end function displaystructure(X::Logiset; indent_str = "") - out = "$(typeof(X))\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" + out = "$(typeof(X))\t$(humansize(X))\n" out *= indent_str * "├ features:\t\t$((length(features(X))))\t$(features(X))\n" out *= indent_str * "├ relations:\t\t$((length(relations(X))))\t$(relations(X))\n" out *= indent_str * "├ featstruct:\t\t$(typeof(featstruct(X)))\t$(Base.summarysize(featstruct(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" diff --git a/src/datasets/dimensional-datasets/datasets/dimensional-fwds.jl b/src/logisets/dimensional-logisets/datasets/dimensional-fwds.jl similarity index 92% rename from src/datasets/dimensional-datasets/datasets/dimensional-fwds.jl rename to src/logisets/dimensional-logisets/datasets/dimensional-fwds.jl index e8b4344..e4fa8ea 100644 --- a/src/datasets/dimensional-datasets/datasets/dimensional-fwds.jl +++ b/src/logisets/dimensional-logisets/datasets/dimensional-fwds.jl @@ -6,10 +6,10 @@ import Base: size, ndims, getindex, setindex! ############################################################################################ ############################################################################################ -abstract type AbstractUniformFullDimensionalFWD{T,N,W<:AbstractWorld,FR<:FullDimensionalFrame{N,W,Bool}} <: AbstractFeatureLookupSet{T,W,FR} end +abstract type AbstractUniformFullDimensionalFWD{T,N,W<:AbstractWorld,FR<:FullDimensionalFrame{N,W}} <: AbstractFeatureLookupSet{T,W,FR} end channel_size(fwd::AbstractUniformFullDimensionalFWD) = error("TODO add message inviting to add channel_size") -frame(fwd::AbstractUniformFullDimensionalFWD, i_instance) = FullDimensionalFrame(channel_size(fwd)) +frame(fwd::AbstractUniformFullDimensionalFWD, i_instance::Integer) = FullDimensionalFrame(channel_size(fwd)) ############################################################################################ ############################################################################################ @@ -19,7 +19,7 @@ struct UniformFullDimensionalFWD{ W<:AbstractWorld, N, D<:AbstractArray{T}, - FR<:FullDimensionalFrame{N,W,Bool}, + FR<:FullDimensionalFrame{N,W}, G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, } <: AbstractUniformFullDimensionalFWD{T,N,W,FR} @@ -34,12 +34,12 @@ struct UniformFullDimensionalFWD{ grouped_featsnaggrs :: G2 - function UniformFullDimensionalFWD{T,W,N,D,FR}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{T},FR<:FullDimensionalFrame{N,W,Bool}} + function UniformFullDimensionalFWD{T,W,N,D,FR}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{T},FR<:FullDimensionalFrame{N,W}} new{T,W,N,D,FR}(d) end function UniformFullDimensionalFWD{T,W,N}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{T}} - new{T,W,N,D,FullDimensionalFrame{N,W,Bool}}(d) + new{T,W,N,D,FullDimensionalFrame{N,W}}(d) end ############################################################################################ @@ -155,15 +155,15 @@ end ############################################################################################ -function _slice_dataset(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T,W<:OneWorld,N} +function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:OneWorld,N} UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[inds,:] else fwd.d[inds,:] end) end -function _slice_dataset(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T,W<:Interval,N} +function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:Interval,N} UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[:,:,inds,:] else fwd.d[:,:,inds,:] end) end -function _slice_dataset(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T,W<:Interval2D,N} +function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:Interval2D,N} UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[:,:,:,:,inds,:] else fwd.d[:,:,:,:,inds,:] end) end @@ -236,7 +236,7 @@ const FWDFeatureSlice{T} = Union{ # fwd.d[i_instance, i_feature] = threshold # end -# function _slice_dataset(fwd::OneWorldFWD{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(fwd::OneWorldFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # OneWorldFWD{T}(if return_view == Val(true) @view fwd.d[inds,:] else fwd.d[inds,:] end) # end @@ -289,7 +289,7 @@ const FWDFeatureSlice{T} = Union{ # fwd.d[:, :, :, i_feature] = fwdslice # end -# function _slice_dataset(fwd::IntervalFWD{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(fwd::IntervalFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # IntervalFWD{T}(if return_view == Val(true) @view fwd.d[:,:,inds,:] else fwd.d[:,:,inds,:] end) # end # Base.@propagate_inbounds @inline fwdread_channel(fwd::IntervalFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = @@ -339,7 +339,7 @@ const FWDFeatureSlice{T} = Union{ # fwd.d[:, :, :, :, :, i_feature] = fwdslice # end -# function _slice_dataset(fwd::Interval2DFWD{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(fwd::Interval2DFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # Interval2DFWD{T}(if return_view == Val(true) @view fwd.d[:,:,:,:,inds,:] else fwd.d[:,:,:,:,inds,:] end) # end # Base.@propagate_inbounds @inline fwdread_channel(fwd::Interval2DFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = diff --git a/src/datasets/dimensional-datasets/datasets/dimensional-logiset.jl b/src/logisets/dimensional-logisets/datasets/dimensional-logiset.jl similarity index 91% rename from src/datasets/dimensional-datasets/datasets/dimensional-logiset.jl rename to src/logisets/dimensional-logisets/datasets/dimensional-logiset.jl index 32085ad..1fb7d29 100644 --- a/src/datasets/dimensional-datasets/datasets/dimensional-logiset.jl +++ b/src/logisets/dimensional-logisets/datasets/dimensional-logiset.jl @@ -17,7 +17,7 @@ struct DimensionalLogiset{ FT<:AbstractFeature{V}, G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, -} <: AbstractActiveScalarLogiset{W,V,FT,Bool,FullDimensionalFrame{N,W,Bool}} +} <: AbstractActiveScalarLogiset{W,V,FT,Bool,FullDimensionalFrame{N,W}} # Core data (a dimensional domain) domain :: D @@ -52,9 +52,9 @@ struct DimensionalLogiset{ @assert allow_no_instances || ninstances(domain) > 0 "" * "Can't instantiate $(ty) with no instance. (domain's type $(typeof(domain)))" @assert length(features) == length(grouped_featsaggrsnops) "" * - "Can't instantiate $(ty) with mismatching length(features) and" * - " length(grouped_featsaggrsnops):" * - " $(length(features)) != $(length(grouped_featsaggrsnops))" + "Can't instantiate $(ty) with mismatching length(features) and " * + "length(grouped_featsaggrsnops): " * + "$(length(features)) != $(length(grouped_featsaggrsnops))" @assert length(grouped_featsaggrsnops) > 0 && sum(length.(grouped_featsaggrsnops)) > 0 && sum(vcat([[length(test_ops) for test_ops in aggrs] for aggrs in grouped_featsaggrsnops]...)) > 0 "" * @@ -101,9 +101,9 @@ struct DimensionalLogiset{ ) where {V,N,W<:AbstractWorld} domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{N,W}(domain) : domain) - @assert all(isa.(mixed_features, MixedFeature)) "Unknown feature encountered!" * - " $(filter(f->!isa(f, MixedFeature), mixed_features)), " * - " $(typeof.(filter(f->!isa(f, MixedFeature), mixed_features)))" + @assert all(isa.(mixed_features, MixedFeature)) "Unknown feature encountered! " * + "$(filter(f->!isa(f, MixedFeature), mixed_features)), " * + "$(typeof.(filter(f->!isa(f, MixedFeature), mixed_features)))" mixed_features = Vector{MixedFeature}(mixed_features) @@ -135,10 +135,10 @@ struct DimensionalLogiset{ ) @assert length(readymade_cfs) + length(variable_specific_cfs) == length(mixed_features) "" * - "Unexpected" * - " mixed_features. $(mixed_features)." * - " $(filter(x->(! (x in readymade_cfs) && ! (x in variable_specific_cfs)), mixed_features))." * - " $(length(readymade_cfs)) + $(length(variable_specific_cfs)) == $(length(mixed_features))." + "Unexpected " * + "mixed_features. $(mixed_features). " * + "$(filter(x->(! (x in readymade_cfs) && ! (x in variable_specific_cfs)), mixed_features)). " * + "$(length(readymade_cfs)) + $(length(variable_specific_cfs)) == $(length(mixed_features))." for (test_ops,cf) in readymade_cfs push!(_features, cf) @@ -246,17 +246,17 @@ max_channel_size(X::DimensionalLogiset) = max_channel_size(domain(X)) get_instance(X::DimensionalLogiset, args...) = get_instance(domain(X), args...) -_slice_dataset(X::DimensionalLogiset, inds::AbstractVector{<:Integer}, args...; kwargs...) = - DimensionalLogiset(_slice_dataset(domain(X), inds, args...; kwargs...), ontology(X), features(X), grouped_featsaggrsnops(X); initialworld = initialworld(X)) +instances(X::DimensionalLogiset, inds::AbstractVector{<:Integer}, args...; kwargs...) = + DimensionalLogiset(instances(domain(X), inds, args...; kwargs...), ontology(X), features(X), grouped_featsaggrsnops(X); initialworld = initialworld(X)) -frame(X::DimensionalLogiset, i_instance) = frame(domain(X), i_instance) +frame(X::DimensionalLogiset, i_instance::Integer) = frame(domain(X), i_instance) initialworld(X::DimensionalLogiset) = X.initialworld -function initialworld(X::DimensionalLogiset, i_instance) +function initialworld(X::DimensionalLogiset, i_instance::Integer) initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_instance] : initialworld(X) end function displaystructure(X::DimensionalLogiset; indent_str = "") - out = "$(typeof(X))\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" + out = "$(typeof(X))\t$(humansize(X))\n" out *= indent_str * "├ relations:\t($((nrelations(X))))\t[$(join(syntaxstring.(relations(X)), ", "))]\n" out *= indent_str * "├ features:\t($((nfeatures(X))))\t[$(join(syntaxstring.(features(X)), ", "))]\n" out *= indent_str * "├ domain shape:\t\t$(Base.size(domain(X)))\n" diff --git a/src/datasets/dimensional-datasets/datasets/dimensional-supports.jl b/src/logisets/dimensional-logisets/datasets/dimensional-supports.jl similarity index 95% rename from src/datasets/dimensional-datasets/datasets/dimensional-supports.jl rename to src/logisets/dimensional-logisets/datasets/dimensional-supports.jl index d626cb0..6862ff4 100644 --- a/src/datasets/dimensional-datasets/datasets/dimensional-supports.jl +++ b/src/logisets/dimensional-logisets/datasets/dimensional-supports.jl @@ -6,7 +6,7 @@ import Base: size, ndims, getindex, setindex! ############################################################################################ ############################################################################################ -abstract type AbstractUniformFullDimensionalRelationalSupport{T,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} <: AbstractRelationalSupport{T,W,FR} end +abstract type AbstractUniformFullDimensionalRelationalSupport{T,W<:AbstractWorld,FR<:AbstractFrame{W}} <: AbstractScalarOneStepRelationalMemoset{T,W,FR} end # TODO switch from nothing to missing? usesmemo(fwd_rs::AbstractUniformFullDimensionalRelationalSupport) = Nothing <: Base.eltype(fwd_rs.d) @@ -221,24 +221,24 @@ end ############################################################################################ -function _slice_dataset( +function instances( support::UniformFullDimensionalRelationalSupport{T,W,N}, inds::AbstractVector{<:Integer}, - return_view::Val = Val(false) + return_view::Union{Val{true},Val{false}} = Val(false) ) where {T,W<:OneWorld,N} UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) end -function _slice_dataset( +function instances( support::UniformFullDimensionalRelationalSupport{T,W,N}, inds::AbstractVector{<:Integer}, - return_view::Val = Val(false) + return_view::Union{Val{true},Val{false}} = Val(false) ) where {T,W<:Interval,N} UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) end -function _slice_dataset( +function instances( support::UniformFullDimensionalRelationalSupport{T,W,N}, inds::AbstractVector{<:Integer}, - return_view::Val = Val(false) + return_view::Union{Val{true},Val{false}} = Val(false) ) where {T,W<:Interval2D,N} UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[:,:,:,:,inds,:,:] else support.d[:,:,:,:,inds,:,:] end) end @@ -279,7 +279,7 @@ end # nothing # Base.@propagate_inbounds @inline Base.setindex!(support::OneWorldFWD_RS{T}, threshold::T, i_instance::Integer, w::OneWorld, i_featsnaggr::Integer, i_relation::Integer) where {T} = # support.d[i_instance, i_featsnaggr, i_relation] = threshold -# function _slice_dataset(support::OneWorldFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(support::OneWorldFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # OneWorldFWD_RS{T}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) # end @@ -326,7 +326,7 @@ end # nothing # Base.@propagate_inbounds @inline Base.setindex!(support::IntervalFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval, i_featsnaggr::Integer, i_relation::Integer) where {T} = # support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] = threshold -# function _slice_dataset(support::IntervalFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(support::IntervalFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # IntervalFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) # end @@ -366,7 +366,7 @@ end # nothing # Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = # support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] = threshold -# function _slice_dataset(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # Interval2DFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,:,:,inds,:,:] else support.d[:,:,:,:,inds,:,:] end) # end @@ -410,6 +410,6 @@ end # nothing # Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = # support.d[w.x.x+div((w.x.y-2)*(w.x.y-1),2), w.y.x+div((w.y.y-2)*(w.y.y-1),2), i_instance, i_featsnaggr, i_relation] = threshold -# function _slice_dataset(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # Interval2DFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) # end diff --git a/src/datasets/dimensional-datasets/datasets/main.jl b/src/logisets/dimensional-logisets/datasets/main.jl similarity index 68% rename from src/datasets/dimensional-datasets/datasets/main.jl rename to src/logisets/dimensional-logisets/datasets/main.jl index 24c2fe2..d9d9160 100644 --- a/src/datasets/dimensional-datasets/datasets/main.jl +++ b/src/logisets/dimensional-logisets/datasets/main.jl @@ -18,19 +18,19 @@ import SoleLogics: worldtype, accessibles, allworlds, alphabet, initialworld using SoleData import SoleData: _isnan, hasnans, nvariables, max_channel_size, channel_size -import SoleData: instance, get_instance, slice_dataset, _slice_dataset +import SoleData: instance, get_instance, slicedataset, instances import SoleData: dimensionality using SoleModels using SoleModels: Aggregator, AbstractCondition -using SoleModels: BoundedExplicitConditionalAlphabet +using SoleModels: BoundedScalarConditions using SoleModels: CanonicalFeatureGeq, CanonicalFeatureGeqSoft, CanonicalFeatureLeq, CanonicalFeatureLeqSoft using SoleModels: AbstractLogiset, AbstractMultiModalFrame -using SoleModels: MultiFrameLogiset, AbstractLogiset +using SoleModels: MultiLogiset, AbstractLogiset using SoleModels: apply_test_operator, existential_aggregator, aggregator_bottom, aggregator_to_binary import SoleModels: representatives, ScalarMetaCondition, ScalarCondition, featvaltype -import SoleModels: ninstances, nrelations, nfeatures, check, _slice_dataset, minify -import SoleModels: nframes, frames, displaystructure, frame +import SoleModels: ninstances, nrelations, nfeatures, check, instances, minify +import SoleModels: nmodalities, frames, displaystructure, frame import SoleModels: grouped_featsaggrsnops, features, grouped_metaconditions, alphabet, findfeature, findrelation, isminifiable using SoleModels: grouped_featsnops2grouped_featsaggrsnops, @@ -41,8 +41,8 @@ using SoleModels: grouped_featsnops2grouped_featsaggrsnops, ############################################################################################ function check_initialworld(FD::Type{<:AbstractLogiset}, initialworld, W) - @assert isnothing(initialworld) || initialworld isa W "Cannot instantiate" * - " $(FD) with worldtype = $(W) but initialworld of type $(typeof(initialworld))." + @assert isnothing(initialworld) || initialworld isa W "Cannot instantiate " * + "$(FD) with worldtype = $(W) but initialworld of type $(typeof(initialworld))." end include("passive-dimensional-datasets.jl") @@ -54,20 +54,6 @@ include("dimensional-fwds.jl") _default_fwd_type(::Type{<:FullDimensionalFrame}) = UniformFullDimensionalFWD -abstract type SupportingDataset{W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} end - -isminifiable(X::SupportingDataset) = false - -worldtype(X::SupportingDataset{W}) where {W} = W - -function displaystructure(X::SupportingDataset; indent_str = "") - out = "$(typeof(X))\t$((Base.summarysize(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs" - out *= " ($(round(nmemoizedvalues(X))) values)\n" - out -end - -abstract type FeaturedSupportingDataset{V<:Number,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} <: SupportingDataset{W,FR} end - include("dimensional-supports.jl") include("check.jl") diff --git a/src/datasets/dimensional-datasets/datasets/passive-dimensional-dataset.jl b/src/logisets/dimensional-logisets/datasets/passive-dimensional-dataset.jl similarity index 88% rename from src/datasets/dimensional-datasets/datasets/passive-dimensional-dataset.jl rename to src/logisets/dimensional-logisets/datasets/passive-dimensional-dataset.jl index 305e2b0..2acef34 100644 --- a/src/datasets/dimensional-datasets/datasets/passive-dimensional-dataset.jl +++ b/src/logisets/dimensional-logisets/datasets/passive-dimensional-dataset.jl @@ -1,4 +1,4 @@ -using SoleData: slice_dataset +using SoleData: slicedataset import SoleData: get_instance, ninstances, nvariables, channel_size, max_channel_size, dimensionality, eltype using SoleData: AbstractDimensionalDataset, AbstractDimensionalInstance, @@ -20,7 +20,7 @@ struct PassiveDimensionalDataset{ W<:AbstractWorld, DOM<:AbstractDimensionalDataset, FR<:AbstractDimensionalFrame{N,W}, -} <: AbstractLogiset{W,FT where FT<:DimensionalFeature,Bool,FR} # Note: truth value could by different +} <: AbstractLogiset{W,FT where FT<:VarFeature,Bool,FR} # Note: truth value could by different d::DOM @@ -79,16 +79,16 @@ eltype(X::PassiveDimensionalDataset) = eltype(X.d) get_instance(X::PassiveDimensionalDataset, args...) = get_instance(X.d, args...) -_slice_dataset(X::PassiveDimensionalDataset{N,W}, inds::AbstractVector{<:Integer}, args...; kwargs...) where {N,W} = - PassiveDimensionalDataset{N,W}(_slice_dataset(X.d, inds, args...; kwargs...)) +instances(X::PassiveDimensionalDataset{N,W}, inds::AbstractVector{<:Integer}, args...; kwargs...) where {N,W} = + PassiveDimensionalDataset{N,W}(instances(X.d, inds, args...; kwargs...)) hasnans(X::PassiveDimensionalDataset) = hasnans(X.d) worldtype(X::PassiveDimensionalDataset{N,W}) where {N,W} = W -frame(X::PassiveDimensionalDataset, i_instance) = _frame(X.d, i_instance) +frame(X::PassiveDimensionalDataset, i_instance::Integer) = _frame(X.d, i_instance) ############################################################################################ -_frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance) = _frame(X) +_frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance::Integer) = _frame(X) _frame(X::Union{UniformDimensionalDataset,AbstractArray}) = FullDimensionalFrame(channel_size(X)) diff --git a/src/datasets/dimensional-datasets/dimensional-ontologies.jl b/src/logisets/dimensional-logisets/dimensional-ontologies.jl similarity index 100% rename from src/datasets/dimensional-datasets/dimensional-ontologies.jl rename to src/logisets/dimensional-logisets/dimensional-ontologies.jl diff --git a/src/datasets/dimensional-datasets/gamma-access.jl b/src/logisets/dimensional-logisets/gamma-access.jl similarity index 100% rename from src/datasets/dimensional-datasets/gamma-access.jl rename to src/logisets/dimensional-logisets/gamma-access.jl diff --git a/src/logisets/dimensional-logisets/main.jl b/src/logisets/dimensional-logisets/main.jl new file mode 100644 index 0000000..f5b5d62 --- /dev/null +++ b/src/logisets/dimensional-logisets/main.jl @@ -0,0 +1,35 @@ +module DimensionalDatasets + +import SoleLogics: worldtype + +using SoleModels.utils + +# export parsecondition + +# # Conditions on features for dimensional datasets +# include("_parse-dimensional-condition.jl") + +# # Concrete type for ontologies +# include("ontology.jl") # TODO frame inside the ontology? + +# export DimensionalLogiset, Logiset, SupportedScalarLogiset + +# # Dataset structures +# include("datasets/main.jl") + +# const GenericModalDataset = Union{AbstractDimensionalDataset,AbstractLogiset,MultiLogiset} + +# # Dimensional ontologies +# include("dimensional-ontologies.jl") + +using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame +using SoleLogics: X, Y, Z + +# Representatives for dimensional frames +include("representatives/Full0DFrame.jl") +include("representatives/Full1DFrame.jl") +include("representatives/Full1DFrame+IA.jl") +include("representatives/Full1DFrame+RCC.jl") +include("representatives/Full2DFrame.jl") + +end diff --git a/src/datasets/dimensional-datasets/ontology.jl b/src/logisets/dimensional-logisets/ontology.jl similarity index 92% rename from src/datasets/dimensional-datasets/ontology.jl rename to src/logisets/dimensional-logisets/ontology.jl index 8d0cf26..797ce0f 100644 --- a/src/datasets/dimensional-datasets/ontology.jl +++ b/src/logisets/dimensional-logisets/ontology.jl @@ -3,9 +3,9 @@ using SoleLogics: OneWorld, Interval, Interval2D using SoleLogics: FullDimensionalFrame world2frametype = Dict([ - OneWorld => FullDimensionalFrame{0,OneWorld,Bool}, - Interval => FullDimensionalFrame{1,Interval{Int},Bool}, - Interval2D => FullDimensionalFrame{2,Interval2D{Int},Bool}, + OneWorld => FullDimensionalFrame{0,OneWorld}, + Interval => FullDimensionalFrame{1,Interval{Int}}, + Interval2D => FullDimensionalFrame{2,Interval2D{Int}}, ]) # An ontology is a pair `world type` + `set of relations`, and represents the kind of diff --git a/src/datasets/dimensional-datasets/representatives/Full0DFrame.jl b/src/logisets/dimensional-logisets/representatives/Full0DFrame.jl similarity index 100% rename from src/datasets/dimensional-datasets/representatives/Full0DFrame.jl rename to src/logisets/dimensional-logisets/representatives/Full0DFrame.jl diff --git a/src/datasets/dimensional-datasets/representatives/Full1DFrame+IA.jl b/src/logisets/dimensional-logisets/representatives/Full1DFrame+IA.jl similarity index 100% rename from src/datasets/dimensional-datasets/representatives/Full1DFrame+IA.jl rename to src/logisets/dimensional-logisets/representatives/Full1DFrame+IA.jl diff --git a/src/datasets/dimensional-datasets/representatives/Full1DFrame+RCC.jl b/src/logisets/dimensional-logisets/representatives/Full1DFrame+RCC.jl similarity index 100% rename from src/datasets/dimensional-datasets/representatives/Full1DFrame+RCC.jl rename to src/logisets/dimensional-logisets/representatives/Full1DFrame+RCC.jl diff --git a/src/datasets/dimensional-datasets/representatives/Full1DFrame.jl b/src/logisets/dimensional-logisets/representatives/Full1DFrame.jl similarity index 100% rename from src/datasets/dimensional-datasets/representatives/Full1DFrame.jl rename to src/logisets/dimensional-logisets/representatives/Full1DFrame.jl diff --git a/src/datasets/dimensional-datasets/representatives/Full2DFrame.jl b/src/logisets/dimensional-logisets/representatives/Full2DFrame.jl similarity index 100% rename from src/datasets/dimensional-datasets/representatives/Full2DFrame.jl rename to src/logisets/dimensional-logisets/representatives/Full2DFrame.jl diff --git a/src/datasets/scalar-datasets/gamma-access.jl b/src/logisets/dimensional-logisets/scalar-gamma-access.jl similarity index 98% rename from src/datasets/scalar-datasets/gamma-access.jl rename to src/logisets/dimensional-logisets/scalar-gamma-access.jl index a357f47..632ef5c 100644 --- a/src/datasets/scalar-datasets/gamma-access.jl +++ b/src/logisets/dimensional-logisets/scalar-gamma-access.jl @@ -68,7 +68,7 @@ end ############################################################################################ function fwdslice_onestep_accessible_aggregation( - X::OneStepFeaturedSupportingDataset{V,W}, + X::OneStepFeaturedMemoset{V,W}, fd::Logiset{V,W}, fwdslice::FWDFeatureSlice, i_instance::Integer, @@ -86,7 +86,7 @@ function fwdslice_onestep_accessible_aggregation( end function fwdslice_onestep_accessible_aggregation( - X::OneStepFeaturedSupportingDataset{V,W}, + X::OneStepFeaturedMemoset{V,W}, fd::Logiset{V,W}, fwdslice::FWDFeatureSlice, i_instance::Integer, diff --git a/src/datasets/scalar-datasets/active-scalar-logiset.jl b/src/logisets/dimensional-logisets/scalar-logiset.jl similarity index 98% rename from src/datasets/scalar-datasets/active-scalar-logiset.jl rename to src/logisets/dimensional-logisets/scalar-logiset.jl index d613ea6..1bbd4b8 100644 --- a/src/datasets/scalar-datasets/active-scalar-logiset.jl +++ b/src/logisets/dimensional-logisets/scalar-logiset.jl @@ -17,7 +17,7 @@ end function check( p::Proposition{<:ScalarCondition}, X::AbstractScalarLogiset{W}, - i_instance, + i_instance::Integer, w::W, ) where {W<:AbstractWorld} cond = atom(p) @@ -42,7 +42,7 @@ function alphabet(X::AbstractScalarLogiset) [(mc, thresholds) for mc in metaconditions] end for (feature, metaconditions) in grouped_metaconditions(X)]...) C = ScalarCondition{featvaltype(X),featuretype(X),ScalarMetaCondition{featuretype(X)}} - BoundedExplicitConditionalAlphabet{C}(collect(conds)) + BoundedScalarConditions{C}(collect(conds)) end diff --git a/src/datasets/scalar-datasets/one-step-featured-supporting-dataset.jl b/src/logisets/dimensional-logisets/scalar-one-step-featured-supporting-dataset.jl similarity index 77% rename from src/datasets/scalar-datasets/one-step-featured-supporting-dataset.jl rename to src/logisets/dimensional-logisets/scalar-one-step-featured-supporting-dataset.jl index 420e4fb..aafa2cb 100644 --- a/src/datasets/scalar-datasets/one-step-featured-supporting-dataset.jl +++ b/src/logisets/dimensional-logisets/scalar-one-step-featured-supporting-dataset.jl @@ -1,14 +1,14 @@ # Compute modal dataset propositions and 1-modal decisions -struct OneStepFeaturedSupportingDataset{ +struct OneStepFeaturedMemoset{ V<:Number, W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, + FR<:AbstractFrame{W}, VV<:Union{V,Nothing}, - FWDRS<:AbstractRelationalSupport{VV,W,FR}, - FWDGS<:Union{AbstractGlobalSupport{V},Nothing}, + FWDRS<:AbstractScalarOneStepRelationalMemoset{VV,W,FR}, + FWDGS<:Union{AbstractScalarOneStepGlobalMemoset{W,V},Nothing}, G<:AbstractVector{Tuple{<:AbstractFeature,<:Aggregator}}, -} <: FeaturedSupportingDataset{V,W,FR} +} <: FeaturedMemoset{V,W,FR} # Relational support fwd_rs :: FWDRS @@ -19,17 +19,17 @@ struct OneStepFeaturedSupportingDataset{ # Features and Aggregators featsnaggrs :: G - function OneStepFeaturedSupportingDataset( + function OneStepFeaturedMemoset( fwd_rs::FWDRS, fwd_gs::FWDGS, featsnaggrs::G, ) where { V<:Number, W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, + FR<:AbstractFrame{W}, VV<:Union{V,Nothing}, - FWDRS<:AbstractRelationalSupport{VV,W,FR}, - FWDGS<:Union{AbstractGlobalSupport{V},Nothing}, + FWDRS<:AbstractScalarOneStepRelationalMemoset{VV,W,FR}, + FWDGS<:Union{AbstractScalarOneStepGlobalMemoset{W,V},Nothing}, G<:AbstractVector{Tuple{<:AbstractFeature,<:Aggregator}}, } @assert nfeatsnaggrs(fwd_rs) == length(featsnaggrs) "Can't instantiate $(ty) with unmatching nfeatsnaggrs for fwd_rs and provided featsnaggrs: $(nfeatsnaggrs(fwd_rs)) and $(length(featsnaggrs))" @@ -40,13 +40,13 @@ struct OneStepFeaturedSupportingDataset{ new{V,W,FR,VV,FWDRS,FWDGS,G}(fwd_rs, fwd_gs, featsnaggrs) end - _default_rs_type(::Type{<:AbstractWorld}) = GenericRelationalSupport + _default_rs_type(::Type{<:AbstractWorld}) = ScalarOneStepRelationalMemoset _default_rs_type(::Type{<:Union{OneWorld,Interval,Interval2D}}) = UniformFullDimensionalRelationalSupport # A function that computes the support from an explicit modal dataset - Base.@propagate_inbounds function OneStepFeaturedSupportingDataset( + Base.@propagate_inbounds function OneStepFeaturedMemoset( fd :: Logiset{V,W}, - relational_support_type :: Type{<:AbstractRelationalSupport} = _default_rs_type(W); + relational_support_type :: Type{<:AbstractScalarOneStepRelationalMemoset} = _default_rs_type(W); compute_relation_glob = false, use_memoization = false, ) where {V,W<:AbstractWorld} @@ -81,7 +81,7 @@ struct OneStepFeaturedSupportingDataset{ # Prepare fwd_gs fwd_gs = begin if compute_fwd_gs - GenericGlobalSupport(fd) + ScalarOneStepGlobalMemoset(fd) else nothing end @@ -148,21 +148,21 @@ struct OneStepFeaturedSupportingDataset{ end # next!(p) end - OneStepFeaturedSupportingDataset(fwd_rs, fwd_gs, featsnaggrs) + OneStepFeaturedMemoset(fwd_rs, fwd_gs, featsnaggrs) end end -fwd_rs(X::OneStepFeaturedSupportingDataset) = X.fwd_rs -fwd_gs(X::OneStepFeaturedSupportingDataset) = X.fwd_gs -featsnaggrs(X::OneStepFeaturedSupportingDataset) = X.featsnaggrs +fwd_rs(X::OneStepFeaturedMemoset) = X.fwd_rs +fwd_gs(X::OneStepFeaturedMemoset) = X.fwd_gs +featsnaggrs(X::OneStepFeaturedMemoset) = X.featsnaggrs -ninstances(X::OneStepFeaturedSupportingDataset) = ninstances(fwd_rs(X)) -# nfeatsnaggrs(X::OneStepFeaturedSupportingDataset) = nfeatsnaggrs(fwd_rs(X)) +ninstances(X::OneStepFeaturedMemoset) = ninstances(fwd_rs(X)) +# nfeatsnaggrs(X::OneStepFeaturedMemoset) = nfeatsnaggrs(fwd_rs(X)) # TODO delegate to the two components... function checksupportconsistency( fd::Logiset{V,W}, - X::OneStepFeaturedSupportingDataset{V,W}, + X::OneStepFeaturedMemoset{V,W}, ) where {V,W<:AbstractWorld} @assert ninstances(fd) == ninstances(X) "Consistency check failed! Unmatching ninstances for fd and support: $(ninstances(fd)) and $(ninstances(X))" # @assert nrelations(fd) == (nrelations(fwd_rs(X)) + (isnothing(fwd_gs(X)) ? 0 : 1)) "Consistency check failed! Unmatching nrelations for fd and support: $(nrelations(fd)) and $(nrelations(fwd_rs(X)))+$((isnothing(fwd_gs(X)) ? 0 : 1))" @@ -172,30 +172,30 @@ function checksupportconsistency( return true end -usesmemo(X::OneStepFeaturedSupportingDataset) = usesglobalmemo(X) || usesmodalmemo(X) -usesglobalmemo(X::OneStepFeaturedSupportingDataset) = false -usesmodalmemo(X::OneStepFeaturedSupportingDataset) = usesmemo(fwd_rs(X)) +usesmemo(X::OneStepFeaturedMemoset) = usesglobalmemo(X) || usesmodalmemo(X) +usesglobalmemo(X::OneStepFeaturedMemoset) = false +usesmodalmemo(X::OneStepFeaturedMemoset) = usesmemo(fwd_rs(X)) -Base.size(X::OneStepFeaturedSupportingDataset) = (size(fwd_rs(X)), (isnothing(fwd_gs(X)) ? () : size(fwd_gs(X)))) +Base.size(X::OneStepFeaturedMemoset) = (size(fwd_rs(X)), (isnothing(fwd_gs(X)) ? () : size(fwd_gs(X)))) -find_featsnaggr_id(X::OneStepFeaturedSupportingDataset, feature::AbstractFeature, aggregator::Aggregator) = findfirst(x->x==(feature, aggregator), featsnaggrs(X)) +find_featsnaggr_id(X::OneStepFeaturedMemoset, feature::AbstractFeature, aggregator::Aggregator) = findfirst(x->x==(feature, aggregator), featsnaggrs(X)) -function _slice_dataset(X::OneStepFeaturedSupportingDataset, inds::AbstractVector{<:Integer}, args...; kwargs...) - OneStepFeaturedSupportingDataset( - _slice_dataset(fwd_rs(X), inds, args...; kwargs...), - (isnothing(fwd_gs(X)) ? nothing : _slice_dataset(fwd_gs(X), inds, args...; kwargs...)), +function instances(X::OneStepFeaturedMemoset, inds::AbstractVector{<:Integer}, args...; kwargs...) + OneStepFeaturedMemoset( + instances(fwd_rs(X), inds, args...; kwargs...), + (isnothing(fwd_gs(X)) ? nothing : instances(fwd_gs(X), inds, args...; kwargs...)), featsnaggrs(X) ) end -function hasnans(X::OneStepFeaturedSupportingDataset) +function hasnans(X::OneStepFeaturedMemoset) hasnans(fwd_rs(X)) || (!isnothing(fwd_gs(X)) && hasnans(fwd_gs(X))) end -isminifiable(X::OneStepFeaturedSupportingDataset) = isminifiable(fwd_rs(X)) && (isnothing(fwd_gs(X)) || isminifiable(fwd_gs(X))) +isminifiable(X::OneStepFeaturedMemoset) = isminifiable(fwd_rs(X)) && (isnothing(fwd_gs(X)) || isminifiable(fwd_gs(X))) -function minify(X::OSSD) where {OSSD<:OneStepFeaturedSupportingDataset} +function minify(X::OSSD) where {OSSD<:OneStepFeaturedMemoset} (new_fwd_rs, new_fwd_gs), backmap = minify([ fwd_rs(X), @@ -210,12 +210,12 @@ function minify(X::OSSD) where {OSSD<:OneStepFeaturedSupportingDataset} X, backmap end -function displaystructure(X::OneStepFeaturedSupportingDataset; indent_str = "") +function displaystructure(X::OneStepFeaturedMemoset; indent_str = "") out = "$(typeof(X))\t$((Base.summarysize(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" out *= indent_str * "├ fwd_rs\t$(Base.summarysize(fwd_rs(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t" if usesmodalmemo(X) - out *= "(shape $(Base.size(fwd_rs(X))), $(nmemoizedvalues(fwd_rs(X))) values," * - " $(round(nonnothingshare(fwd_rs(X))*100, digits=2))% memoized)\n" + out *= "(shape $(Base.size(fwd_rs(X))), $(nmemoizedvalues(fwd_rs(X))) values, " * + "$(round(nonnothingshare(fwd_rs(X))*100, digits=2))% memoized)\n" else out *= "(shape $(Base.size(fwd_rs(X))))\n" end @@ -223,8 +223,8 @@ function displaystructure(X::OneStepFeaturedSupportingDataset; indent_str = "") if !isnothing(fwd_gs(X)) out *= "$(Base.summarysize(fwd_gs(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t" if usesglobalmemo(X) - out *= "(shape $(Base.size(fwd_gs(X))), $(nmemoizedvalues(fwd_gs(X))) values," * - " $(round(nonnothingshare(fwd_gs(X))*100, digits=2))% memoized)\n" + out *= "(shape $(Base.size(fwd_gs(X))), $(nmemoizedvalues(fwd_gs(X))) values, " * + "$(round(nonnothingshare(fwd_gs(X))*100, digits=2))% memoized)\n" else out *= "(shape $(Base.size(fwd_gs(X))))\n" end @@ -238,7 +238,7 @@ end ############################################################################################ function compute_global_gamma( - X::OneStepFeaturedSupportingDataset{V,W}, + X::OneStepFeaturedMemoset{V,W}, fd::Logiset{V,W}, i_instance::Integer, feature::AbstractFeature, @@ -258,7 +258,7 @@ function compute_global_gamma( end function compute_modal_gamma( - X::OneStepFeaturedSupportingDataset{V,W}, + X::OneStepFeaturedMemoset{V,W}, fd::Logiset{V,W}, i_instance::Integer, w::W, diff --git a/src/machine-learning.jl b/src/machine-learning.jl index e74499a..a32233e 100644 --- a/src/machine-learning.jl +++ b/src/machine-learning.jl @@ -82,9 +82,9 @@ function bestguess( if isnothing(weights) countmap(labels) else - @assert length(labels) === length(weights) "Can't compute" * - " best guess with uneven number of votes" * - " $(length(labels)) and weights $(length(weights))." + @assert length(labels) === length(weights) "Can't compute " * + "best guess with uneven number of votes " * + "$(length(labels)) and weights $(length(weights))." countmap(labels, weights) end end diff --git a/src/models/base.jl b/src/models/base.jl index 9231e03..6da4f74 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -1,6 +1,6 @@ import Base: convert, length, getindex, isopen import SoleLogics: check, syntaxstring -using SoleData: slice_dataset +using SoleData: slicedataset using SoleLogics: LeftmostLinearForm, LeftmostConjunctiveForm, LeftmostDisjunctiveForm # Util @@ -30,8 +30,8 @@ end # Check on a boolean condition function check(c::AbstractBooleanCondition, i::AbstractInterpretation, args...; kwargs...) - error("Please, provide method check(::$(typeof(c))," * - " i::$(typeof(i)), args...; kwargs...).") + error("Please, provide method check(::$(typeof(c)), " * + "i::$(typeof(i)), args...; kwargs...).") end function check( c::AbstractBooleanCondition, @@ -40,7 +40,7 @@ function check( kwargs... ) map( - i_instance->check(c, slice_dataset(d, [i_instance]; return_view = true), args...; kwargs...)[1], + i_instance->check(c, slicedataset(d, [i_instance]; return_view = true), args...; kwargs...)[1], 1:ninstances(d) ) end @@ -570,16 +570,16 @@ function check_model_constraints( ) I_O = outcometype(I_M) # FM_O = outcometype(FM) - @assert I_O <: FM_O "Can't instantiate $(M) with inner model outcometype" * - " $(I_O)! $(I_O) <: $(FM_O) should hold." + @assert I_O <: FM_O "Can't instantiate $(M) with inner model outcometype " * + "$(I_O)! $(I_O) <: $(FM_O) should hold." # @assert I_M <: FM || typename(I_M) <: typename(FM) "Can't instantiate $(M) with inner model $(I_M))! $(I_M) <: $(FM) || $(typename(I_M)) <: $(typename(FM)) should hold." - @assert I_M <: FM "Can't instantiate $(M) with inner model $(I_M))!" * - " $(I_M) <: $(FM) should hold." + @assert I_M <: FM "Can't instantiate $(M) with inner model $(I_M))! " * + "$(I_M) <: $(FM) should hold." if ! (I_M<:FinalModel{<:FM_O}) # @assert I_M<:ConstrainedModel{FM_O,<:FM} "ConstrainedModels require I_M<:ConstrainedModel{O,<:FM}, but $(I_M) does not subtype $(ConstrainedModel{FM_O,<:FM})." - @assert I_M<:ConstrainedModel{<:FM_O,<:FM} "ConstrainedModels require" * - " I_M<:ConstrainedModel{<:O,<:FM}, but $(I_M) does not" * - " subtype $(ConstrainedModel{<:FM_O,<:FM})." + @assert I_M<:ConstrainedModel{<:FM_O,<:FM} "ConstrainedModels require " * + "I_M<:ConstrainedModel{<:O,<:FM}, but $(I_M) does not " * + "subtype $(ConstrainedModel{<:FM_O,<:FM})." end end @@ -962,7 +962,7 @@ function apply( if !isempty(cpos) out[cpos] .= apply( posconsequent(m), - slice_dataset(d, cpos; return_view = true); + slicedataset(d, cpos; return_view = true); check_args = check_args, check_kwargs = check_kwargs, kwargs... @@ -971,7 +971,7 @@ function apply( if !isempty(cneg) out[cneg] .= apply( negconsequent(m), - slice_dataset(d, cneg; return_view = true); + slicedataset(d, cneg; return_view = true); check_args = check_args, check_kwargs = check_kwargs, kwargs... @@ -1091,7 +1091,7 @@ function apply( for rule in rulebase(m) length(uncovered_idxs) == 0 && break - uncovered_d = slice_dataset(d, uncovered_idxs; return_view = true) + uncovered_d = slicedataset(d, uncovered_idxs; return_view = true) idxs_sat = findall( # check_antecedent(rule, d, check_args...; check_kwargs...) .== true @@ -1127,7 +1127,7 @@ function apply!( for (n, rule) in enumerate(rules) length(uncovered_idxs) == 0 && break - uncovered_d = slice_dataset(d, uncovered_idxs; return_view = true) + uncovered_d = slicedataset(d, uncovered_idxs; return_view = true) idxs_sat = findall( # check_antecedent(rule, d, check_args...; check_kwargs...) .== true @@ -1256,10 +1256,10 @@ struct DecisionTree{ FM = typeintersect(Union{propagate_feasiblemodels(M)}, AbstractModel{<:O}) FFM = typeintersect(FM, FinalModel{<:O}) @assert M <: Union{<:FFM,<:Branch{<:O,<:C,<:Union{Branch,FFM}}} "" * - "Cannot instantiate DecisionTree{$(O),$(C),$(FFM)}(...) with root of" * - " type $(typeof(root)). Note that the should be either a FinalNode or a" * - " bounded Branch." * - " $(M) <: $(Union{FinalModel,Branch{<:O,<:C,<:Union{Branch,FFM}}}) should hold." + "Cannot instantiate DecisionTree{$(O),$(C),$(FFM)}(...) with root of " * + "type $(typeof(root)). Note that the should be either a FinalNode or a " * + "bounded Branch. " * + "$(M) <: $(Union{FinalModel,Branch{<:O,<:C,<:Union{Branch,FFM}}}) should hold." check_model_constraints(DecisionTree{O}, typeof(root), FM, O) new{O,C,FFM}(root, info) end diff --git a/src/models/print.jl b/src/models/print.jl index f4753b2..c0a4904 100644 --- a/src/models/print.jl +++ b/src/models/print.jl @@ -58,8 +58,8 @@ function displaymodel( show_subtree_info = false, syntaxstring_kwargs = (;), ) - println("Please, provide method displaymodel(::$(typeof(m)); kwargs...)." * - " See help for displaymodel.") + println("Please, provide method displaymodel(::$(typeof(m)); kwargs...). " * + "See help for displaymodel.") end ############################################################################################ diff --git a/test/datasets.jl b/test/datasets.jl index 30856ea..359c22c 100644 --- a/test/datasets.jl +++ b/test/datasets.jl @@ -2,49 +2,170 @@ using Test using StatsBase using SoleLogics using SoleModels -using SoleModels.DimensionalDatasets +using Graphs +using Random +using ThreadSafeDicts -X = Array(reshape(1.0:180.0, 3,3,2,10)) +features = SoleModels.Feature.(string.('p':'z')) +worlds = SoleLogics.World.(1:10) +fr = SoleLogics.ExplicitCrispUniModalFrame(worlds, SimpleDiGraph(length(worlds), 4)) -ontology = get_interval_ontology(2) -@test_nowarn DimensionalLogiset{Float64}(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]) +i_instance = 1 -dfd = @test_nowarn DimensionalLogiset(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]) +# Boolean +rng = Random.MersenneTwister(1) +bool_logiset = SoleModels.ExplicitBooleanLogiset([(Dict([w => sample(rng, features, 2, replace = false) for w in worlds]), fr)]) +bool_condition = SoleModels.ValueCondition(features[1]) -@test_nowarn DimensionalLogiset{Float64}(X, ontology, [minimum, maximum]) -dfd2 = @test_nowarn DimensionalLogiset(X, ontology, [minimum, maximum]) +@test [SoleModels.checkcondition(bool_condition, bool_logiset, i_instance, w) + for w in worlds] == Bool[0, 1, 1, 1, 0, 0, 0, 0, 0, 0] -@test_throws AssertionError DimensionalLogiset(X, ontology, [StatsBase.mean]) +# Scalar (Float) +rng = Random.MersenneTwister(1) +scalar_logiset = SoleModels.ExplicitLogiset([(Dict([w => Dict([f => rand(rng) for f in features]) for w in worlds]), fr)]) +scalar_condition = SoleModels.ScalarCondition(features[1], >, 0.5) +@test [SoleModels.checkcondition(scalar_condition, scalar_logiset, i_instance, w) + for w in worlds] == Bool[0, 0, 1, 1, 0, 1, 0, 1, 0, 0] -@test_nowarn dfd |> alphabet |> propositions -@test_nowarn length(alphabet(dfd)) -@test_nowarn length(collect(propositions(alphabet(dfd)))) -@test length(collect(propositions(alphabet(dfd))))*2 == length(collect(propositions(alphabet(dfd2)))) +# Non-scalar (Vector{Float}) +rng = Random.MersenneTwister(2) +nonscalar_logiset = SoleModels.ExplicitLogiset([(Dict([w => Dict([f => rand(rng, rand(rng, 1:3)) for f in features]) for w in worlds]), fr)]) -@test all(((propositions(dfd |> SupportedScalarLogiset |> alphabet))) .== - ((propositions(dfd |> alphabet)))) +@test SoleModels.featvalue(nonscalar_logiset, 1, worlds[1], features[1]) == SoleModels.featvalue(features[1], nonscalar_logiset, 1, worlds[1]) +nonscalar_condition = SoleModels.FunctionalCondition(features[1], (vals)->length(vals) >= 2) -dfd3 = @test_nowarn DimensionalLogiset{Float64}(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]; initialworld = Interval2D((2,3),(2,3))) +@test [SoleModels.checkcondition(nonscalar_condition, nonscalar_logiset, i_instance, w) + for w in worlds] == Bool[0, 1, 0, 0, 1, 1, 1, 0, 1, 1] -check(SyntaxTree(⊤), dfd, 1) -check(SyntaxTree(⊤), dfd2, 1) -check(SyntaxTree(⊤), dfd3, 1) +multilogiset = MultiLogiset([bool_logiset, scalar_logiset, nonscalar_logiset]) +@test SoleModels.modalitytype(multilogiset) <: +SoleModels.AbstractLogiset{SoleLogics.World{Int64}, U, SoleModels.Feature{String}, SoleLogics.ExplicitCrispUniModalFrame{SoleLogics.World{Int64}, SimpleDiGraph{Int64}}} where U -X = Array(reshape(1.0:180.0, 3,3,2,10)); +SoleModels.AbstractLogiset{SoleLogics.World{Int64}, U, Feature{String}, SoleLogics.ExplicitCrispUniModalFrame{SoleLogics.World{Int64}, SimpleDiGraph{Int64}}} where U <: SoleModels.AbstractLogiset{SoleLogics.World{Int64}, U, Feature{String}, SoleLogics.ExplicitCrispUniModalFrame{SoleLogics.World{Int64}, SimpleDiGraph{Int64}}} where U -d = DimensionalLogiset{UInt16}( -X, -get_ontology(2, :interval, :RCC5), -[minimum, maximum]; -initialworld = SoleLogics.Interval2D((2,3),(2,3)), -) -@assert SoleLogics.Interval2D((2,3),(2,3)) == initialworld(d) -@assert SoleLogics.Interval2D((2,3),(2,3)) == initialworld(d, 1) +@test_nowarn displaystructure(bool_logiset) +@test_nowarn displaystructure(scalar_logiset) +@test_nowarn displaystructure(multilogiset) +############################################################################################ + +for w in worlds + @test accessibles(fr, w) == accessibles(scalar_logiset, 1, w) + @test representatives(fr, w, scalar_condition) == representatives(scalar_logiset, 1, w, scalar_condition) +end + +cond1 = SoleModels.ScalarCondition(features[1], >, 0.9) +cond2 = SoleModels.ScalarCondition(features[2], >, 0.3) + +for w in worlds + @test (featvalue(scalar_logiset, 1, w, features[1]) > 0.9) == check(Proposition(cond1) ∧ ⊤, scalar_logiset, 1, w) + @test (featvalue(scalar_logiset, 1, w, features[2]) > 0.3) == check(Proposition(cond2) ∧ ⊤, scalar_logiset, 1, w) +end + +# Propositional formula +φ = ⊤ → Proposition(cond1) ∧ Proposition(cond2) +for w in worlds + @test ((featvalue(scalar_logiset, 1, w, features[1]) > 0.9) && (featvalue(scalar_logiset, 1, w, features[2]) > 0.3)) == check(φ, scalar_logiset, 1, w) +end + +# Modal formula +φ = ◊(⊤ → Proposition(cond1) ∧ Proposition(cond2)) +for w in worlds + @test check(φ, scalar_logiset, 1, w) == (length(accessibles(fr, w)) > 0 && any([ + ((featvalue(scalar_logiset, 1, v, features[1]) > 0.9) && (featvalue(scalar_logiset, 1, v, features[2]) > 0.3)) + for v in accessibles(fr, w)])) +end + +# Modal formula on multilogiset +for w in worlds + @test check(φ, multilogiset, 2, 1, w) == (length(accessibles(fr, w)) > 0 && any([ + ((featvalue(multilogiset, 2, 1, v, features[1]) > 0.9) && (featvalue(multilogiset, 2, 1, v, features[2]) > 0.3)) + for v in accessibles(fr, w)])) +end + +############################################################################################ + +# Check with memoset + +w = worlds[1] +W = worldtype(bool_logiset) +bool_supported_logiset = SupportedLogiset(bool_logiset) +scalar_supported_logiset = SupportedLogiset(scalar_logiset) +nonscalar_supported_logiset = SupportedLogiset(nonscalar_logiset) + +@test SoleModels.featvalue(nonscalar_logiset, 1, worlds[1], features[1]) == SoleModels.featvalue(nonscalar_supported_logiset, 1, worlds[1], features[1]) + +@test_nowarn displaystructure(bool_supported_logiset) +@test_nowarn displaystructure(scalar_supported_logiset) +@test_nowarn displaystructure(nonscalar_supported_logiset) + +@test_nowarn slicedataset(bool_logiset, [1]) +@test_nowarn slicedataset(bool_logiset, [1]; return_view = true) +@test_nowarn slicedataset(bool_supported_logiset, [1]) + +@test_nowarn SoleModels.allfeatvalues(bool_logiset) +@test_nowarn SoleModels.allfeatvalues(bool_logiset, 1) +@test_nowarn SoleModels.allfeatvalues(bool_logiset, 1, features[1]) +@test_nowarn SoleModels.allfeatvalues(bool_supported_logiset) +@test_nowarn SoleModels.allfeatvalues(bool_supported_logiset, 1) +@test_nowarn SoleModels.allfeatvalues(bool_supported_logiset, 1, features[1]) + +@test SoleLogics.allworlds(bool_logiset, 1) == SoleLogics.allworlds(bool_supported_logiset, 1) +@test SoleLogics.nworlds(bool_logiset, 1) == SoleLogics.nworlds(bool_supported_logiset, 1) +@test SoleLogics.frame(bool_logiset, 1) == SoleLogics.frame(bool_supported_logiset, 1) + + +@test_throws AssertionError SoleModels.parsecondition(SoleModels.ScalarCondition, "p > 0.5") +@test_nowarn SoleModels.parsecondition(SoleModels.ScalarCondition, "p > 0.5"; featvaltype = String, featuretype = Feature) +@test SoleModels.ScalarCondition(features[1], >, 0.5) == SoleModels.parsecondition(SoleModels.ScalarCondition, "p > 0.5"; featvaltype = String, featuretype = Feature) + +memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstances(bool_supported_logiset)] + +@test_nowarn check(φ, bool_logiset, 1, w) +@test_nowarn check(φ, bool_logiset, 1, w; use_memo = nothing) +@test_nowarn check(φ, bool_logiset, 1, w; use_memo = memoset) +@test_nowarn check(φ, bool_supported_logiset, 1, w) +@test_nowarn check(φ, bool_supported_logiset, 1, w; use_memo = nothing) +@test_logs (:warn,) check(φ, bool_supported_logiset, 1, w; use_memo = memoset) + + +@test_nowarn bool_supported_logiset2 = SupportedLogiset(bool_logiset, memoset) +@test_nowarn bool_supported_logiset2 = SupportedLogiset(bool_logiset, (memoset,)) +@test_nowarn bool_supported_logiset2 = SupportedLogiset(bool_logiset, [memoset]) + +@test_throws AssertionError SupportedLogiset(bool_supported_logiset2) + +@test_nowarn SupportedLogiset(bool_logiset, bool_supported_logiset2) + +@test_nowarn SupportedLogiset(bool_logiset, (bool_supported_logiset2,)) +@test_nowarn SupportedLogiset(bool_logiset, [bool_supported_logiset2]) + + +rng = Random.MersenneTwister(1) +alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]) +syntaxstring.(alph) +_formulas = [randformulatree(rng, 4, alph, [NEGATION, CONJUNCTION, IMPLICATION, DIAMOND, BOX]) for i in 1:10] +syntaxstring.(_formulas) + +c1 = @test_nowarn [check(φ, bool_logiset, 1, w) for φ in _formulas] +c2 = @test_nowarn [check(φ, bool_logiset, 1, w; use_memo = nothing) for φ in _formulas] +c3 = @test_nowarn [check(φ, bool_logiset, 1, w; use_memo = memoset) for φ in _formulas] +c4 = @test_nowarn [check(φ, bool_supported_logiset, 1, w) for φ in _formulas] +c5 = @test_nowarn [check(φ, bool_supported_logiset, 1, w; use_memo = nothing) for φ in _formulas] +# c6 = @test_logs (:warn,) [check(φ, bool_supported_logiset, 1, w; use_memo = memoset) for φ in _formulas] + +@test c1 == c2 == c3 == c4 == c5 + +w = worlds[1] +W = worldtype(scalar_logiset) +memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstances(scalar_logiset)] +@test_throws AssertionError check(φ, scalar_logiset, 1; use_memo = nothing) +@time check(φ, scalar_logiset, 1, w; use_memo = nothing) +@time check(φ, scalar_logiset, 1, w; use_memo = memoset) diff --git a/test/dimensional-datasets.jl b/test/dimensional-datasets.jl new file mode 100644 index 0000000..30856ea --- /dev/null +++ b/test/dimensional-datasets.jl @@ -0,0 +1,50 @@ +using Test +using StatsBase +using SoleLogics +using SoleModels +using SoleModels.DimensionalDatasets + +X = Array(reshape(1.0:180.0, 3,3,2,10)) + +ontology = get_interval_ontology(2) +@test_nowarn DimensionalLogiset{Float64}(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]) + +dfd = @test_nowarn DimensionalLogiset(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]) + +@test_nowarn DimensionalLogiset{Float64}(X, ontology, [minimum, maximum]) +dfd2 = @test_nowarn DimensionalLogiset(X, ontology, [minimum, maximum]) + +@test_throws AssertionError DimensionalLogiset(X, ontology, [StatsBase.mean]) + + +@test_nowarn dfd |> alphabet |> propositions +@test_nowarn length(alphabet(dfd)) +@test_nowarn length(collect(propositions(alphabet(dfd)))) +@test length(collect(propositions(alphabet(dfd))))*2 == length(collect(propositions(alphabet(dfd2)))) + +@test all(((propositions(dfd |> SupportedScalarLogiset |> alphabet))) .== + ((propositions(dfd |> alphabet)))) + + +dfd3 = @test_nowarn DimensionalLogiset{Float64}(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]; initialworld = Interval2D((2,3),(2,3))) + +check(SyntaxTree(⊤), dfd, 1) +check(SyntaxTree(⊤), dfd2, 1) +check(SyntaxTree(⊤), dfd3, 1) + + + +X = Array(reshape(1.0:180.0, 3,3,2,10)); + + +d = DimensionalLogiset{UInt16}( +X, +get_ontology(2, :interval, :RCC5), +[minimum, maximum]; +initialworld = SoleLogics.Interval2D((2,3),(2,3)), +) +@assert SoleLogics.Interval2D((2,3),(2,3)) == initialworld(d) +@assert SoleLogics.Interval2D((2,3),(2,3)) == initialworld(d, 1) + + + diff --git a/test/parse.jl b/test/parse.jl index 7b76b50..26d8f3a 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -1,31 +1,49 @@ -using SoleModels.DimensionalDatasets: parsecondition - -@test_logs (:warn,) SoleModels.parsecondition("min[V1] <= 32") -@test_nowarn SoleModels.parsecondition("min[V1] <= 32"; featvaltype = Float64) - -@test_nowarn SoleModels.parsecondition("min[V1] <= 32"; featvaltype = Float64) -@test_nowarn SoleModels.parsecondition("max[V2] <= 435"; featvaltype = Float64) -@test_nowarn SoleModels.parsecondition("minimum[V6] > 250.631"; featvaltype = Float64) -@test_nowarn SoleModels.parsecondition(" minimum [V7] > 11.2"; featvaltype = Float64) -@test_nowarn SoleModels.parsecondition("avg [V8] > 63.2 "; featvaltype = Float64) -@test_nowarn SoleModels.parsecondition("mean[V9] <= 1.0e100"; featvaltype = Float64) - -@test_nowarn SoleModels.parsecondition("max{3] <= 12"; featvaltype = Float64, opening_bracket="{", variable_name_prefix = "") -@test_nowarn SoleModels.parsecondition(" min[V4} > 43.25 "; featvaltype = Float64, closing_bracket="}") -@test_nowarn SoleModels.parsecondition("max{5} <= 250"; featvaltype = Float64, opening_bracket="{", closing_bracket="}", variable_name_prefix = "") -@test_nowarn SoleModels.parsecondition("mean[V9] <= 1.0e100"; featvaltype = Float64) -@test_nowarn SoleModels.parsecondition("mean🌅V9🌄 <= 1.0e100"; featvaltype = Float64, opening_bracket="🌅", closing_bracket="🌄") - -@test_nowarn SoleModels.featvaltype(SoleModels.feature(SoleModels.parsecondition("mean[V10] > 462.2"; featvaltype = Float64))) == Float64 -@test_nowarn SoleModels.featvaltype(SoleModels.feature(SoleModels.parsecondition("mean[V11]<1.0e100"; featvaltype = Float64))) == Float64 - -@test_nowarn SoleModels.parsecondition("max[V15] <= 723"; featvaltype = Float64) -@test_nowarn SoleModels.parsecondition("mean[V16] == 54.2"; featvaltype = Float64) - -@test_throws Exception SoleModels.parsecondition("5345.4 < avg [V13] < 32.2 < 12.2") -@test_throws AssertionError SoleModels.parsecondition("avg [V14] < 12.2 <= 6127.2") -@test_throws AssertionError SoleModels.parsecondition("mean189] > 113.2") -@test_throws AssertionError SoleModels.parsecondition("123.4 < avg [V12] > 777.2 ") -@test_throws Exception SoleModels.parsecondition("mimimum [V17] < 23.2 <= 156.2") -@test_throws AssertionError SoleModels.parsecondition("max[V3} <= 12"; opening_bracket="{") -@test_throws AssertionError SoleModels.parsecondition("max{18] <= 12"; opening_bracket="}", variable_name_prefix = "") +using SoleModels: parsecondition + +@test_nowarn SoleModels.parsefeature(SoleModels.VarFeature{Float64}, "min[V1]") +@test_nowarn SoleModels.parsefeature(SoleModels.VarFeature, "min[V1]"; featvaltype = Float64) +@test_nowarn SoleModels.parsefeature(SoleModels.VarFeature{Float64}, "min[V1]"; featvaltype = Float64) +@test_throws AssertionError SoleModels.parsefeature(SoleModels.VarFeature{Float64}, "min[V1]"; featvaltype = Int64) +@test_logs (:warn,) SoleModels.parsefeature(SoleModels.AbstractUnivariateFeature, "min[V1]") +@test_logs (:warn,) SoleModels.parsefeature(UnivariateMin, "min[V1]") +@test_nowarn SoleModels.parsefeature(UnivariateMin{Float64}, "min[V1]") + + + +@test_logs (:warn,) parsecondition(SoleModels.ScalarCondition, "F1 > 2"; featuretype = SoleModels.Feature) +@test_logs (:warn,) parsecondition(SoleModels.ScalarCondition, "1 > 2"; featuretype = SoleModels.Feature{Int}) +@test_logs (:warn,) parsecondition(SoleModels.ScalarCondition, "1 > 2"; featuretype = SoleModels.Feature{Proposition{Int}}) + +C = SoleModels.ScalarCondition + +@test_logs (:warn,) SoleModels.parsecondition(C, "min[V1] <= 32") +@test_logs (:warn,) SoleModels.parsecondition(C, "min[V1] <= 32"; featvaltype = Float64) +@test_nowarn SoleModels.parsecondition(C, "min[V1] <= 32"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) + +@test_nowarn SoleModels.parsecondition(C, "min[V1] <= 32"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) +@test_nowarn SoleModels.parsecondition(C, "max[V2] <= 435"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) +@test_nowarn SoleModels.parsecondition(C, "minimum[V6] > 250.631"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) +@test_throws AssertionError SoleModels.parsecondition(C, " minimum [V7] > 11.2"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) +@test_throws AssertionError SoleModels.parsecondition(C, "avg [V8] > 63.2 "; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) +@test_nowarn SoleModels.parsecondition(C, "mean[V9] <= 1.0e100"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) + +@test_nowarn SoleModels.parsecondition(C, "max{3] <= 12"; featvaltype = Float64, opening_bracket="{", variable_name_prefix = "", featuretype = SoleModels.AbstractUnivariateFeature) +@test_nowarn SoleModels.parsecondition(C, " min[V4} > 43.25 "; featvaltype = Float64, closing_bracket="}", featuretype = SoleModels.AbstractUnivariateFeature) +@test_nowarn SoleModels.parsecondition(C, "max{5} <= 250"; featvaltype = Float64, opening_bracket="{", closing_bracket="}", variable_name_prefix = "", featuretype = SoleModels.AbstractUnivariateFeature) +@test_nowarn SoleModels.parsecondition(C, "mean[V9] <= 1.0e100"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) +@test_nowarn SoleModels.parsecondition(C, "mean🌅V9🌄 <= 1.0e100"; featvaltype = Float64, opening_bracket="🌅", closing_bracket="🌄", featuretype = SoleModels.AbstractUnivariateFeature) + +@test_nowarn SoleModels.featvaltype(SoleModels.feature(SoleModels.parsecondition(C, "mean[V10] > 462.2"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature))) == Float64 +@test_nowarn SoleModels.featvaltype(SoleModels.feature(SoleModels.parsecondition(C, "mean[V11] < 1.0e100"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature))) == Float64 + + +@test_nowarn SoleModels.parsecondition(C, "max[V15] <= 723"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) +@test_nowarn SoleModels.parsecondition(C, "mean[V16] == 54.2"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) + +@test_throws Exception SoleModels.parsecondition(C, "5345.4 < avg [V13] < 32.2 < 12.2"; featuretype = SoleModels.AbstractUnivariateFeature) +@test_throws AssertionError SoleModels.parsecondition(C, "avg [V14] < 12.2 <= 6127.2"; featuretype = SoleModels.AbstractUnivariateFeature) +@test_throws AssertionError SoleModels.parsecondition(C, "mean189] > 113.2"; featuretype = SoleModels.AbstractUnivariateFeature) +@test_throws AssertionError SoleModels.parsecondition(C, "123.4 < avg [V12] > 777.2 "; featuretype = SoleModels.AbstractUnivariateFeature) +@test_throws Exception SoleModels.parsecondition(C, "mimimum [V17] < 23.2 <= 156.2"; featuretype = SoleModels.AbstractUnivariateFeature) +@test_throws AssertionError SoleModels.parsecondition(C, "max[V3} <= 12"; opening_bracket="{", featuretype = SoleModels.AbstractUnivariateFeature) +@test_throws AssertionError SoleModels.parsecondition(C, "max{18] <= 12"; opening_bracket="}", variable_name_prefix = "", featuretype = SoleModels.AbstractUnivariateFeature) diff --git a/test/runtests.jl b/test/runtests.jl index 56b286f..7faef65 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,7 +17,10 @@ println("Julia version: ", VERSION) test_suites = [ ("Models", ["base.jl", ]), - # ("Datasets", ["datasets.jl", ]), + ("Datasets", [ + "datasets.jl", + # "dimensional-datasets.jl", + ]), ("Miscellaneous", ["misc.jl", "minify.jl"]), # ("Parse", ["parse.jl", ]), ] From aaf39c859cc3d8c670a3151d05c0910ecea57780 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 13 Jun 2023 14:25:23 +0200 Subject: [PATCH 09/77] attributes->variables, samples->instances, _slice_dataset->instances, slice_dataset->slicedatasetgitd --- src/SoleModels.jl | 2 +- .../active-featured-dataset.jl | 6 +- src/conditional-data/main.jl | 7 +- .../multi-frame-conditional-datasets.jl | 54 ++++++------- .../datasets/dimensional-featured-dataset.jl | 12 +-- .../datasets/dimensional-fwds.jl | 40 +++++----- .../datasets/featured-dataset.jl | 22 +++--- .../datasets/generic-supporting-datasets.jl | 16 ++-- src/dimensional-datasets/datasets/main.jl | 8 +- .../dimensional-supports.jl | 76 +++++++++---------- .../generic-supports.jl | 14 ++-- .../main.jl | 14 ++-- .../datasets/passive-dimensional-dataset.jl | 12 +-- .../datasets/supported-featured-dataset.jl | 10 +-- src/dimensional-datasets/main.jl | 2 +- src/models/base.jl | 36 ++++----- src/models/rule-evaluation.jl | 4 +- 17 files changed, 168 insertions(+), 167 deletions(-) diff --git a/src/SoleModels.jl b/src/SoleModels.jl index acc1986..1d49517 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -61,7 +61,7 @@ export computefeature include("conditional-data/main.jl") -export nsamples, nframes, frames, nfeatures +export ninstances, nmodalities, modalities, nfeatures export get_ontology, get_interval_ontology diff --git a/src/conditional-data/active-featured-dataset.jl b/src/conditional-data/active-featured-dataset.jl index 85900a7..ddaf1d6 100644 --- a/src/conditional-data/active-featured-dataset.jl +++ b/src/conditional-data/active-featured-dataset.jl @@ -34,7 +34,7 @@ function alphabet(X::AbstractActiveFeaturedDataset) conds = vcat([begin thresholds = unique([ X[i_sample, w, feature] - for i_sample in 1:nsamples(X) + for i_sample in 1:ninstances(X) for w in allworlds(X, i_sample) ]) [(mc, thresholds) for mc in metaconditions] @@ -44,8 +44,8 @@ function alphabet(X::AbstractActiveFeaturedDataset) end -# Base.length(X::AbstractActiveFeaturedDataset) = nsamples(X) -# Base.iterate(X::AbstractActiveFeaturedDataset, state=1) = state > nsamples(X) ? nothing : (get_instance(X, state), state+1) +# Base.length(X::AbstractActiveFeaturedDataset) = ninstances(X) +# Base.iterate(X::AbstractActiveFeaturedDataset, state=1) = state > ninstances(X) ? nothing : (get_instance(X, state), state+1) function find_feature_id(X::AbstractActiveFeaturedDataset, feature::AbstractFeature) id = findfirst(x->(Base.isequal(x, feature)), features(X)) diff --git a/src/conditional-data/main.jl b/src/conditional-data/main.jl index bc9b280..c1aafa5 100644 --- a/src/conditional-data/main.jl +++ b/src/conditional-data/main.jl @@ -5,8 +5,9 @@ using SoleLogics: AbstractWorld, IdentityRel import SoleLogics: syntaxstring import SoleLogics: frame -import SoleData: nsamples, nfeatures -import SoleData: nframes, frames, hasnans, _slice_dataset +import SoleData: nvariables +import SoleData: ninstances +import SoleData: nmodalities, hasnans, instances # import SoleData: frame # TODO # Minification interface for lossless data compression @@ -46,7 +47,7 @@ include("conditional-datasets.jl") include("active-featured-dataset.jl") -export nframes, frames, frame, +export nmodalities, modalities, frame, display_structure, MultiFrameConditionalDataset, worldtypes diff --git a/src/conditional-data/multi-frame-conditional-datasets.jl b/src/conditional-data/multi-frame-conditional-datasets.jl index 47202c6..c0b6087 100644 --- a/src/conditional-data/multi-frame-conditional-datasets.jl +++ b/src/conditional-data/multi-frame-conditional-datasets.jl @@ -1,6 +1,6 @@ """ struct MultiFrameConditionalDataset{MD<:AbstractConditionalDataset} - frames :: Vector{<:MD} + modalities :: Vector{<:MD} end A multi-frame conditional dataset. This structure is useful for representing @@ -12,14 +12,14 @@ See also """ struct MultiFrameConditionalDataset{MD<:AbstractConditionalDataset} - frames :: Vector{<:MD} + modalities :: Vector{<:MD} function MultiFrameConditionalDataset{MD}(X::MultiFrameConditionalDataset{MD}) where {MD<:AbstractConditionalDataset} - MultiFrameConditionalDataset{MD}(X.frames) + MultiFrameConditionalDataset{MD}(X.modalities) end function MultiFrameConditionalDataset{MD}(Xs::AbstractVector) where {MD<:AbstractConditionalDataset} Xs = collect(Xs) - @assert length(Xs) > 0 && length(unique(nsamples.(Xs))) == 1 "Can't create an empty MultiFrameConditionalDataset or with mismatching number of samples (nframes: $(length(Xs)), frame_sizes: $(nsamples.(Xs)))." + @assert length(Xs) > 0 && length(unique(ninstances.(Xs))) == 1 "Can't create an empty MultiFrameConditionalDataset or with mismatching number of samples (nmodalities: $(length(Xs)), frame_sizes: $(ninstances.(Xs)))." new{MD}(Xs) end function MultiFrameConditionalDataset{MD}() where {MD<:AbstractConditionalDataset} @@ -36,59 +36,59 @@ struct MultiFrameConditionalDataset{MD<:AbstractConditionalDataset} end end -frames(X::MultiFrameConditionalDataset) = X.frames +modalities(X::MultiFrameConditionalDataset) = X.modalities Base.iterate(X::MultiFrameConditionalDataset, state=1) = state > length(X) ? nothing : (get_instance(X, state), state+1) -Base.length(X::MultiFrameConditionalDataset) = nsamples(X) -Base.push!(X::MultiFrameConditionalDataset, f::AbstractConditionalDataset) = push!(frames(X), f) +Base.length(X::MultiFrameConditionalDataset) = ninstances(X) +Base.push!(X::MultiFrameConditionalDataset, f::AbstractConditionalDataset) = push!(modalities(X), f) -Base.size(X::MultiFrameConditionalDataset) = map(size, frames(X)) +Base.size(X::MultiFrameConditionalDataset) = map(size, modalities(X)) -frame(X::MultiFrameConditionalDataset, i_frame::Integer) = nframes(X) > 0 ? frames(X)[i_frame] : error("MultiFrameConditionalDataset has no frame!") -nframes(X::MultiFrameConditionalDataset) = length(frames(X)) -nsamples(X::MultiFrameConditionalDataset) = nsamples(frame(X, 1)) +frame(X::MultiFrameConditionalDataset, i_frame::Integer) = nmodalities(X) > 0 ? modalities(X)[i_frame] : error("MultiFrameConditionalDataset has no frame!") +nmodalities(X::MultiFrameConditionalDataset) = length(modalities(X)) +ninstances(X::MultiFrameConditionalDataset) = ninstances(frame(X, 1)) -# max_channel_size(X::MultiFrameConditionalDataset) = map(max_channel_size, frames(X)) # TODO: figure if this is useless or not. Note: channel_size doesn't make sense at this point. -nfeatures(X::MultiFrameConditionalDataset) = map(nfeatures, frames(X)) # Note: used for safety checks in fit_tree.jl -# nrelations(X::MultiFrameConditionalDataset) = map(nrelations, frames(X)) # TODO: figure if this is useless or not +# max_channel_size(X::MultiFrameConditionalDataset) = map(max_channel_size, modalities(X)) # TODO: figure if this is useless or not. Note: channel_size doesn't make sense at this point. +nfeatures(X::MultiFrameConditionalDataset) = map(nfeatures, modalities(X)) # Note: used for safety checks in fit_tree.jl +# nrelations(X::MultiFrameConditionalDataset) = map(nrelations, modalities(X)) # TODO: figure if this is useless or not nfeatures(X::MultiFrameConditionalDataset, i_frame::Integer) = nfeatures(frame(X, i_frame)) nrelations(X::MultiFrameConditionalDataset, i_frame::Integer) = nrelations(frame(X, i_frame)) worldtype(X::MultiFrameConditionalDataset, i_frame::Integer) = worldtype(frame(X, i_frame)) -worldtypes(X::MultiFrameConditionalDataset) = Vector{Type{<:AbstractWorld}}(worldtype.(frames(X))) +worldtypes(X::MultiFrameConditionalDataset) = Vector{Type{<:AbstractWorld}}(worldtype.(modalities(X))) get_instance(X::MultiFrameConditionalDataset, i_frame::Integer, idx_i::Integer, args...) = get_instance(frame(X, i_frame), idx_i, args...) -# get_instance(X::MultiFrameConditionalDataset, idx_i::Integer, args...) = get_instance(frame(X, i), idx_i, args...) # TODO should slice across the frames! +# get_instance(X::MultiFrameConditionalDataset, idx_i::Integer, args...) = get_instance(frame(X, i), idx_i, args...) # TODO should slice across the modalities! -_slice_dataset(X::MultiFrameConditionalDataset{MD}, inds::AbstractVector{<:Integer}, args...; kwargs...) where {MD<:AbstractConditionalDataset} = - MultiFrameConditionalDataset{MD}(Vector{MD}(map(frame->_slice_dataset(frame, inds, args...; kwargs...), frames(X)))) +instances(X::MultiFrameConditionalDataset{MD}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {MD<:AbstractConditionalDataset} = + MultiFrameConditionalDataset{MD}(Vector{MD}(map(frame->instances(frame, inds, return_view), modalities(X)))) function display_structure(Xs::MultiFrameConditionalDataset; indent_str = "") out = "$(typeof(Xs))" # * "\t\t\t$(Base.summarysize(Xs) / 1024 / 1024 |> x->round(x, digits=2)) MBs" - for (i_frame, X) in enumerate(frames(Xs)) - if i_frame == nframes(Xs) + for (i_frame, X) in enumerate(modalities(Xs)) + if i_frame == nmodalities(Xs) out *= "\n$(indent_str)└ " else out *= "\n$(indent_str)├ " end out *= "[$(i_frame)] " # \t\t\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t(worldtype: $(worldtype(X)))" - out *= display_structure(X; indent_str = indent_str * (i_frame == nframes(Xs) ? " " : "│ ")) * "\n" + out *= display_structure(X; indent_str = indent_str * (i_frame == nmodalities(Xs) ? " " : "│ ")) * "\n" end out end -hasnans(Xs::MultiFrameConditionalDataset) = any(hasnans.(frames(Xs))) +hasnans(Xs::MultiFrameConditionalDataset) = any(hasnans.(modalities(Xs))) isminifiable(::MultiFrameConditionalDataset) = true function minify(Xs::MultiFrameConditionalDataset) - if !any(map(isminifiable, frames(Xs))) - if !all(map(isminifiable, frames(Xs))) - @error "Cannot perform minification with frames of types $(typeof.(frames(Xs))). Please use a minifiable format (e.g., SupportedFeaturedDataset)." + if !any(map(isminifiable, modalities(Xs))) + if !all(map(isminifiable, modalities(Xs))) + @error "Cannot perform minification with modalities of types $(typeof.(modalities(Xs))). Please use a minifiable format (e.g., SupportedFeaturedDataset)." else - @warn "Cannot perform minification on some of the frames provided. Please use a minifiable format (e.g., SupportedFeaturedDataset) ($(typeof.(frames(Xs))) were used instead)." + @warn "Cannot perform minification on some of the modalities provided. Please use a minifiable format (e.g., SupportedFeaturedDataset) ($(typeof.(modalities(Xs))) were used instead)." end end - Xs, backmap = zip([!isminifiable(X) ? minify(X) : (X, identity) for X in frames(Xs)]...) + Xs, backmap = zip([!isminifiable(X) ? minify(X) : (X, identity) for X in modalities(Xs)]...) Xs, backmap end diff --git a/src/dimensional-datasets/datasets/dimensional-featured-dataset.jl b/src/dimensional-datasets/datasets/dimensional-featured-dataset.jl index 74b834a..b8b59fd 100644 --- a/src/dimensional-datasets/datasets/dimensional-featured-dataset.jl +++ b/src/dimensional-datasets/datasets/dimensional-featured-dataset.jl @@ -52,7 +52,7 @@ struct DimensionalFeaturedDataset{ features = collect(features) FT = Union{typeof.(features)...} features = Vector{FT}(features) - @assert allow_no_instances || nsamples(domain) > 0 "" * + @assert allow_no_instances || ninstances(domain) > 0 "" * "Can't instantiate $(ty) with no instance. (domain's type $(typeof(domain)))" @assert length(features) == length(grouped_featsaggrsnops) "" * "Can't instantiate $(ty) with mismatching length(features) and" * @@ -157,7 +157,7 @@ struct DimensionalFeaturedDataset{ single_attr_feats_n_featsnops(i_attr,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},Function}) = (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_attr, (x)->(V(cf(x))))) single_attr_feats_n_featsnops(i_attr,::Any) = throw_n_log("Unknown mixed_feature type: $(cf), $(typeof(cf))") - for i_attr in 1:nattributes(domain) + for i_attr in 1:nvariables(domain) for (test_ops,cf) in map((cf)->single_attr_feats_n_featsnops(i_attr,cf),attribute_specific_cfs) push!(featsnops, test_ops) push!(_features, cf) @@ -237,8 +237,8 @@ Base.size(X::DimensionalFeaturedDataset) = Base.size(domain(X)) dimensionality(X::DimensionalFeaturedDataset{V,N,W}) where {V,N,W} = N worldtype(X::DimensionalFeaturedDataset{V,N,W}) where {V,N,W} = W -nsamples(X::DimensionalFeaturedDataset) = nsamples(domain(X)) -nattributes(X::DimensionalFeaturedDataset) = nattributes(domain(X)) +ninstances(X::DimensionalFeaturedDataset) = ninstances(domain(X)) +nvariables(X::DimensionalFeaturedDataset) = nvariables(domain(X)) relations(X::DimensionalFeaturedDataset) = relations(ontology(X)) nrelations(X::DimensionalFeaturedDataset) = length(relations(X)) @@ -249,8 +249,8 @@ max_channel_size(X::DimensionalFeaturedDataset) = max_channel_size(doma get_instance(X::DimensionalFeaturedDataset, args...) = get_instance(domain(X), args...) -_slice_dataset(X::DimensionalFeaturedDataset, inds::AbstractVector{<:Integer}, args...; kwargs...) = - DimensionalFeaturedDataset(_slice_dataset(domain(X), inds, args...; kwargs...), ontology(X), features(X), grouped_featsaggrsnops(X); initialworld = initialworld(X)) +instances(X::DimensionalFeaturedDataset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) = + DimensionalFeaturedDataset(instances(domain(X), inds, return_view), ontology(X), features(X), grouped_featsaggrsnops(X); initialworld = initialworld(X)) frame(X::DimensionalFeaturedDataset, i_sample) = frame(domain(X), i_sample) initialworld(X::DimensionalFeaturedDataset) = X.initialworld diff --git a/src/dimensional-datasets/datasets/dimensional-fwds.jl b/src/dimensional-datasets/datasets/dimensional-fwds.jl index 6c6e413..55e0867 100644 --- a/src/dimensional-datasets/datasets/dimensional-fwds.jl +++ b/src/dimensional-datasets/datasets/dimensional-fwds.jl @@ -35,13 +35,13 @@ struct UniformFullDimensionalFWD{ ############################################################################################ function UniformFullDimensionalFWD(X::DimensionalFeaturedDataset{T,N,W}) where {T,N,W<:OneWorld} - UniformFullDimensionalFWD{T,W,N}(Array{T,2}(undef, nsamples(X), nfeatures(X))) + UniformFullDimensionalFWD{T,W,N}(Array{T,2}(undef, ninstances(X), nfeatures(X))) end function UniformFullDimensionalFWD(X::DimensionalFeaturedDataset{T,N,W}) where {T,N,W<:Interval} - UniformFullDimensionalFWD{T,W,N}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, nsamples(X), nfeatures(X))) + UniformFullDimensionalFWD{T,W,N}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, ninstances(X), nfeatures(X))) end function UniformFullDimensionalFWD(X::DimensionalFeaturedDataset{T,N,W}) where {T,N,W<:Interval2D} - UniformFullDimensionalFWD{T,W,N}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, nsamples(X), nfeatures(X))) + UniformFullDimensionalFWD{T,W,N}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, ninstances(X), nfeatures(X))) end end @@ -49,7 +49,7 @@ end Base.size(fwd::UniformFullDimensionalFWD, args...) = size(fwd.d, args...) Base.ndims(fwd::UniformFullDimensionalFWD, args...) = ndims(fwd.d, args...) -nsamples(fwd::UniformFullDimensionalFWD) = size(fwd, ndims(fwd)-1) +ninstances(fwd::UniformFullDimensionalFWD) = size(fwd, ndims(fwd)-1) nfeatures(fwd::UniformFullDimensionalFWD) = size(fwd, ndims(fwd)) ############################################################################################ @@ -65,14 +65,14 @@ function capacity(fwd::UniformFullDimensionalFWD{T,OneWorld}) where {T} end function capacity(fwd::UniformFullDimensionalFWD{T,<:Interval}) where {T} prod([ - nsamples(fwd), + ninstances(fwd), nfeatures(fwd), div(size(fwd, 1)*(size(fwd, 2)),2), ]) end function capacity(fwd::UniformFullDimensionalFWD{T,<:Interval2D}) where {T} prod([ - nsamples(fwd), + ninstances(fwd), nfeatures(fwd), div(size(fwd, 1)*(size(fwd, 2)),2), div(size(fwd, 3)*(size(fwd, 4)),2), @@ -145,15 +145,15 @@ end ############################################################################################ -function _slice_dataset(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T,W<:OneWorld,N} +function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:OneWorld,N} UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[inds,:] else fwd.d[inds,:] end) end -function _slice_dataset(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T,W<:Interval,N} +function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:Interval,N} UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[:,:,inds,:] else fwd.d[:,:,inds,:] end) end -function _slice_dataset(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T,W<:Interval2D,N} +function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:Interval2D,N} UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[:,:,:,:,inds,:] else fwd.d[:,:,:,:,inds,:] end) end @@ -203,11 +203,11 @@ const FWDFeatureSlice{T} = Union{ # channel_size(fwd::OneWorldFWD) = () -# nsamples(fwd::OneWorldFWD) = size(fwd.d, 1) +# ninstances(fwd::OneWorldFWD) = size(fwd.d, 1) # nfeatures(fwd::OneWorldFWD) = size(fwd.d, 2) # function fwd_init(::Type{OneWorldFWD}, X::DimensionalFeaturedDataset{T,0,OneWorld}) where {T} -# OneWorldFWD{T}(Array{T,2}(undef, nsamples(X), nfeatures(X))) +# OneWorldFWD{T}(Array{T,2}(undef, ninstances(X), nfeatures(X))) # end # function fwd_init_world_slice(fwd::OneWorldFWD, i_sample::Integer, w::AbstractWorld) @@ -226,7 +226,7 @@ const FWDFeatureSlice{T} = Union{ # fwd.d[i_sample, i_feature] = threshold # end -# function _slice_dataset(fwd::OneWorldFWD{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(fwd::OneWorldFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # OneWorldFWD{T}(if return_view == Val(true) @view fwd.d[inds,:] else fwd.d[inds,:] end) # end @@ -240,7 +240,7 @@ const FWDFeatureSlice{T} = Union{ # fwd_channel_interpret_world(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc ############################################################################################ -# FWD, Interval: 4D array (x × y × nsamples × nfeatures) +# FWD, Interval: 4D array (x × y × ninstances × nfeatures) ############################################################################################ # struct IntervalFWD{T} <: UniformFullDimensionalFWD{T,1,<:Interval} @@ -249,11 +249,11 @@ const FWDFeatureSlice{T} = Union{ # channel_size(fwd::IntervalFWD) = (size(fwd, 1),) -# nsamples(fwd::IntervalFWD) = size(fwd, 3) +# ninstances(fwd::IntervalFWD) = size(fwd, 3) # nfeatures(fwd::IntervalFWD) = size(fwd, 4) # function fwd_init(::Type{IntervalFWD}, X::DimensionalFeaturedDataset{T,1,<:Interval}) where {T} -# IntervalFWD{T}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, nsamples(X), nfeatures(X))) +# IntervalFWD{T}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, ninstances(X), nfeatures(X))) # end # function fwd_init_world_slice(fwd::IntervalFWD, i_sample::Integer, w::AbstractWorld) @@ -279,7 +279,7 @@ const FWDFeatureSlice{T} = Union{ # fwd.d[:, :, :, i_feature] = fwdslice # end -# function _slice_dataset(fwd::IntervalFWD{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(fwd::IntervalFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # IntervalFWD{T}(if return_view == Val(true) @view fwd.d[:,:,inds,:] else fwd.d[:,:,inds,:] end) # end # Base.@propagate_inbounds @inline fwdread_channel(fwd::IntervalFWD{T}, i_sample::Integer, i_feature::Integer) where {T} = @@ -289,7 +289,7 @@ const FWDFeatureSlice{T} = Union{ # fwc[w.x, w.y] ############################################################################################ -# FWD, Interval: 6D array (x.x × x.y × y.x × y.y × nsamples × nfeatures) +# FWD, Interval: 6D array (x.x × x.y × y.x × y.y × ninstances × nfeatures) ############################################################################################ # struct Interval2DFWD{T} <: UniformFullDimensionalFWD{T,2,<:Interval2D} @@ -298,12 +298,12 @@ const FWDFeatureSlice{T} = Union{ # channel_size(fwd::Interval2DFWD) = (size(fwd, 1),size(fwd, 3)) -# nsamples(fwd::Interval2DFWD) = size(fwd, 5) +# ninstances(fwd::Interval2DFWD) = size(fwd, 5) # nfeatures(fwd::Interval2DFWD) = size(fwd, 6) # function fwd_init(::Type{Interval2DFWD}, X::DimensionalFeaturedDataset{T,2,<:Interval2D}) where {T} -# Interval2DFWD{T}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, nsamples(X), nfeatures(X))) +# Interval2DFWD{T}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, ninstances(X), nfeatures(X))) # end # function fwd_init_world_slice(fwd::Interval2DFWD, i_sample::Integer, w::AbstractWorld) @@ -329,7 +329,7 @@ const FWDFeatureSlice{T} = Union{ # fwd.d[:, :, :, :, :, i_feature] = fwdslice # end -# function _slice_dataset(fwd::Interval2DFWD{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(fwd::Interval2DFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # Interval2DFWD{T}(if return_view == Val(true) @view fwd.d[:,:,:,:,inds,:] else fwd.d[:,:,:,:,inds,:] end) # end # Base.@propagate_inbounds @inline fwdread_channel(fwd::Interval2DFWD{T}, i_sample::Integer, i_feature::Integer) where {T} = diff --git a/src/dimensional-datasets/datasets/featured-dataset.jl b/src/dimensional-datasets/datasets/featured-dataset.jl index d3bf4d4..e1bc1c0 100644 --- a/src/dimensional-datasets/datasets/featured-dataset.jl +++ b/src/dimensional-datasets/datasets/featured-dataset.jl @@ -35,20 +35,20 @@ end # way of learning it is by considering the fallback fwd structure defined as follows. # TODO oh, but the implementation is broken due to a strange error (see https://discourse.julialang.org/t/tricky-too-many-parameters-for-type-error/25182 ) -# # The most generic fwd structure is a matrix of dictionaries of size (nsamples × nfeatures) +# # The most generic fwd structure is a matrix of dictionaries of size (ninstances × nfeatures) # struct GenericFWD{V,W} <: AbstractFWD{V,W} # d :: AbstractVector{<:AbstractDict{W,AbstractVector{V,1}},1} # nfeatures :: Integer # end -# nsamples(fwd::GenericFWD{V}) where {V} = size(fwd, 1) +# ninstances(fwd::GenericFWD{V}) where {V} = size(fwd, 1) # nfeatures(fwd::GenericFWD{V}) where {V} = fwd.d # Base.size(fwd::GenericFWD{V}, args...) where {V} = size(fwd.d, args...) # # The matrix is initialized with #undef values # function fwd_init(::Type{GenericFWD}, X::DimensionalFeaturedDataset{V}) where {V} -# d = Array{Dict{W,V}, 2}(undef, nsamples(X)) -# for i in 1:nsamples +# d = Array{Dict{W,V}, 2}(undef, ninstances(X)) +# for i in 1:ninstances # d[i] = Dict{W,Array{V,1}}() # end # GenericFWD{V}(d, nfeatures(X)) @@ -80,7 +80,7 @@ end # end # # A function for slicing the dataset -# function _slice_dataset(fwd::GenericFWD{V}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {V} +# function instances(fwd::GenericFWD{V}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {V} # GenericFWD{V}(if return_view == Val(true) @view fwd.d[inds] else fwd.d[inds] end, fwd.nfeatures) # end @@ -143,9 +143,9 @@ struct FeaturedDataset{ ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FWD<:AbstractFWD{V,W,FR},FT<:AbstractFeature{V}} features = collect(features) ty = "FeaturedDataset{$(V),$(W),$(FR),$(FT)}" - @assert allow_no_instances || nsamples(fwd) > 0 "Can't instantiate $(ty) with no instance. (fwd's type $(typeof(fwd)))" + @assert allow_no_instances || ninstances(fwd) > 0 "Can't instantiate $(ty) with no instance. (fwd's type $(typeof(fwd)))" @assert length(grouped_featsaggrsnops) > 0 && sum(length.(grouped_featsaggrsnops)) > 0 && sum(vcat([[length(test_ops) for test_ops in aggrs] for aggrs in grouped_featsaggrsnops]...)) > 0 "Can't instantiate $(ty) with no test operator: grouped_featsaggrsnops" - @assert nfeatures(fwd) == length(features) "Can't instantiate $(ty) with different numbers of instances $(nsamples(fwd)) and of features $(length(features))." + @assert nfeatures(fwd) == length(features) "Can't instantiate $(ty) with different numbers of instances $(ninstances(fwd)) and of features $(length(features))." grouped_featsnaggrs = features_grouped_featsaggrsnops2grouped_featsnaggrs(features, grouped_featsaggrsnops) check_initialworld(FeaturedDataset, initialworld, W) new{ @@ -231,7 +231,7 @@ struct FeaturedDataset{ _features = features(X) - _n_samples = nsamples(X) + _n_samples = ninstances(X) # Load any (possible) external features if any(isa.(_features, ExternalFWDFeature)) @@ -323,7 +323,7 @@ grouped_featsnaggrs(X::FeaturedDataset) = X.grouped_featsnaggrs nfeatures(X::FeaturedDataset) = length(features(X)) nrelations(X::FeaturedDataset) = length(relations(X)) -nsamples(X::FeaturedDataset) = nsamples(fwd(X)) +ninstances(X::FeaturedDataset) = ninstances(fwd(X)) worldtype(X::FeaturedDataset{V,W}) where {V,W<:AbstractWorld} = W nfeatsnaggrs(X::FeaturedDataset) = sum(length.(grouped_featsnaggrs(X))) @@ -334,9 +334,9 @@ function initialworld(X::FeaturedDataset, i_sample) initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_sample] : initialworld(X) end -function _slice_dataset(X::FeaturedDataset, inds::AbstractVector{<:Integer}, args...; kwargs...) +function instances(X::FeaturedDataset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) FeaturedDataset( - _slice_dataset(fwd(X), inds, args...; kwargs...), + instances(fwd(X), inds, return_view), relations(X), features(X), grouped_featsaggrsnops(X); diff --git a/src/dimensional-datasets/datasets/generic-supporting-datasets.jl b/src/dimensional-datasets/datasets/generic-supporting-datasets.jl index 9fb926f..4c2c110 100644 --- a/src/dimensional-datasets/datasets/generic-supporting-datasets.jl +++ b/src/dimensional-datasets/datasets/generic-supporting-datasets.jl @@ -16,8 +16,8 @@ struct GenericSupportingDataset{ function GenericSupportingDataset( fd :: FeaturedDataset{V,W,FR}, ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, nsamples(fd)) - for i_sample in 1:nsamples(fd) + memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, ninstances(fd)) + for i_sample in 1:ninstances(fd) memo[i_sample] = ThreadSafeDict{AbstractFormula,Vector{W}}() end GenericSupportingDataset{W,FR,typeof(memo)}(memo) @@ -30,8 +30,8 @@ Base.size(X::GenericSupportingDataset) = () capacity(X::GenericSupportingDataset) = Inf nmemoizedvalues(X::GenericSupportingDataset) = sum(length.(X.d)) -function _slice_dataset(X::GFSD, inds::AbstractVector{<:Integer}, args...; kwargs...) where {GFSD<:GenericSupportingDataset} - GFSD(X.w0, _slice_dataset(X.memo[inds], args...; kwargs...)) +function instances(X::GFSD, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {GFSD<:GenericSupportingDataset} + GFSD(X.w0, instances(X.memo[inds], return_view)) end hasnans(X::GenericSupportingDataset) = false # TODO double check that this is intended @@ -58,8 +58,8 @@ struct ChainedFeaturedSupportingDataset{ function ChainedFeaturedSupportingDataset( fd :: FeaturedDataset{V,W,FR}, ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, nsamples(fd)) - for i_sample in 1:nsamples(fd) + memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, ninstances(fd)) + for i_sample in 1:ninstances(fd) memo[i_sample] = ThreadSafeDict{AbstractFormula,Vector{W}}() end ChainedFeaturedSupportingDataset{V,W,FR,typeof(memo)}(memo) @@ -72,8 +72,8 @@ Base.size(X::ChainedFeaturedSupportingDataset) = () capacity(X::ChainedFeaturedSupportingDataset) = Inf nmemoizedvalues(X::ChainedFeaturedSupportingDataset) = sum(length.(X.d)) -function _slice_dataset(X::CFSD, inds::AbstractVector{<:Integer}, args...; kwargs...) where {CFSD<:ChainedFeaturedSupportingDataset} - CFSD(X.w0, _slice_dataset(X.memo[inds], args...; kwargs...)) +function instances(X::CFSD, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {CFSD<:ChainedFeaturedSupportingDataset} + CFSD(X.w0, instances(X.memo[inds], return_view)) end hasnans(X::ChainedFeaturedSupportingDataset) = false # TODO double check that this is intended diff --git a/src/dimensional-datasets/datasets/main.jl b/src/dimensional-datasets/datasets/main.jl index 6f99f7c..07231c6 100644 --- a/src/dimensional-datasets/datasets/main.jl +++ b/src/dimensional-datasets/datasets/main.jl @@ -17,8 +17,8 @@ using SoleLogics: AbstractFrame, AbstractDimensionalFrame, FullDimensionalFrame import SoleLogics: worldtype, accessibles, allworlds, alphabet, initialworld using SoleData -import SoleData: _isnan, hasnans, nattributes, max_channel_size, channel_size -import SoleData: instance, get_instance, slice_dataset, _slice_dataset +import SoleData: _isnan, hasnans, nvariables, max_channel_size, channel_size +import SoleData: instance, get_instance, slicedataset, instances import SoleData: dimensionality using SoleModels @@ -29,8 +29,8 @@ using SoleModels: AbstractConditionalDataset, AbstractMultiModalFrame using SoleModels: MultiFrameConditionalDataset, AbstractActiveFeaturedDataset using SoleModels: apply_test_operator, existential_aggregator, aggregator_bottom, aggregator_to_binary import SoleModels: representatives, FeatMetaCondition, FeatCondition, featvaltype -import SoleModels: nsamples, nrelations, nfeatures, check, _slice_dataset, minify -import SoleModels: nframes, frames, display_structure, frame +import SoleModels: ninstances, nrelations, nfeatures, check, instances, minify +import SoleModels: nmodalities, modalities, display_structure, frame import SoleModels: grouped_featsaggrsnops, features, grouped_metaconditions, alphabet, find_feature_id, find_relation_id, isminifiable using SoleModels: grouped_featsnops2grouped_featsaggrsnops, diff --git a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl b/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl index a574615..e6e95ea 100644 --- a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl +++ b/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl @@ -15,8 +15,8 @@ capacity(fwd_rs::AbstractUniformFullDimensionalRelationalSupport) = nmemoizedvalues(support::AbstractUniformFullDimensionalRelationalSupport) = (capacity(support) - count(isnothing, support.d)) ############################################################################################ -# FWD relational support for uniform full dimensional frames: -# a (nsamples × nfeatsnaggrs × nrelations) structure for each world. +# FWD relational support for uniform full dimensional modalities: +# a (ninstances × nfeatsnaggrs × nrelations) structure for each world. # Each world is linearized, resulting in a (3+N*2)-D array ############################################################################################ @@ -46,10 +46,10 @@ struct UniformFullDimensionalRelationalSupport{ # error("TODO actually, using a relational or a global support with a OneWorld frame makes no sense. Figure out what to do here!") _fwd_rs = begin if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 3}(undef, nsamples(fwd), nfeatsnaggrs, nrelations) + _fwd_rs = Array{Union{T,Nothing}, 3}(undef, ninstances(fwd), nfeatsnaggrs, nrelations) fill!(_fwd_rs, nothing) else - Array{T,3}(undef, nsamples(fwd), nfeatsnaggrs, nrelations) + Array{T,3}(undef, ninstances(fwd), nfeatsnaggrs, nrelations) end end UniformFullDimensionalRelationalSupport{T,W,0,typeof(_fwd_rs)}(_fwd_rs) @@ -62,10 +62,10 @@ struct UniformFullDimensionalRelationalSupport{ ) where {T,W<:Interval} _fwd_rs = begin if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 5}(undef, size(fwd, 1), size(fwd, 2), nsamples(fwd), nfeatsnaggrs, nrelations) + _fwd_rs = Array{Union{T,Nothing}, 5}(undef, size(fwd, 1), size(fwd, 2), ninstances(fwd), nfeatsnaggrs, nrelations) fill!(_fwd_rs, nothing) else - Array{T,5}(undef, size(fwd, 1), size(fwd, 2), nsamples(fwd), nfeatsnaggrs, nrelations) + Array{T,5}(undef, size(fwd, 1), size(fwd, 2), ninstances(fwd), nfeatsnaggrs, nrelations) end end UniformFullDimensionalRelationalSupport{T,W,1,typeof(_fwd_rs)}(_fwd_rs) @@ -78,10 +78,10 @@ struct UniformFullDimensionalRelationalSupport{ ) where {T,W<:Interval2D} _fwd_rs = begin if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), nsamples(fwd), nfeatsnaggrs, nrelations) + _fwd_rs = Array{Union{T,Nothing}, 7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), ninstances(fwd), nfeatsnaggrs, nrelations) fill!(_fwd_rs, nothing) else - Array{T,7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), nsamples(fwd), nfeatsnaggrs, nrelations) + Array{T,7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), ninstances(fwd), nfeatsnaggrs, nrelations) end end UniformFullDimensionalRelationalSupport{T,W,2,typeof(_fwd_rs)}(_fwd_rs) @@ -99,7 +99,7 @@ end Base.size(support::UniformFullDimensionalRelationalSupport, args...) = size(support.d, args...) Base.ndims(support::UniformFullDimensionalRelationalSupport, args...) = ndims(support.d, args...) -nsamples(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)-2) +ninstances(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)-2) nfeatsnaggrs(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)-1) nrelations(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)) @@ -110,7 +110,7 @@ function capacity(support::UniformFullDimensionalRelationalSupport{T,OneWorld}) end function capacity(support::UniformFullDimensionalRelationalSupport{T,<:Interval}) where {T} prod([ - nsamples(support), + ninstances(support), nfeatsnaggrs(support), nrelations(support), div(size(support, 1)*(size(support, 2)),2), @@ -118,7 +118,7 @@ function capacity(support::UniformFullDimensionalRelationalSupport{T,<:Interval} end function capacity(support::UniformFullDimensionalRelationalSupport{T,<:Interval2D}) where {T} prod([ - nsamples(support), + ninstances(support), nfeatsnaggrs(support), nrelations(support), div(size(support, 1)*(size(support, 2)),2), @@ -221,37 +221,37 @@ end ############################################################################################ -function _slice_dataset( +function instances( support::UniformFullDimensionalRelationalSupport{T,W,N}, inds::AbstractVector{<:Integer}, - return_view::Val = Val(false) + return_view::Union{Val{true},Val{false}} = Val(false) ) where {T,W<:OneWorld,N} UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) end -function _slice_dataset( +function instances( support::UniformFullDimensionalRelationalSupport{T,W,N}, inds::AbstractVector{<:Integer}, - return_view::Val = Val(false) + return_view::Union{Val{true},Val{false}} = Val(false) ) where {T,W<:Interval,N} UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) end -function _slice_dataset( +function instances( support::UniformFullDimensionalRelationalSupport{T,W,N}, inds::AbstractVector{<:Integer}, - return_view::Val = Val(false) + return_view::Union{Val{true},Val{false}} = Val(false) ) where {T,W<:Interval2D,N} UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[:,:,:,:,inds,:,:] else support.d[:,:,:,:,inds,:,:] end) end ############################################################################################ -# FWD support, OneWorld: 3D array (nsamples × nfeatsnaggrs × nrelations) +# FWD support, OneWorld: 3D array (ninstances × nfeatsnaggrs × nrelations) ############################################################################################ # struct OneWorldFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,OneWorld} # d :: Array{T,3} # end -# nsamples(support::OneWorldFWD_RS) = size(support, 1) +# ninstances(support::OneWorldFWD_RS) = size(support, 1) # nfeatsnaggrs(support::OneWorldFWD_RS) = size(support, 2) # nrelations(support::OneWorldFWD_RS) = size(support, 3) # capacity(support::OneWorldFWD_RS) = prod(size(support.d)) @@ -268,10 +268,10 @@ end # function fwd_rs_init(fd::FeaturedDataset{T,OneWorld}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} # if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 3}(undef, nsamples(fd), nfeatsnaggrs, nrelations), nothing) +# _fwd_rs = fill!(Array{Union{T,Nothing}, 3}(undef, ninstances(fd), nfeatsnaggrs, nrelations), nothing) # OneWorldFWD_RS{Union{T,Nothing}}(_fwd_rs) # else -# _fwd_rs = Array{T,3}(undef, nsamples(fd), nfeatsnaggrs, nrelations) +# _fwd_rs = Array{T,3}(undef, ninstances(fd), nfeatsnaggrs, nrelations) # OneWorldFWD_RS{T}(_fwd_rs) # end # end @@ -279,12 +279,12 @@ end # nothing # Base.@propagate_inbounds @inline Base.setindex!(support::OneWorldFWD_RS{T}, threshold::T, i_sample::Integer, w::OneWorld, i_featsnaggr::Integer, i_relation::Integer) where {T} = # support.d[i_sample, i_featsnaggr, i_relation] = threshold -# function _slice_dataset(support::OneWorldFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(support::OneWorldFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # OneWorldFWD_RS{T}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) # end ############################################################################################ -# FWD support, Interval: 5D array (x × y × nsamples × nfeatsnaggrs × nrelations) +# FWD support, Interval: 5D array (x × y × ninstances × nfeatsnaggrs × nrelations) ############################################################################################ @@ -292,11 +292,11 @@ end # d :: Array{T,5} # end -# nsamples(support::IntervalFWD_RS) = size(support, 3) +# ninstances(support::IntervalFWD_RS) = size(support, 3) # nfeatsnaggrs(support::IntervalFWD_RS) = size(support, 4) # nrelations(support::IntervalFWD_RS) = size(support, 5) # capacity(support::IntervalFWD_RS) = -# prod([nsamples(support), nfeatsnaggrs(support), nrelations(support), div(size(support.d, 1)*(size(support.d, 1)+1),2)]) +# prod([ninstances(support), nfeatsnaggrs(support), nrelations(support), div(size(support.d, 1)*(size(support.d, 1)+1),2)]) # @inline Base.getindex( # support :: IntervalFWD_RS{T}, @@ -315,10 +315,10 @@ end # function fwd_rs_init(fd::FeaturedDataset{T,<:Interval}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} # _fwd = fd.fwd # if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, size(_fwd, 1), size(_fwd, 2), nsamples(fd), nfeatsnaggrs, nrelations), nothing) +# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, size(_fwd, 1), size(_fwd, 2), ninstances(fd), nfeatsnaggrs, nrelations), nothing) # IntervalFWD_RS{Union{T,Nothing}}(_fwd_rs) # else -# _fwd_rs = Array{T,5}(undef, size(_fwd, 1), size(_fwd, 2), nsamples(fd), nfeatsnaggrs, nrelations) +# _fwd_rs = Array{T,5}(undef, size(_fwd, 1), size(_fwd, 2), ninstances(fd), nfeatsnaggrs, nrelations) # IntervalFWD_RS{T}(_fwd_rs) # end # end @@ -326,19 +326,19 @@ end # nothing # Base.@propagate_inbounds @inline Base.setindex!(support::IntervalFWD_RS{T}, threshold::T, i_sample::Integer, w::Interval, i_featsnaggr::Integer, i_relation::Integer) where {T} = # support.d[w.x, w.y, i_sample, i_featsnaggr, i_relation] = threshold -# function _slice_dataset(support::IntervalFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(support::IntervalFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # IntervalFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) # end ############################################################################################ -# FWD support, Interval2D: 7D array (x.x × x.y × y.x × y.y × nsamples × nfeatsnaggrs × nrelations) +# FWD support, Interval2D: 7D array (x.x × x.y × y.x × y.y × ninstances × nfeatsnaggrs × nrelations) ############################################################################################ # struct Interval2DFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,<:Interval2D} # d :: Array{T,7} # end -# nsamples(support::Interval2DFWD_RS) = size(support, 5) +# ninstances(support::Interval2DFWD_RS) = size(support, 5) # nfeatsnaggrs(support::Interval2DFWD_RS) = size(support, 6) # nrelations(support::Interval2DFWD_RS) = size(support, 7) # @inline Base.getindex( @@ -355,10 +355,10 @@ end # fwd_rs_init(fd::FeaturedDataset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} = begin # _fwd = fd.fwd # if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), nsamples(fd), nfeatsnaggrs, nrelations), nothing) +# _fwd_rs = fill!(Array{Union{T,Nothing}, 7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), ninstances(fd), nfeatsnaggrs, nrelations), nothing) # Interval2DFWD_RS{Union{T,Nothing}}(_fwd_rs) # else -# _fwd_rs = Array{T,7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), nsamples(fd), nfeatsnaggrs, nrelations) +# _fwd_rs = Array{T,7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), ninstances(fd), nfeatsnaggrs, nrelations) # Interval2DFWD_RS{T}(_fwd_rs) # end # end @@ -366,13 +366,13 @@ end # nothing # Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_sample::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = # support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_sample, i_featsnaggr, i_relation] = threshold -# function _slice_dataset(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # Interval2DFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,:,:,inds,:,:] else support.d[:,:,:,:,inds,:,:] end) # end ############################################################################################ -# FWD support, Interval2D: 7D array (linearized(x) × linearized(y) × nsamples × nfeatsnaggrs × nrelations) +# FWD support, Interval2D: 7D array (linearized(x) × linearized(y) × ninstances × nfeatsnaggrs × nrelations) ############################################################################################ # # TODO rewrite @@ -381,7 +381,7 @@ end # d :: Array{T,5} # end -# nsamples(support::Interval2DFWD_RS) = size(support, 3) +# ninstances(support::Interval2DFWD_RS) = size(support, 3) # nfeatsnaggrs(support::Interval2DFWD_RS) = size(support, 4) # nrelations(support::Interval2DFWD_RS) = size(support, 5) # capacity(support::Interval2DFWD_RS) = prod(size(support.d)) @@ -399,10 +399,10 @@ end # function fwd_rs_init(fd::FeaturedDataset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} # _fwd = fd.fwd # if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), nsamples(fd), nfeatsnaggrs, nrelations), nothing) +# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), ninstances(fd), nfeatsnaggrs, nrelations), nothing) # Interval2DFWD_RS{Union{T,Nothing}}(_fwd_rs) # else -# _fwd_rs = Array{T,5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), nsamples(fd), nfeatsnaggrs, nrelations) +# _fwd_rs = Array{T,5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), ninstances(fd), nfeatsnaggrs, nrelations) # Interval2DFWD_RS{T}(_fwd_rs) # end # end @@ -410,6 +410,6 @@ end # nothing # Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_sample::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = # support.d[w.x.x+div((w.x.y-2)*(w.x.y-1),2), w.y.x+div((w.y.y-2)*(w.y.y-1),2), i_sample, i_featsnaggr, i_relation] = threshold -# function _slice_dataset(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {T} +# function instances(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # Interval2DFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) # end diff --git a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl b/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl index 865f762..7ac2fdc 100644 --- a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl +++ b/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl @@ -20,10 +20,10 @@ struct GenericRelationalSupport{ _nfeatsnaggrs = nfeatsnaggrs(fd) _fwd_rs = begin if perform_initialization - _fwd_rs = Array{Dict{W,Union{V,Nothing}}, 3}(undef, nsamples(fd), _nfeatsnaggrs, nrelations(fd)) + _fwd_rs = Array{Dict{W,Union{V,Nothing}}, 3}(undef, ninstances(fd), _nfeatsnaggrs, nrelations(fd)) fill!(_fwd_rs, nothing) else - Array{Dict{W,V}, 3}(undef, nsamples(fd), _nfeatsnaggrs, nrelations(fd)) + Array{Dict{W,V}, 3}(undef, ninstances(fd), _nfeatsnaggrs, nrelations(fd)) end end GenericRelationalSupport{V,W,FR}(_fwd_rs) @@ -37,7 +37,7 @@ function hasnans(support::GenericRelationalSupport) any(map(d->(any(_isnan.(collect(values(d))))), support.d)) end -nsamples(support::GenericRelationalSupport) = size(support, 1) +ninstances(support::GenericRelationalSupport) = size(support, 1) nfeatsnaggrs(support::GenericRelationalSupport) = size(support, 2) nrelations(support::GenericRelationalSupport) = size(support, 3) capacity(support::GenericRelationalSupport) = Inf @@ -59,7 +59,7 @@ fwd_rs_init_world_slice(support::GenericRelationalSupport{V,W}, i_sample::Intege @inline function Base.setindex!(support::GenericRelationalSupport{V,W}, threshold::V, i_sample::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {V,W} support.d[i_sample, i_featsnaggr, i_relation][w] = threshold end -function _slice_dataset(support::GenericRelationalSupport{V,W,FR}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {V,W,FR} +function instances(support::GenericRelationalSupport{V,W,FR}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {V,W,FR} GenericRelationalSupport{V,W,FR}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) end @@ -79,7 +79,7 @@ struct GenericGlobalSupport{V,D<:AbstractArray{V,2}} <: AbstractGlobalSupport{V} function GenericGlobalSupport(fd::FeaturedDataset{V}) where {V} @assert worldtype(fd) != OneWorld "TODO adjust this note: note that you should not use a global support when not using global decisions" _nfeatsnaggrs = nfeatsnaggrs(fd) - GenericGlobalSupport{V}(Array{V,2}(undef, nsamples(fd), _nfeatsnaggrs)) + GenericGlobalSupport{V}(Array{V,2}(undef, ninstances(fd), _nfeatsnaggrs)) end end @@ -93,7 +93,7 @@ function hasnans(support::GenericGlobalSupport) any(_isnan.(support.d)) end -nsamples(support::GenericGlobalSupport) = size(support, 1) +ninstances(support::GenericGlobalSupport) = size(support, 1) nfeatsnaggrs(support::GenericGlobalSupport) = size(support, 2) Base.getindex( support :: GenericGlobalSupport, @@ -103,6 +103,6 @@ Base.size(support::GenericGlobalSupport{V}, args...) where {V} = size(support.d, Base.setindex!(support::GenericGlobalSupport{V}, threshold::V, i_sample::Integer, i_featsnaggr::Integer) where {V} = support.d[i_sample, i_featsnaggr] = threshold -function _slice_dataset(support::GenericGlobalSupport{V}, inds::AbstractVector{<:Integer}, return_view::Val = Val(false)) where {V} +function instances(support::GenericGlobalSupport{V}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {V} GenericGlobalSupport{V}(if return_view == Val(true) @view support.d[inds,:] else support.d[inds,:] end) end diff --git a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl b/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl index 18c1745..87a1f66 100644 --- a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl +++ b/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl @@ -35,7 +35,7 @@ struct OneStepFeaturedSupportingDataset{ @assert nfeatsnaggrs(fwd_rs) == length(featsnaggrs) "Can't instantiate $(ty) with unmatching nfeatsnaggrs for fwd_rs and provided featsnaggrs: $(nfeatsnaggrs(fwd_rs)) and $(length(featsnaggrs))" if fwd_gs != nothing @assert nfeatsnaggrs(fwd_gs) == length(featsnaggrs) "Can't instantiate $(ty) with unmatching nfeatsnaggrs for fwd_gs and provided featsnaggrs: $(nfeatsnaggrs(fwd_gs)) and $(length(featsnaggrs))" - @assert nsamples(fwd_gs) == nsamples(fwd_rs) "Can't instantiate $(ty) with unmatching nsamples for fwd_gs and fwd_rs support: $(nsamples(fwd_gs)) and $(nsamples(fwd_rs))" + @assert ninstances(fwd_gs) == ninstances(fwd_rs) "Can't instantiate $(ty) with unmatching ninstances for fwd_gs and fwd_rs support: $(ninstances(fwd_gs)) and $(ninstances(fwd_rs))" end new{V,W,FR,VV,FWDRS,FWDGS,G}(fwd_rs, fwd_gs, featsnaggrs) end @@ -71,7 +71,7 @@ struct OneStepFeaturedSupportingDataset{ end end - _n_samples = nsamples(fd) + _n_samples = ninstances(fd) nrelations = length(_relations) nfeatsnaggrs = sum(length.(_grouped_featsnaggrs)) @@ -156,7 +156,7 @@ fwd_rs(X::OneStepFeaturedSupportingDataset) = X.fwd_rs fwd_gs(X::OneStepFeaturedSupportingDataset) = X.fwd_gs featsnaggrs(X::OneStepFeaturedSupportingDataset) = X.featsnaggrs -nsamples(X::OneStepFeaturedSupportingDataset) = nsamples(fwd_rs(X)) +ninstances(X::OneStepFeaturedSupportingDataset) = ninstances(fwd_rs(X)) # nfeatsnaggrs(X::OneStepFeaturedSupportingDataset) = nfeatsnaggrs(fwd_rs(X)) # TODO delegate to the two components... @@ -164,7 +164,7 @@ function checksupportconsistency( fd::FeaturedDataset{V,W}, X::OneStepFeaturedSupportingDataset{V,W}, ) where {V,W<:AbstractWorld} - @assert nsamples(fd) == nsamples(X) "Consistency check failed! Unmatching nsamples for fd and support: $(nsamples(fd)) and $(nsamples(X))" + @assert ninstances(fd) == ninstances(X) "Consistency check failed! Unmatching ninstances for fd and support: $(ninstances(fd)) and $(ninstances(X))" # @assert nrelations(fd) == (nrelations(fwd_rs(X)) + (isnothing(fwd_gs(X)) ? 0 : 1)) "Consistency check failed! Unmatching nrelations for fd and support: $(nrelations(fd)) and $(nrelations(fwd_rs(X)))+$((isnothing(fwd_gs(X)) ? 0 : 1))" @assert nrelations(fd) >= nrelations(fwd_rs(X)) "Consistency check failed! Inconsistent nrelations for fd and support: $(nrelations(fd)) < $(nrelations(fwd_rs(X)))" _nfeatsnaggrs = nfeatsnaggrs(fd) @@ -180,10 +180,10 @@ Base.size(X::OneStepFeaturedSupportingDataset) = (size(fwd_rs(X)), (isnothing(fw find_featsnaggr_id(X::OneStepFeaturedSupportingDataset, feature::AbstractFeature, aggregator::Aggregator) = findfirst(x->x==(feature, aggregator), featsnaggrs(X)) -function _slice_dataset(X::OneStepFeaturedSupportingDataset, inds::AbstractVector{<:Integer}, args...; kwargs...) +function instances(X::OneStepFeaturedSupportingDataset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) OneStepFeaturedSupportingDataset( - _slice_dataset(fwd_rs(X), inds, args...; kwargs...), - (isnothing(fwd_gs(X)) ? nothing : _slice_dataset(fwd_gs(X), inds, args...; kwargs...)), + instances(fwd_rs(X), inds, return_view), + (isnothing(fwd_gs(X)) ? nothing : instances(fwd_gs(X), inds, return_view)), featsnaggrs(X) ) end diff --git a/src/dimensional-datasets/datasets/passive-dimensional-dataset.jl b/src/dimensional-datasets/datasets/passive-dimensional-dataset.jl index 9ad1a8a..6917cee 100644 --- a/src/dimensional-datasets/datasets/passive-dimensional-dataset.jl +++ b/src/dimensional-datasets/datasets/passive-dimensional-dataset.jl @@ -1,5 +1,5 @@ -using SoleData: slice_dataset -import SoleData: get_instance, nsamples, nattributes, channel_size, max_channel_size, dimensionality, eltype +using SoleData: slicedataset +import SoleData: get_instance, ninstances, nvariables, channel_size, max_channel_size, dimensionality, eltype using SoleData: AbstractDimensionalDataset, AbstractDimensionalInstance, AbstractDimensionalChannel, @@ -70,8 +70,8 @@ end Base.size(X::PassiveDimensionalDataset) = Base.size(X.d) -nattributes(X::PassiveDimensionalDataset) = nattributes(X.d) -nsamples(X::PassiveDimensionalDataset) = nsamples(X.d) +nvariables(X::PassiveDimensionalDataset) = nvariables(X.d) +ninstances(X::PassiveDimensionalDataset) = ninstances(X.d) channel_size(X::PassiveDimensionalDataset) = channel_size(X.d) max_channel_size(X::PassiveDimensionalDataset) = max_channel_size(X.d) dimensionality(X::PassiveDimensionalDataset) = dimensionality(X.d) @@ -79,8 +79,8 @@ eltype(X::PassiveDimensionalDataset) = eltype(X.d) get_instance(X::PassiveDimensionalDataset, args...) = get_instance(X.d, args...) -_slice_dataset(X::PassiveDimensionalDataset{N,W}, inds::AbstractVector{<:Integer}, args...; kwargs...) where {N,W} = - PassiveDimensionalDataset{N,W}(_slice_dataset(X.d, inds, args...; kwargs...)) +instances(X::PassiveDimensionalDataset{N,W}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {N,W} = + PassiveDimensionalDataset{N,W}(instances(X.d, inds, return_view)) hasnans(X::PassiveDimensionalDataset) = hasnans(X.d) diff --git a/src/dimensional-datasets/datasets/supported-featured-dataset.jl b/src/dimensional-datasets/datasets/supported-featured-dataset.jl index 20b2414..bd583a8 100644 --- a/src/dimensional-datasets/datasets/supported-featured-dataset.jl +++ b/src/dimensional-datasets/datasets/supported-featured-dataset.jl @@ -84,7 +84,7 @@ struct SupportedFeaturedDataset{ allow_no_instances = false, ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FT<:AbstractFeature{V},S<:FeaturedSupportingDataset{V,W,FR}} ty = "SupportedFeaturedDataset{$(V),$(W),$(FR),$(FT),$(S)}" - @assert allow_no_instances || nsamples(fd) > 0 "Can't instantiate $(ty) with no instance." + @assert allow_no_instances || ninstances(fd) > 0 "Can't instantiate $(ty) with no instance." @assert checksupportconsistency(fd, support) "Can't instantiate $(ty) with an inconsistent support:\n\nemd:\n$(display_structure(fd))\n\nsupport:\n$(display_structure(support))" new{V,W,FR,FT,S}(fd, support) end @@ -147,7 +147,7 @@ grouped_featsaggrsnops(X::SupportedFeaturedDataset) = grouped_featsaggrsnops grouped_featsnaggrs(X::SupportedFeaturedDataset) = grouped_featsnaggrs(fd(X)) nfeatures(X::SupportedFeaturedDataset) = nfeatures(fd(X)) nrelations(X::SupportedFeaturedDataset) = nrelations(fd(X)) -nsamples(X::SupportedFeaturedDataset) = nsamples(fd(X)) +ninstances(X::SupportedFeaturedDataset) = ninstances(fd(X)) relations(X::SupportedFeaturedDataset) = relations(fd(X)) fwd(X::SupportedFeaturedDataset) = fwd(fd(X)) worldtype(X::SupportedFeaturedDataset{V,W}) where {V,W} = W @@ -157,10 +157,10 @@ usesmemo(X::SupportedFeaturedDataset) = usesmemo(support(X)) frame(X::SupportedFeaturedDataset, i_sample) = frame(fd(X), i_sample) initialworld(X::SupportedFeaturedDataset, args...) = initialworld(fd(X), args...) -function _slice_dataset(X::SupportedFeaturedDataset, inds::AbstractVector{<:Integer}, args...; kwargs...) +function instances(X::SupportedFeaturedDataset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) SupportedFeaturedDataset( - _slice_dataset(fd(X), inds, args...; kwargs...), - _slice_dataset(support(X), inds, args...; kwargs...), + instances(fd(X), inds, return_view), + instances(support(X), inds, return_view), ) end diff --git a/src/dimensional-datasets/main.jl b/src/dimensional-datasets/main.jl index 05d750d..04d83d5 100644 --- a/src/dimensional-datasets/main.jl +++ b/src/dimensional-datasets/main.jl @@ -41,7 +41,7 @@ include("dimensional-ontologies.jl") using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame using SoleLogics: X, Y, Z -# Representatives for dimensional frames +# Representatives for dimensional modalities include("representatives/Full0DFrame.jl") include("representatives/Full1DFrame.jl") include("representatives/Full1DFrame+IA.jl") diff --git a/src/models/base.jl b/src/models/base.jl index 542547a..31a41d6 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -1,6 +1,6 @@ import Base: convert, length, getindex, isopen import SoleLogics: check, syntaxstring -using SoleData: slice_dataset +using SoleData: slicedataset using SoleLogics: LeftmostLinearForm, LeftmostConjunctiveForm, LeftmostDisjunctiveForm # Util @@ -40,8 +40,8 @@ function check( kwargs... ) map( - i_sample->check(c, slice_dataset(d, [i_sample]; return_view = true), args...; kwargs...)[1], - 1:nsamples(d) + i_sample->check(c, slicedataset(d, [i_sample]; return_view = true), args...; kwargs...)[1], + 1:ninstances(d) ) end @@ -91,7 +91,7 @@ struct TrueCondition <: AbstractLogicalBooleanCondition end formula(::TrueCondition) = SyntaxTree(⊤) check(::TrueCondition, i::AbstractInterpretation, args...; kwargs...) = true check(::TrueCondition, d::AbstractInterpretationSet, args...; kwargs...) = - fill(true, nsamples(d)) + fill(true, ninstances(d)) """ struct LogicalTruthCondition{F<:AbstractFormula} <: AbstractLogicalBooleanCondition @@ -225,7 +225,7 @@ end m::AbstractModel, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), functional_args::Tuple = (), functional_kwargs::NamedTuple = (;), kwargs... @@ -276,7 +276,7 @@ function apply( d::AbstractInterpretationSet; kwargs... )::AbstractVector{<:outputtype(m)} - map(i_sample->apply(m, d, i_sample; kwargs...), 1:nsamples(d)) + map(i_sample->apply(m, d, i_sample; kwargs...), 1:ninstances(d)) end """ @@ -381,7 +381,7 @@ outcome(m::ConstantModel) = m.outcome isopen(::ConstantModel) = false apply(m::ConstantModel, i::AbstractInterpretation; kwargs...) = outcome(m) apply(m::ConstantModel, d::AbstractInterpretationSet, i_sample::Integer; kwargs...) = outcome(m) -apply(m::ConstantModel, d::AbstractInterpretationSet; kwargs...) = fill(outcome(m), nsamples(d)) +apply(m::ConstantModel, d::AbstractInterpretationSet; kwargs...) = fill(outcome(m), ninstances(d)) convert(::Type{ConstantModel{O}}, o::O) where {O} = ConstantModel{O}(o) convert(::Type{<:AbstractModel{F}}, m::ConstantModel) where {F} = ConstantModel{F}(m) @@ -745,7 +745,7 @@ function apply( d::AbstractInterpretationSet, i_sample::Integer; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [Dict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [Dict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), kwargs... ) if check_antecedent(m, d, i_sample, check_args...; check_kwargs...) == true @@ -930,7 +930,7 @@ function apply( d::AbstractInterpretationSet, i_sample::Integer; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [Dict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [Dict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), kwargs... ) where {O} if check_antecedent(m, d, i_sample, check_args...; check_kwargs...) == true @@ -952,7 +952,7 @@ function apply( m::Branch{O,<:LogicalTruthCondition}, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), kwargs... ) where {O} cs = check_antecedent(m, d, check_args...; check_kwargs...) @@ -962,7 +962,7 @@ function apply( if !isempty(cpos) out[cpos] .= apply( posconsequent(m), - slice_dataset(d, cpos; return_view = true); + slicedataset(d, cpos; return_view = true); check_args = check_args, check_kwargs = check_kwargs, kwargs... @@ -971,7 +971,7 @@ function apply( if !isempty(cneg) out[cneg] .= apply( negconsequent(m), - slice_dataset(d, cneg; return_view = true); + slicedataset(d, cneg; return_view = true); check_args = check_args, check_kwargs = check_kwargs, kwargs... @@ -1082,16 +1082,16 @@ function apply( m::DecisionList{O}, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), ) where {O} - nsamp = nsamples(d) + nsamp = ninstances(d) pred = Vector{O}(undef, nsamp) uncovered_idxs = 1:nsamp for rule in rulebase(m) length(uncovered_idxs) == 0 && break - uncovered_d = slice_dataset(d, uncovered_idxs; return_view = true) + uncovered_d = slicedataset(d, uncovered_idxs; return_view = true) idxs_sat = findall( # check_antecedent(rule, d, check_args...; check_kwargs...) .== true @@ -1115,10 +1115,10 @@ function apply!( m::DecisionList{O}, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:nsamples(d)]), + check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), compute_metrics::Union{Symbol,Bool} = false, ) where {O} - nsamp = nsamples(d) + nsamp = ninstances(d) pred = Vector{O}(undef, nsamp) delays = Vector{Integer}(undef, nsamp) uncovered_idxs = 1:nsamp @@ -1127,7 +1127,7 @@ function apply!( for (n, rule) in enumerate(rules) length(uncovered_idxs) == 0 && break - uncovered_d = slice_dataset(d, uncovered_idxs; return_view = true) + uncovered_d = slicedataset(d, uncovered_idxs; return_view = true) idxs_sat = findall( # check_antecedent(rule, d, check_args...; check_kwargs...) .== true diff --git a/src/models/rule-evaluation.jl b/src/models/rule-evaluation.jl index 26b119b..aeffb67 100644 --- a/src/models/rule-evaluation.jl +++ b/src/models/rule-evaluation.jl @@ -95,7 +95,7 @@ See also [`AbstractInterpretationSet`](@ref), [`Label`](@ref), [`evaluaterule`](@ref), -[`nsamples`](@ref), +[`ninstances`](@ref), [`outcometype`](@ref), [`consequent`](@ref). """ @@ -107,7 +107,7 @@ function rulemetrics( eval_result = evaluaterule(rule, X, Y) ys = eval_result[:ys] antsat = eval_result[:antsat] - n_instances = nsamples(X) + n_instances = ninstances(X) n_satisfy = sum(antsat) rule_support = n_satisfy / n_instances From 53ba96a4fdf163af1127b40a9493b06b1cfeb96a Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 13 Jun 2023 15:15:56 +0200 Subject: [PATCH 10/77] Still broken. Moved decisions in --- src/logisets/base/decisions.jl | 1 + src/logisets/base/main.jl | 2 +- .../canonical-conditions.jl | 0 .../conditions.jl | 0 src/logisets/base/scalar/decisions.jl | 202 ++++++++++++++++++ .../{scalar-conditions => scalar}/main.jl | 4 +- .../scalar-memosets.jl => scalar/memosets.jl} | 165 +++++++++----- .../{scalar-conditions => scalar}/random.jl | 0 .../representatives.jl | 0 .../test-operators.jl | 0 .../var-features.jl | 0 11 files changed, 317 insertions(+), 57 deletions(-) create mode 100644 src/logisets/base/decisions.jl rename src/logisets/base/{scalar-conditions => scalar}/canonical-conditions.jl (100%) rename src/logisets/base/{scalar-conditions => scalar}/conditions.jl (100%) create mode 100644 src/logisets/base/scalar/decisions.jl rename src/logisets/base/{scalar-conditions => scalar}/main.jl (95%) rename src/logisets/base/{scalar-conditions/scalar-memosets.jl => scalar/memosets.jl} (50%) rename src/logisets/base/{scalar-conditions => scalar}/random.jl (100%) rename src/logisets/base/{scalar-conditions => scalar}/representatives.jl (100%) rename src/logisets/base/{scalar-conditions => scalar}/test-operators.jl (100%) rename src/logisets/base/{scalar-conditions => scalar}/var-features.jl (100%) diff --git a/src/logisets/base/decisions.jl b/src/logisets/base/decisions.jl new file mode 100644 index 0000000..d14b291 --- /dev/null +++ b/src/logisets/base/decisions.jl @@ -0,0 +1 @@ +decisions.jl \ No newline at end of file diff --git a/src/logisets/base/main.jl b/src/logisets/base/main.jl index 2b27297..7f71e83 100644 --- a/src/logisets/base/main.jl +++ b/src/logisets/base/main.jl @@ -37,7 +37,7 @@ include("multilogiset.jl") # Model checking algorithms for logisets and multilogisets include("check.jl") -include("scalar-conditions/main.jl") +include("scalar/main.jl") # # TODO figure out which convert function works best: # convert(::Type{<:MultiLogiset{T}}, X::MD) where {T,MD<:AbstractLogiset{T}} = MultiLogiset{MD}([X]) diff --git a/src/logisets/base/scalar-conditions/canonical-conditions.jl b/src/logisets/base/scalar/canonical-conditions.jl similarity index 100% rename from src/logisets/base/scalar-conditions/canonical-conditions.jl rename to src/logisets/base/scalar/canonical-conditions.jl diff --git a/src/logisets/base/scalar-conditions/conditions.jl b/src/logisets/base/scalar/conditions.jl similarity index 100% rename from src/logisets/base/scalar-conditions/conditions.jl rename to src/logisets/base/scalar/conditions.jl diff --git a/src/logisets/base/scalar/decisions.jl b/src/logisets/base/scalar/decisions.jl new file mode 100644 index 0000000..5d76dcd --- /dev/null +++ b/src/logisets/base/scalar/decisions.jl @@ -0,0 +1,202 @@ +using SoleLogics: identityrel, globalrel + +using SoleLogics: AbstractRelation +using SoleModels: AbstractFeature, TestOperator, ScalarCondition +using SoleModels: syntaxstring +using SoleModels.DimensionalDatasets: alpha + +export ExistentialScalarDecision, + # + relation, feature, test_operator, threshold, + is_propositional_decision, + is_global_decision, + # + display_decision, display_decision_inverse + +############################################################################################ +# Decision +############################################################################################ + +# A decision inducing a branching/split (e.g., ⟨L⟩ (minimum(A2) ≥ 10) ) +abstract type AbstractDecision <: SoleLogics.AbstractFormula end + +import SoleModels: negation + +function Base.show(io::IO, decision::AbstractDecision) + println(io, display_decision(decision)) +end +function display_decision( + frameid::FrameId, + decision::AbstractDecision; + variable_names_map::Union{Nothing,AbstractVector{<:AbstractVector},AbstractVector{<:AbstractDict}} = nothing, + kwargs..., +) + _variable_names_map = isnothing(variable_names_map) ? nothing : variable_names_map[frameid] + "{$frameid} $(display_decision(decision; variable_names_map = _variable_names_map, kwargs...))" +end + +############################################################################################ + +abstract type SimpleDecision <: AbstractDecision end + +function display_decision_inverse(decision::SimpleDecision, kwargs...; args...) + display_decision(negation(decision), kwargs...; args...) +end + +function display_decision_inverse(frameid::FrameId, decision::SimpleDecision, kwargs...; args...) + display_decision(frameid, negation(decision), kwargs...; args...) +end + +display_existential(rel::AbstractRelation; kwargs...) = SoleLogics.syntaxstring(DiamondRelationalOperator{typeof(rel)}(); kwargs...) +display_universal(rel::AbstractRelation; kwargs...) = SoleLogics.syntaxstring(BoxRelationalOperator{typeof(rel)}(); kwargs...) + +############################################################################################ + +# ⊤ +struct TopDecision <: SimpleDecision end +display_decision(::TopDecision) = "⊤" +negation(::TopDecision) = BotDecision() + +# ⊥ +struct BotDecision <: SimpleDecision end +display_decision(::BotDecision) = "⊥" +negation(::BotDecision) = TopDecision() + +# ⟨R⟩⊤ +struct ExistentialTopDecision{R<:AbstractRelation} <: SimpleDecision end +display_decision(::ExistentialTopDecision{R}) where {R<:AbstractRelation} = "$(display_existential(R))⊤" +negation(::ExistentialTopDecision{R}) where {R<:AbstractRelation} = UniversalBotDecision{R}() + +# [R]⊥ +struct UniversalBotDecision{R<:AbstractRelation} <: SimpleDecision end +display_decision(::UniversalBotDecision{R}) where {R<:AbstractRelation} = "$(display_universal(R))⊥" +negation(::UniversalBotDecision{R}) where {R<:AbstractRelation} = ExistentialTopDecision{R}() + +############################################################################################ +############################################################################################ +############################################################################################ + +# Decisions based on dimensional conditions +abstract type ScalarDecision{U} <: SimpleDecision end + +# p +struct PropositionalScalarDecision{U} <: ScalarDecision{U} + p :: ScalarCondition{U} +end + +proposition(d::PropositionalScalarDecision) = d.p +feature(d::PropositionalScalarDecision) = feature(proposition(d)) +test_operator(d::PropositionalScalarDecision) = test_operator(proposition(d)) +threshold(d::PropositionalScalarDecision) = threshold(proposition(d)) + +negation(p::PropositionalScalarDecision{U}) where {U} = + PropositionalScalarDecision{U}(negation(p)) + +############################################################################################ + +abstract type ModalScalarDecision{U} <: ScalarDecision{U} end + +relation(d::ModalScalarDecision) = d.relation +proposition(d::ModalScalarDecision) = d.p +feature(d::ModalScalarDecision) = feature(proposition(d)) +test_operator(d::ModalScalarDecision) = test_operator(proposition(d)) +threshold(d::ModalScalarDecision) = threshold(proposition(d)) + +is_propositional_decision(d::ModalScalarDecision) = (relation(d) == identityrel) +is_global_decision(d::ModalScalarDecision) = (relation(d) == globalrel) + +# ⟨R⟩p +struct ExistentialScalarDecision{U} <: ModalScalarDecision{U} + + # Relation, interpreted as an existential modal operator + relation :: AbstractRelation + + p :: ScalarCondition{U} + + function ExistentialScalarDecision{U}() where {U} + new{U}() + end + + function ExistentialScalarDecision{U}( + relation :: AbstractRelation, + p :: ScalarCondition{U} + ) where {U} + new{U}(relation, p) + end + + function ExistentialScalarDecision( + relation :: AbstractRelation, + p :: ScalarCondition{U} + ) where {U} + ExistentialScalarDecision{U}(relation, p) + end + + function ExistentialScalarDecision{U}( + relation :: AbstractRelation, + feature :: AbstractFeature, + test_operator :: TestOperator, + threshold :: U + ) where {U} + p = ScalarCondition(feature, test_operator, threshold) + ExistentialScalarDecision{U}(relation, p) + end + + function ExistentialScalarDecision( + relation :: AbstractRelation, + feature :: AbstractFeature, + test_operator :: TestOperator, + threshold :: U + ) where {U} + ExistentialScalarDecision{U}(relation, feature, test_operator, threshold) + end + + function ExistentialScalarDecision( + decision :: ExistentialScalarDecision{U}, + threshold_f :: Function + ) where {U} + q = ScalarCondition(decision.p, threshold_f(threshold(decision.p))) + ExistentialScalarDecision{U}(relation(decision), q) + end +end + +# [R]p +struct UniversalScalarDecision{U} <: ModalScalarDecision{U} + relation :: AbstractRelation + p :: ScalarCondition{U} +end + +function negation(decision::ExistentialScalarDecision{U}) where {U} + UniversalScalarDecision{U}( + relation(decision), + negation(proposition(decision)) + ) +end +function negation(decision::UniversalScalarDecision{U}) where {U} + ExistentialScalarDecision{U}( + relation(decision), + negation(proposition(decision)) + ) +end + +function display_decision( + decision::Union{ExistentialScalarDecision,UniversalScalarDecision}; + threshold_display_method::Function = x -> x, + variable_names_map::Union{Nothing,AbstractVector,AbstractDict} = nothing, + use_feature_abbreviations::Bool = false, +) + prop_decision_str = syntaxstring( + decision.p; + threshold_display_method = threshold_display_method, + variable_names_map = variable_names_map, + use_feature_abbreviations = use_feature_abbreviations, + ) + if !is_propositional_decision(decision) + rel_display_fun = (decision isa ExistentialScalarDecision ? display_existential : display_universal) + "$(rel_display_fun(relation(decision))) ($prop_decision_str)" + else + "$prop_decision_str" + end +end + + +############################################################################################ diff --git a/src/logisets/base/scalar-conditions/main.jl b/src/logisets/base/scalar/main.jl similarity index 95% rename from src/logisets/base/scalar-conditions/main.jl rename to src/logisets/base/scalar/main.jl index aea4e21..c382675 100644 --- a/src/logisets/base/scalar-conditions/main.jl +++ b/src/logisets/base/scalar/main.jl @@ -26,7 +26,9 @@ include("random.jl") include("representatives.jl") -include("scalar-memosets.jl") +include("decisions.jl") + +include("memosets.jl") export UnivariateMin, UnivariateMax, UnivariateSoftMin, UnivariateSoftMax, diff --git a/src/logisets/base/scalar-conditions/scalar-memosets.jl b/src/logisets/base/scalar/memosets.jl similarity index 50% rename from src/logisets/base/scalar-conditions/scalar-memosets.jl rename to src/logisets/base/scalar/memosets.jl index 31f15f9..9193fba 100644 --- a/src/logisets/base/scalar-conditions/scalar-memosets.jl +++ b/src/logisets/base/scalar/memosets.jl @@ -78,7 +78,7 @@ function check( w::W; kwargs... ) where {W<:AbstractWorld} - error("TODO implement") + error("TODO implement chained threshold checking algorithm.") end function instances( @@ -108,14 +108,14 @@ Abstract type for one-step memoization structure for checking formulas of type ` abstract type AbstractScalarOneStepMemoset{U,W,F<:AbstractFeature,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,F,FR} end # Access inner structure -function innerstruct(memoset::AbstractScalarOneStepMemoset) - error("Please, provide method innerstruct(::$(typeof(memoset))).") +function innerstruct(Xm::AbstractScalarOneStepMemoset) + error("Please, provide method innerstruct(::$(typeof(Xm))).") end isminifiable(::AbstractScalarOneStepMemoset) = true -function minify(memoset::AbstractScalarOneStepMemoset) - minify(innerstruct(memoset)) +function minify(Xm::AbstractScalarOneStepMemoset) + minify(innerstruct(Xm)) end """ @@ -150,67 +150,122 @@ struct ScalarOneStepRelationalMemoset{ D<:AbstractArray{<:AbstractDict{W,VV}, 3} where VV<:Union{U,Nothing}, } <: AbstractScalarOneStepRelationalMemoset{W,U,FR} + metaconditions :: Vector{<:ScalarMetaCondition} + + relations :: Vector{<:AbstractRelation} + d :: D - function ScalarOneStepRelationalMemoset{W,U,FR}(d::D) where {W<:AbstractWorld,U,FR<:AbstractFrame{W},D<:AbstractArray{U,2}} + function ScalarOneStepRelationalMemoset{W,U,FR}( + metaconditions::AbstractVector{<:ScalarMetaCondition}, + relations::AbstractVector{<:AbstractRelation}, + d::D, + ) where {W<:AbstractWorld,U,FR<:AbstractFrame{W},D<:AbstractArray{U,2}} new{W,U,FR,D}(d) end function ScalarOneStepRelationalMemoset( X::AbstractLogiset{W,U,F,FR}, - # TODO: metaconditions, relations, + metaconditions::AbstractVector{<:ScalarMetaCondition}, + relations::AbstractVector{<:AbstractRelation}, perform_initialization = false ) where {W,U,F<:AbstractFeature,FR<:AbstractFrame{W}} - _nfeatsnaggrs = nfeatsnaggrs(X) - _fwd_rs = begin + nmetaconditions = length(metaconditions) + nrelations = length(relations) + d = begin if perform_initialization - _fwd_rs = Array{Dict{W,Union{U,Nothing}}, 3}(undef, ninstances(X), _nfeatsnaggrs, nrelations(X)) - fill!(_fwd_rs, nothing) + d = Array{Dict{W,Union{U,Nothing}}, 3}(undef, ninstances(X), nmetaconditions, nrelations) + fill!(d, nothing) else - Array{Dict{W,U}, 3}(undef, ninstances(X), _nfeatsnaggrs, nrelations(X)) + Array{Dict{W,U}, 3}(undef, ninstances(X), nmetaconditions, nrelations) end end - ScalarOneStepRelationalMemoset{W,U,FR}(_fwd_rs) + ScalarOneStepRelationalMemoset{W,U,FR}(d, metaconditions, relations) end end -# default_fwd_rs_type(::Type{<:AbstractWorld}) = ScalarOneStepRelationalMemoset # TODO implement similar pattern used for fwd -function hasnans(memoset::ScalarOneStepRelationalMemoset) - # @show any(map(d->(any(_isnan.(collect(values(d))))), memoset.d)) - any(map(d->(any(_isnan.(collect(values(d))))), memoset.d)) +innerstruct(Xm::ScalarOneStepRelationalMemoset) = Xm.d + +metaconditions(Xm::ScalarOneStepRelationalMemoset) = Xm.metaconditions +relations(Xm::ScalarOneStepRelationalMemoset) = Xm.relations + +ninstances(Xm::ScalarOneStepRelationalMemoset) = size(Xm.d, 1) +nmetaconditions(Xm::ScalarOneStepRelationalMemoset) = size(Xm.d, 2) +nrelations(Xm::ScalarOneStepRelationalMemoset) = size(Xm.d, 3) + +capacity(Xm::ScalarOneStepRelationalMemoset) = Inf +nmemoizedvalues(Xm::ScalarOneStepRelationalMemoset) = sum(length.(Xm.d)) + + +usesfullmemo(::ScalarOneStepRelationalMemoset) = false + +function hasnans(Xm::ScalarOneStepRelationalMemoset) + any(map(d->(any(_isnan.(collect(values(d))))), Xm.d)) end -innerstruct(memoset::ScalarOneStepRelationalMemoset) = memoset.d -ninstances(memoset::ScalarOneStepRelationalMemoset) = size(memoset, 1) -nfeatsnaggrs(memoset::ScalarOneStepRelationalMemoset) = size(memoset, 2) -nrelations(memoset::ScalarOneStepRelationalMemoset) = size(memoset, 3) -capacity(memoset::ScalarOneStepRelationalMemoset) = Inf -nmemoizedvalues(memoset::ScalarOneStepRelationalMemoset) = sum(length.(memoset.d)) +function check( + f::AbstractFormula, + Xm::ScalarOneStepRelationalMemoset{W}, + i_instance::Integer, + w::W; + kwargs... +) where {W<:AbstractWorld} + error("TODO implement chained threshold checking algorithm.") +end -@inline function Base.getindex( - memoset :: ScalarOneStepRelationalMemoset{W,U}, - i_instance :: Integer, - w :: W, - i_featsnaggr :: Integer, - i_relation :: Integer -) where {W<:AbstractWorld,U} - memoset.d[i_instance, i_featsnaggr, i_relation][w] +function instances(Xm::ScalarOneStepRelationalMemoset{W,U,FR}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {W,U,FR} + ScalarOneStepRelationalMemoset{W,U,FR}(if return_view == Val(true) @view Xm.d[inds,:,:] else Xm.d[inds,:,:] end) end -Base.size(memoset::ScalarOneStepRelationalMemoset, args...) = size(memoset.d, args...) -fwd_rs_init_world_slice(memoset::ScalarOneStepRelationalMemoset{W,U}, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) where {W,U} = - memoset.d[i_instance, i_featsnaggr, i_relation] = Dict{W,U}() -@inline function Base.setindex!(memoset::ScalarOneStepRelationalMemoset{W,U}, threshold::U, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {W,U} - memoset.d[i_instance, i_featsnaggr, i_relation][w] = threshold +function concatdatasets(Xms::ScalarOneStepRelationalMemoset...) + ScalarOneStepRelationalMemoset(vcat([Xm.d for Xm in Xms]...)) end -function instances(memoset::ScalarOneStepRelationalMemoset{W,U,FR}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {W,U,FR} - ScalarOneStepRelationalMemoset{W,U,FR}(if return_view == Val(true) @view memoset.d[inds,:,:] else memoset.d[inds,:,:] end) + + +function displaystructure(Xm::ScalarOneStepRelationalMemoset; indent_str = "", include_ninstances = true) + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) + pieces = [] + push!(pieces, "") + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") + push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") + push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + if include_ninstances + push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") + end + push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") + push!(pieces, "$(padattribute("# metaconditions:", nmetaconditions(Xm)))") + push!(pieces, "$(padattribute("# relations:", nrelations(Xm)))") + push!(pieces, "") + push!(pieces, "$(padattribute("metaconditions:", metaconditions(Xm)))") + push!(pieces, "$(padattribute("relations:", relations(Xm)))") + + return "ScalarOneStepRelationalMemoset ($(humansize(Xm)))" * + join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" end + +# @inline function Base.getindex( +# Xm :: ScalarOneStepRelationalMemoset{W,U}, +# i_instance :: Integer, +# w :: W, +# i_featsnaggr :: Integer, +# i_relation :: Integer +# ) where {W<:AbstractWorld,U} +# Xm.d[i_instance, i_featsnaggr, i_relation][w] +# end +# Base.size(Xm::ScalarOneStepRelationalMemoset, args...) = size(Xm.d, args...) + +# fwd_rs_init_world_slice(Xm::ScalarOneStepRelationalMemoset{W,U}, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) where {W,U} = +# Xm.d[i_instance, i_featsnaggr, i_relation] = Dict{W,U}() +# @inline function Base.setindex!(Xm::ScalarOneStepRelationalMemoset{W,U}, threshold::U, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {W,U} +# Xm.d[i_instance, i_featsnaggr, i_relation][w] = threshold +# end + ############################################################################################ -# Note: the global memoset is world-agnostic +# Note: the global Xm is world-agnostic struct ScalarOneStepGlobalMemoset{ W<:AbstractWorld, U, @@ -227,35 +282,35 @@ struct ScalarOneStepGlobalMemoset{ end function ScalarOneStepGlobalMemoset(X::AbstractLogiset{W,U}) where {W<:AbstractWorld,U} - @assert worldtype(X) != OneWorld "TODO adjust this note: note that you should not use a global memoset when not using global decisions" + @assert worldtype(X) != OneWorld "TODO adjust this note: note that you should not use a global Xm when not using global decisions" _nfeatsnaggrs = nfeatsnaggrs(X) ScalarOneStepGlobalMemoset{W,U}(Array{U,2}(undef, ninstances(X), _nfeatsnaggrs)) end end -capacity(memoset::ScalarOneStepGlobalMemoset) = prod(size(memoset.d)) -nmemoizedvalues(memoset::ScalarOneStepGlobalMemoset) = sum(memoset.d) -innerstruct(memoset::ScalarOneStepGlobalMemoset) = memoset.d +capacity(Xm::ScalarOneStepGlobalMemoset) = prod(size(Xm.d)) +nmemoizedvalues(Xm::ScalarOneStepGlobalMemoset) = sum(Xm.d) +innerstruct(Xm::ScalarOneStepGlobalMemoset) = Xm.d # default_fwd_gs_type(::Type{<:AbstractWorld}) = ScalarOneStepGlobalMemoset # TODO implement similar pattern used for fwd -function hasnans(memoset::ScalarOneStepGlobalMemoset) - # @show any(_isnan.(memoset.d)) - any(_isnan.(memoset.d)) +function hasnans(Xm::ScalarOneStepGlobalMemoset) + # @show any(_isnan.(Xm.d)) + any(_isnan.(Xm.d)) end -ninstances(memoset::ScalarOneStepGlobalMemoset) = size(memoset, 1) -nfeatsnaggrs(memoset::ScalarOneStepGlobalMemoset) = size(memoset, 2) +ninstances(Xm::ScalarOneStepGlobalMemoset) = size(Xm, 1) +nfeatsnaggrs(Xm::ScalarOneStepGlobalMemoset) = size(Xm, 2) Base.getindex( - memoset :: ScalarOneStepGlobalMemoset, + Xm :: ScalarOneStepGlobalMemoset, i_instance :: Integer, - i_featsnaggr :: Integer) = memoset.d[i_instance, i_featsnaggr] -Base.size(memoset::ScalarOneStepGlobalMemoset{U}, args...) where {U} = size(memoset.d, args...) + i_featsnaggr :: Integer) = Xm.d[i_instance, i_featsnaggr] +Base.size(Xm::ScalarOneStepGlobalMemoset{U}, args...) where {U} = size(Xm.d, args...) -Base.setindex!(memoset::ScalarOneStepGlobalMemoset{U}, threshold::U, i_instance::Integer, i_featsnaggr::Integer) where {U} = - memoset.d[i_instance, i_featsnaggr] = threshold -function instances(memoset::ScalarOneStepGlobalMemoset{U}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {U} - ScalarOneStepGlobalMemoset{U}(if return_view == Val(true) @view memoset.d[inds,:] else memoset.d[inds,:] end) +Base.setindex!(Xm::ScalarOneStepGlobalMemoset{U}, threshold::U, i_instance::Integer, i_featsnaggr::Integer) where {U} = + Xm.d[i_instance, i_featsnaggr] = threshold +function instances(Xm::ScalarOneStepGlobalMemoset{U}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {U} + ScalarOneStepGlobalMemoset{U}(if return_view == Val(true) @view Xm.d[inds,:] else Xm.d[inds,:] end) end abstract type FeaturedMemoset{U<:Number,W<:AbstractWorld,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,F where F<:AbstractFeature,FR} end diff --git a/src/logisets/base/scalar-conditions/random.jl b/src/logisets/base/scalar/random.jl similarity index 100% rename from src/logisets/base/scalar-conditions/random.jl rename to src/logisets/base/scalar/random.jl diff --git a/src/logisets/base/scalar-conditions/representatives.jl b/src/logisets/base/scalar/representatives.jl similarity index 100% rename from src/logisets/base/scalar-conditions/representatives.jl rename to src/logisets/base/scalar/representatives.jl diff --git a/src/logisets/base/scalar-conditions/test-operators.jl b/src/logisets/base/scalar/test-operators.jl similarity index 100% rename from src/logisets/base/scalar-conditions/test-operators.jl rename to src/logisets/base/scalar/test-operators.jl diff --git a/src/logisets/base/scalar-conditions/var-features.jl b/src/logisets/base/scalar/var-features.jl similarity index 100% rename from src/logisets/base/scalar-conditions/var-features.jl rename to src/logisets/base/scalar/var-features.jl From ea52738b03a5210bb8060333fc488919cc630458 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Wed, 14 Jun 2023 01:21:36 +0200 Subject: [PATCH 11/77] Broken. decision<->templated formulas, AbstractOneStepMemoset, UniqueVectors --- Project.toml | 1 + src/logisets/base/decisions.jl | 1 - src/logisets/base/main.jl | 27 + src/logisets/base/memosets.jl | 9 +- src/logisets/base/scalar/decisions.jl | 202 ------- src/logisets/base/scalar/main.jl | 7 +- src/logisets/base/scalar/memosets.jl | 221 +------- src/logisets/base/scalar/one-step-memoset.jl | 532 ++++++++++++++++++ .../base/scalar/templated-formulas.jl | 116 ++++ src/logisets/base/supported-logiset.jl | 58 +- src/logisets/base/templated-formulas.jl | 35 ++ .../datasets/dimensional-fwds.jl | 7 +- .../datasets/dimensional-supports.jl | 9 +- .../dimensional-logisets/datasets/main.jl | 2 +- .../scalar-gamma-access.jl | 12 +- ...ar-one-step-featured-supporting-dataset.jl | 279 --------- test/datasets.jl | 29 +- 17 files changed, 821 insertions(+), 726 deletions(-) delete mode 100644 src/logisets/base/decisions.jl delete mode 100644 src/logisets/base/scalar/decisions.jl create mode 100644 src/logisets/base/scalar/one-step-memoset.jl create mode 100644 src/logisets/base/scalar/templated-formulas.jl create mode 100644 src/logisets/base/templated-formulas.jl delete mode 100644 src/logisets/dimensional-logisets/scalar-one-step-featured-supporting-dataset.jl diff --git a/Project.toml b/Project.toml index 4e6ae0c..9a5288f 100644 --- a/Project.toml +++ b/Project.toml @@ -23,6 +23,7 @@ SoleLogics = "b002da8f-3cb3-4d91-bbe3-2953433912b5" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" ThreadSafeDicts = "4239201d-c60e-5e0a-9702-85d713665ba7" +UniqueVectors = "2fbcfb34-fd0c-5fbb-b5d7-e826d8f5b0a9" [compat] BenchmarkTools = "1" diff --git a/src/logisets/base/decisions.jl b/src/logisets/base/decisions.jl deleted file mode 100644 index d14b291..0000000 --- a/src/logisets/base/decisions.jl +++ /dev/null @@ -1 +0,0 @@ -decisions.jl \ No newline at end of file diff --git a/src/logisets/base/main.jl b/src/logisets/base/main.jl index 7f71e83..4105cce 100644 --- a/src/logisets/base/main.jl +++ b/src/logisets/base/main.jl @@ -15,6 +15,9 @@ include("features.jl") # Conditions on the features, to be wrapped in Proposition's include("conditions.jl") +# Templates for formulas of conditions (e.g., templates for ⊤, p, ⟨R⟩p, etc.) +include("templated-formulas.jl") + export check, accessibles, allworlds, representatives, initialworld # Interface for representative accessibles, for optimized model checking on specific frames @@ -39,6 +42,30 @@ include("check.jl") include("scalar/main.jl") +function default_relmemoset_type(X::AbstractLogiset) + frames = [frame(X, i_instance) for i_instance in 1:ninstances(X)] + if allequal(frames) && first(unique(frames)) isa FullDimensionalFrame + UniformFullDimensionalRelationalSupport + else + ScalarOneStepRelationalMemoset + end +end + +function default_onestep_memoset_type(X::AbstractLogiset) + if featvaltype(X) <: Real + ScalarOneStepMemoset + else + OneStepMemoset + end +end +function default_full_memoset_type(X::AbstractLogiset) + # if ... + # ScalarMemoset TODO + # else + Memoset + # end +end + # # TODO figure out which convert function works best: # convert(::Type{<:MultiLogiset{T}}, X::MD) where {T,MD<:AbstractLogiset{T}} = MultiLogiset{MD}([X]) # convert(::Type{<:MultiLogiset}, X::AbstractLogiset) = MultiLogiset([X]) diff --git a/src/logisets/base/memosets.jl b/src/logisets/base/memosets.jl index 2d93eb7..0fede69 100644 --- a/src/logisets/base/memosets.jl +++ b/src/logisets/base/memosets.jl @@ -53,6 +53,13 @@ end ############################################################################################ +""" +Abstract type for one-step memoization structure for checking formulas of type `⟨R⟩ (f ⋈ t)`. +""" +abstract type AbstractOneStepMemoset{W<:AbstractWorld,U,F<:AbstractFeature,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,F,FR} end + +############################################################################################ + """ A generic, full memoization structure that works for any *crisp* logic; For each instance of a dataset, @@ -161,5 +168,3 @@ fullmemo(Xm::Memoset) = Xm hasnans(::Memoset) = false # Base.size(::Memoset) = () - -############################################################################################ diff --git a/src/logisets/base/scalar/decisions.jl b/src/logisets/base/scalar/decisions.jl deleted file mode 100644 index 5d76dcd..0000000 --- a/src/logisets/base/scalar/decisions.jl +++ /dev/null @@ -1,202 +0,0 @@ -using SoleLogics: identityrel, globalrel - -using SoleLogics: AbstractRelation -using SoleModels: AbstractFeature, TestOperator, ScalarCondition -using SoleModels: syntaxstring -using SoleModels.DimensionalDatasets: alpha - -export ExistentialScalarDecision, - # - relation, feature, test_operator, threshold, - is_propositional_decision, - is_global_decision, - # - display_decision, display_decision_inverse - -############################################################################################ -# Decision -############################################################################################ - -# A decision inducing a branching/split (e.g., ⟨L⟩ (minimum(A2) ≥ 10) ) -abstract type AbstractDecision <: SoleLogics.AbstractFormula end - -import SoleModels: negation - -function Base.show(io::IO, decision::AbstractDecision) - println(io, display_decision(decision)) -end -function display_decision( - frameid::FrameId, - decision::AbstractDecision; - variable_names_map::Union{Nothing,AbstractVector{<:AbstractVector},AbstractVector{<:AbstractDict}} = nothing, - kwargs..., -) - _variable_names_map = isnothing(variable_names_map) ? nothing : variable_names_map[frameid] - "{$frameid} $(display_decision(decision; variable_names_map = _variable_names_map, kwargs...))" -end - -############################################################################################ - -abstract type SimpleDecision <: AbstractDecision end - -function display_decision_inverse(decision::SimpleDecision, kwargs...; args...) - display_decision(negation(decision), kwargs...; args...) -end - -function display_decision_inverse(frameid::FrameId, decision::SimpleDecision, kwargs...; args...) - display_decision(frameid, negation(decision), kwargs...; args...) -end - -display_existential(rel::AbstractRelation; kwargs...) = SoleLogics.syntaxstring(DiamondRelationalOperator{typeof(rel)}(); kwargs...) -display_universal(rel::AbstractRelation; kwargs...) = SoleLogics.syntaxstring(BoxRelationalOperator{typeof(rel)}(); kwargs...) - -############################################################################################ - -# ⊤ -struct TopDecision <: SimpleDecision end -display_decision(::TopDecision) = "⊤" -negation(::TopDecision) = BotDecision() - -# ⊥ -struct BotDecision <: SimpleDecision end -display_decision(::BotDecision) = "⊥" -negation(::BotDecision) = TopDecision() - -# ⟨R⟩⊤ -struct ExistentialTopDecision{R<:AbstractRelation} <: SimpleDecision end -display_decision(::ExistentialTopDecision{R}) where {R<:AbstractRelation} = "$(display_existential(R))⊤" -negation(::ExistentialTopDecision{R}) where {R<:AbstractRelation} = UniversalBotDecision{R}() - -# [R]⊥ -struct UniversalBotDecision{R<:AbstractRelation} <: SimpleDecision end -display_decision(::UniversalBotDecision{R}) where {R<:AbstractRelation} = "$(display_universal(R))⊥" -negation(::UniversalBotDecision{R}) where {R<:AbstractRelation} = ExistentialTopDecision{R}() - -############################################################################################ -############################################################################################ -############################################################################################ - -# Decisions based on dimensional conditions -abstract type ScalarDecision{U} <: SimpleDecision end - -# p -struct PropositionalScalarDecision{U} <: ScalarDecision{U} - p :: ScalarCondition{U} -end - -proposition(d::PropositionalScalarDecision) = d.p -feature(d::PropositionalScalarDecision) = feature(proposition(d)) -test_operator(d::PropositionalScalarDecision) = test_operator(proposition(d)) -threshold(d::PropositionalScalarDecision) = threshold(proposition(d)) - -negation(p::PropositionalScalarDecision{U}) where {U} = - PropositionalScalarDecision{U}(negation(p)) - -############################################################################################ - -abstract type ModalScalarDecision{U} <: ScalarDecision{U} end - -relation(d::ModalScalarDecision) = d.relation -proposition(d::ModalScalarDecision) = d.p -feature(d::ModalScalarDecision) = feature(proposition(d)) -test_operator(d::ModalScalarDecision) = test_operator(proposition(d)) -threshold(d::ModalScalarDecision) = threshold(proposition(d)) - -is_propositional_decision(d::ModalScalarDecision) = (relation(d) == identityrel) -is_global_decision(d::ModalScalarDecision) = (relation(d) == globalrel) - -# ⟨R⟩p -struct ExistentialScalarDecision{U} <: ModalScalarDecision{U} - - # Relation, interpreted as an existential modal operator - relation :: AbstractRelation - - p :: ScalarCondition{U} - - function ExistentialScalarDecision{U}() where {U} - new{U}() - end - - function ExistentialScalarDecision{U}( - relation :: AbstractRelation, - p :: ScalarCondition{U} - ) where {U} - new{U}(relation, p) - end - - function ExistentialScalarDecision( - relation :: AbstractRelation, - p :: ScalarCondition{U} - ) where {U} - ExistentialScalarDecision{U}(relation, p) - end - - function ExistentialScalarDecision{U}( - relation :: AbstractRelation, - feature :: AbstractFeature, - test_operator :: TestOperator, - threshold :: U - ) where {U} - p = ScalarCondition(feature, test_operator, threshold) - ExistentialScalarDecision{U}(relation, p) - end - - function ExistentialScalarDecision( - relation :: AbstractRelation, - feature :: AbstractFeature, - test_operator :: TestOperator, - threshold :: U - ) where {U} - ExistentialScalarDecision{U}(relation, feature, test_operator, threshold) - end - - function ExistentialScalarDecision( - decision :: ExistentialScalarDecision{U}, - threshold_f :: Function - ) where {U} - q = ScalarCondition(decision.p, threshold_f(threshold(decision.p))) - ExistentialScalarDecision{U}(relation(decision), q) - end -end - -# [R]p -struct UniversalScalarDecision{U} <: ModalScalarDecision{U} - relation :: AbstractRelation - p :: ScalarCondition{U} -end - -function negation(decision::ExistentialScalarDecision{U}) where {U} - UniversalScalarDecision{U}( - relation(decision), - negation(proposition(decision)) - ) -end -function negation(decision::UniversalScalarDecision{U}) where {U} - ExistentialScalarDecision{U}( - relation(decision), - negation(proposition(decision)) - ) -end - -function display_decision( - decision::Union{ExistentialScalarDecision,UniversalScalarDecision}; - threshold_display_method::Function = x -> x, - variable_names_map::Union{Nothing,AbstractVector,AbstractDict} = nothing, - use_feature_abbreviations::Bool = false, -) - prop_decision_str = syntaxstring( - decision.p; - threshold_display_method = threshold_display_method, - variable_names_map = variable_names_map, - use_feature_abbreviations = use_feature_abbreviations, - ) - if !is_propositional_decision(decision) - rel_display_fun = (decision isa ExistentialScalarDecision ? display_existential : display_universal) - "$(rel_display_fun(relation(decision))) ($prop_decision_str)" - else - "$prop_decision_str" - end -end - - -############################################################################################ diff --git a/src/logisets/base/scalar/main.jl b/src/logisets/base/scalar/main.jl index c382675..dafe0d9 100644 --- a/src/logisets/base/scalar/main.jl +++ b/src/logisets/base/scalar/main.jl @@ -12,6 +12,9 @@ include("test-operators.jl") # Alphabets of conditions on the features, to be used in logical datasets include("conditions.jl") +# Templates for formulas of scalar conditions (e.g., templates for ⊤, f ⋈ t, ⟨R⟩ f ⋈ t, etc.) +include("templated-formulas.jl") + export MixedFeature, CanonicalFeature, canonical_geq, canonical_leq export canonical_geq_95, canonical_geq_90, canonical_geq_85, canonical_geq_80, canonical_geq_75, canonical_geq_70, canonical_geq_60, @@ -26,10 +29,10 @@ include("random.jl") include("representatives.jl") -include("decisions.jl") - include("memosets.jl") +include("one-step-memoset.jl") + export UnivariateMin, UnivariateMax, UnivariateSoftMin, UnivariateSoftMax, MultivariateFeature diff --git a/src/logisets/base/scalar/memosets.jl b/src/logisets/base/scalar/memosets.jl index 9193fba..caa8cc4 100644 --- a/src/logisets/base/scalar/memosets.jl +++ b/src/logisets/base/scalar/memosets.jl @@ -1,3 +1,5 @@ +using UniqueVectors + """ A full memoization structure used for checking formulas of scalar conditions on datasets with scalar features. This structure is the equivalent to [`Memoset`](@ref), @@ -27,7 +29,7 @@ struct ScalarMemoset{ function ScalarMemoset( X::AbstractLogiset{W,U,F,FR}, - perform_initialization = false, + # perform_initialization = false, ) where {W<:AbstractWorld,U,F<:AbstractFeature,FR<:AbstractFrame{W}} d = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i in 1:ninstances(X)] D = typeof(d) @@ -98,220 +100,3 @@ usesfullmemo(::ScalarMemoset) = true fullmemo(Xm::ScalarMemoset) = Xm hasnans(::ScalarMemoset) = false - - -############################################################################################ - -""" -Abstract type for one-step memoization structure for checking formulas of type `⟨R⟩ (f ⋈ t)`. -""" -abstract type AbstractScalarOneStepMemoset{U,W,F<:AbstractFeature,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,F,FR} end - -# Access inner structure -function innerstruct(Xm::AbstractScalarOneStepMemoset) - error("Please, provide method innerstruct(::$(typeof(Xm))).") -end - -isminifiable(::AbstractScalarOneStepMemoset) = true - -function minify(Xm::AbstractScalarOneStepMemoset) - minify(innerstruct(Xm)) -end - -""" -Abstract type for one-step memoization structure for checking formulas of type `⟨R⟩ (f ⋈ t)`, -for a generic relation `R`. -""" -abstract type AbstractScalarOneStepRelationalMemoset{U,W,FR<:AbstractFrame{W}} <: AbstractScalarOneStepMemoset{W,U,F where F<:AbstractFeature,FR} end -""" -Abstract type for one-step memoization structure for checking "global" formulas -of type `⟨G⟩ (f ⋈ t)`. -""" -abstract type AbstractScalarOneStepGlobalMemoset{W,U} <: AbstractScalarOneStepMemoset{W,U,F where F<:AbstractFeature,FR<:AbstractFrame{W}} end - -############################################################################################ - -""" -A generic, one-step memoization structure used for checking specific formulas -of scalar conditions on -datasets with scalar features. The formulas are of type ⟨R⟩ (f ⋈ t) - -TODO explain - -See also -[`Memoset`](@ref), -[`SuportedLogiset`](@ref), -[`AbstractLogiset`](@ref). -""" -struct ScalarOneStepRelationalMemoset{ - W<:AbstractWorld, - U, - FR<:AbstractFrame{W}, - D<:AbstractArray{<:AbstractDict{W,VV}, 3} where VV<:Union{U,Nothing}, -} <: AbstractScalarOneStepRelationalMemoset{W,U,FR} - - metaconditions :: Vector{<:ScalarMetaCondition} - - relations :: Vector{<:AbstractRelation} - - d :: D - - function ScalarOneStepRelationalMemoset{W,U,FR}( - metaconditions::AbstractVector{<:ScalarMetaCondition}, - relations::AbstractVector{<:AbstractRelation}, - d::D, - ) where {W<:AbstractWorld,U,FR<:AbstractFrame{W},D<:AbstractArray{U,2}} - new{W,U,FR,D}(d) - end - - function ScalarOneStepRelationalMemoset( - X::AbstractLogiset{W,U,F,FR}, - metaconditions::AbstractVector{<:ScalarMetaCondition}, - relations::AbstractVector{<:AbstractRelation}, - perform_initialization = false - ) where {W,U,F<:AbstractFeature,FR<:AbstractFrame{W}} - nmetaconditions = length(metaconditions) - nrelations = length(relations) - d = begin - if perform_initialization - d = Array{Dict{W,Union{U,Nothing}}, 3}(undef, ninstances(X), nmetaconditions, nrelations) - fill!(d, nothing) - else - Array{Dict{W,U}, 3}(undef, ninstances(X), nmetaconditions, nrelations) - end - end - ScalarOneStepRelationalMemoset{W,U,FR}(d, metaconditions, relations) - end -end - - -innerstruct(Xm::ScalarOneStepRelationalMemoset) = Xm.d - -metaconditions(Xm::ScalarOneStepRelationalMemoset) = Xm.metaconditions -relations(Xm::ScalarOneStepRelationalMemoset) = Xm.relations - -ninstances(Xm::ScalarOneStepRelationalMemoset) = size(Xm.d, 1) -nmetaconditions(Xm::ScalarOneStepRelationalMemoset) = size(Xm.d, 2) -nrelations(Xm::ScalarOneStepRelationalMemoset) = size(Xm.d, 3) - -capacity(Xm::ScalarOneStepRelationalMemoset) = Inf -nmemoizedvalues(Xm::ScalarOneStepRelationalMemoset) = sum(length.(Xm.d)) - - -usesfullmemo(::ScalarOneStepRelationalMemoset) = false - -function hasnans(Xm::ScalarOneStepRelationalMemoset) - any(map(d->(any(_isnan.(collect(values(d))))), Xm.d)) -end - -function check( - f::AbstractFormula, - Xm::ScalarOneStepRelationalMemoset{W}, - i_instance::Integer, - w::W; - kwargs... -) where {W<:AbstractWorld} - error("TODO implement chained threshold checking algorithm.") -end - -function instances(Xm::ScalarOneStepRelationalMemoset{W,U,FR}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {W,U,FR} - ScalarOneStepRelationalMemoset{W,U,FR}(if return_view == Val(true) @view Xm.d[inds,:,:] else Xm.d[inds,:,:] end) -end - -function concatdatasets(Xms::ScalarOneStepRelationalMemoset...) - ScalarOneStepRelationalMemoset(vcat([Xm.d for Xm in Xms]...)) -end - - -function displaystructure(Xm::ScalarOneStepRelationalMemoset; indent_str = "", include_ninstances = true) - padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) - pieces = [] - push!(pieces, "") - push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") - push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") - push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") - push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") - if include_ninstances - push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") - end - push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") - push!(pieces, "$(padattribute("# metaconditions:", nmetaconditions(Xm)))") - push!(pieces, "$(padattribute("# relations:", nrelations(Xm)))") - push!(pieces, "") - push!(pieces, "$(padattribute("metaconditions:", metaconditions(Xm)))") - push!(pieces, "$(padattribute("relations:", relations(Xm)))") - - return "ScalarOneStepRelationalMemoset ($(humansize(Xm)))" * - join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" -end - - -# @inline function Base.getindex( -# Xm :: ScalarOneStepRelationalMemoset{W,U}, -# i_instance :: Integer, -# w :: W, -# i_featsnaggr :: Integer, -# i_relation :: Integer -# ) where {W<:AbstractWorld,U} -# Xm.d[i_instance, i_featsnaggr, i_relation][w] -# end -# Base.size(Xm::ScalarOneStepRelationalMemoset, args...) = size(Xm.d, args...) - -# fwd_rs_init_world_slice(Xm::ScalarOneStepRelationalMemoset{W,U}, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) where {W,U} = -# Xm.d[i_instance, i_featsnaggr, i_relation] = Dict{W,U}() -# @inline function Base.setindex!(Xm::ScalarOneStepRelationalMemoset{W,U}, threshold::U, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {W,U} -# Xm.d[i_instance, i_featsnaggr, i_relation][w] = threshold -# end - -############################################################################################ - -# Note: the global Xm is world-agnostic -struct ScalarOneStepGlobalMemoset{ - W<:AbstractWorld, - U, - D<:AbstractArray{U,2} -} <: AbstractScalarOneStepGlobalMemoset{W,U} - - d :: D - - function ScalarOneStepGlobalMemoset{W,U,D}(d::D) where {U,D<:AbstractArray{U,2}} - new{W,U,D}(d) - end - function ScalarOneStepGlobalMemoset{W,U}(d::D) where {U,D<:AbstractArray{U,2}} - ScalarOneStepGlobalMemoset{W,U,D}(d) - end - - function ScalarOneStepGlobalMemoset(X::AbstractLogiset{W,U}) where {W<:AbstractWorld,U} - @assert worldtype(X) != OneWorld "TODO adjust this note: note that you should not use a global Xm when not using global decisions" - _nfeatsnaggrs = nfeatsnaggrs(X) - ScalarOneStepGlobalMemoset{W,U}(Array{U,2}(undef, ninstances(X), _nfeatsnaggrs)) - end -end - -capacity(Xm::ScalarOneStepGlobalMemoset) = prod(size(Xm.d)) -nmemoizedvalues(Xm::ScalarOneStepGlobalMemoset) = sum(Xm.d) -innerstruct(Xm::ScalarOneStepGlobalMemoset) = Xm.d - -# default_fwd_gs_type(::Type{<:AbstractWorld}) = ScalarOneStepGlobalMemoset # TODO implement similar pattern used for fwd - -function hasnans(Xm::ScalarOneStepGlobalMemoset) - # @show any(_isnan.(Xm.d)) - any(_isnan.(Xm.d)) -end - -ninstances(Xm::ScalarOneStepGlobalMemoset) = size(Xm, 1) -nfeatsnaggrs(Xm::ScalarOneStepGlobalMemoset) = size(Xm, 2) -Base.getindex( - Xm :: ScalarOneStepGlobalMemoset, - i_instance :: Integer, - i_featsnaggr :: Integer) = Xm.d[i_instance, i_featsnaggr] -Base.size(Xm::ScalarOneStepGlobalMemoset{U}, args...) where {U} = size(Xm.d, args...) - -Base.setindex!(Xm::ScalarOneStepGlobalMemoset{U}, threshold::U, i_instance::Integer, i_featsnaggr::Integer) where {U} = - Xm.d[i_instance, i_featsnaggr] = threshold -function instances(Xm::ScalarOneStepGlobalMemoset{U}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {U} - ScalarOneStepGlobalMemoset{U}(if return_view == Val(true) @view Xm.d[inds,:] else Xm.d[inds,:] end) -end - -abstract type FeaturedMemoset{U<:Number,W<:AbstractWorld,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,F where F<:AbstractFeature,FR} end - diff --git a/src/logisets/base/scalar/one-step-memoset.jl b/src/logisets/base/scalar/one-step-memoset.jl new file mode 100644 index 0000000..19000b0 --- /dev/null +++ b/src/logisets/base/scalar/one-step-memoset.jl @@ -0,0 +1,532 @@ + +""" +Abstract type for one-step memoization structure for checking formulas of type `⟨R⟩ (f ⋈ t)`, +for a generic relation `R`. +""" +abstract type AbstractScalarOneStepRelationalMemoset{W<:AbstractWorld,U,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,F where F<:AbstractFeature,FR} end + +@inline function Base.getindex( + memoset :: AbstractScalarOneStepRelationalMemoset{W}, + i_instance :: Integer, + w :: W, + i_metacond :: Integer, + i_relation :: Integer +) where {W} + error("Please, provide method Base.getindex(" * + "memoset::$(typeof(memoset)), " * + "i_instance::$(typeof(i_instance)), " * + "w::$(typeof(w)), " * + "i_metacond::$(typeof(i_metacond)), " * + "i_relation::$(typeof(i_relation))" * + ").") +end + +""" +Abstract type for one-step memoization structure for checking "global" formulas +of type `⟨G⟩ (f ⋈ t)`. +""" +abstract type AbstractScalarOneStepGlobalMemoset{W<:AbstractWorld,U} <: AbstractMemoset{W,U,F where F<:AbstractFeature,FR where FR<:AbstractFrame{W}} end + +@inline function Base.getindex( + memoset :: AbstractScalarOneStepGlobalMemoset{W}, + i_instance :: Integer, + i_metacond :: Integer, +) where {W} + error("Please, provide method Base.getindex(" * + "memoset::$(typeof(memoset)), " * + "i_instance::$(typeof(i_instance)), " * + "i_metacond::$(typeof(i_metacond))" * + ").") +end + +# Access inner structure +function innerstruct(Xm::Union{AbstractScalarOneStepRelationalMemoset,AbstractScalarOneStepGlobalMemoset}) + error("Please, provide method innerstruct(::$(typeof(Xm))).") +end + +isminifiable(::Union{AbstractScalarOneStepRelationalMemoset,AbstractScalarOneStepGlobalMemoset}) = true + +function minify(Xm::Union{AbstractScalarOneStepRelationalMemoset,AbstractScalarOneStepGlobalMemoset}) + minify(innerstruct(Xm)) +end + +############################################################################################ +############################################################################################ +############################################################################################ + +# Compute modal dataset propositions and 1-modal decisions +struct ScalarOneStepMemoset{ + U<:Number, + W<:AbstractWorld, + FR<:AbstractFrame{W}, + UU<:Union{U,Nothing}, + RM<:AbstractScalarOneStepRelationalMemoset{W,UU,FR}, + GM<:Union{AbstractScalarOneStepGlobalMemoset{W,U},Nothing}, +} <: AbstractOneStepMemoset{W,U,F where F<:AbstractFeature,FR} + + metaconditions :: UniqueVector{<:ScalarMetaCondition} + relations :: UniqueVector{<:AbstractRelation} + + # Relational memoset + relmemoset :: RM + + # Global memoset + globmemoset :: GM + + function ScalarOneStepMemoset( + metaconditions::AbstractVector{<:ScalarMetaCondition}, + relations::AbstractVector{<:AbstractRelation}, + relmemoset::RM, + globmemoset::GM, + ) where { + U<:Number, + W<:AbstractWorld, + FR<:AbstractFrame{W}, + UU<:Union{U,Nothing}, + RM<:AbstractScalarOneStepRelationalMemoset{W,UU,FR}, + GM<:Union{AbstractScalarOneStepGlobalMemoset{W,U},Nothing}, + } + metaconditions = UniqueVector(metaconditions) + relations = UniqueVector(relations) + + if globalrel in relations && isnothing(globmemoset) + @warn "Using global relation in a relational memoset. This is not optimal." + end + + @assert nmetaconditions(relmemoset) == length(metaconditions) "Can't instantiate " * + "$(ty) with mismatching nmetaconditions for relmemoset and " * + "provided metaconditions: $(nmetaconditions(relmemoset)) and $(length(metaconditions))" + @assert nrelations(relmemoset) == length(relations) "Can't instantiate " * + "$(ty) with mismatching nrelations for relmemoset and " * + "provided relations: $(nrelations(relmemoset)) and $(length(relations))" + + if !isnothing(globmemoset) + @assert nmetaconditions(globmemoset) == length(metaconditions) "Can't " * + "instantiate $(ty) with mismatching nmetaconditions for " * + "globmemoset and provided metaconditions: " * + "$(nmetaconditions(globmemoset)) and $(length(metaconditions))" + @assert ninstances(globmemoset) == ninstances(relmemoset) "Can't " * + "instantiate $(ty) with mismatching ninstances for " * + "globmemoset and relmemoset memoset: " * + "$(ninstances(globmemoset)) and $(ninstances(relmemoset))" + end + + new{U,W,FR,UU,RM,GM}(metaconditions, relations, relmemoset, globmemoset) + end + + Base.@propagate_inbounds function ScalarOneStepMemoset( + X :: AbstractLogiset{W,U}, + metaconditions :: AbstractVector{<:ScalarMetaCondition}, + relations :: AbstractVector{<:AbstractRelation}, + relational_memoset_type :: Type{<:AbstractScalarOneStepRelationalMemoset} = default_relmemoset_type(X); + precompute_globmemoset :: Bool = true, + precompute_relmemoset :: Bool = false, + ) where {W<:AbstractWorld,U} + + _fwd = fwd(X) + + _features = features(X) + _grouped_featsnaggrs = grouped_featsnaggrs(X) + featsnaggrs = features_grouped_featsaggrsnops2featsnaggrs(features(X), grouped_featsaggrsnops(X)) + + compute_globmemoset = begin + if globalrel in relations + relations = filter!(l->l≠globalrel, relations) + true + else + false + end + end + + _n_instances = ninstances(X) + nrelations = length(relations) + nmetaconditions = sum(length.(_grouped_featsnaggrs)) + + # Prepare relmemoset + perform_initialization = !precompute_relmemoset + relmemoset = relational_memoset_type(X, perform_initialization) + + # Prepare globmemoset + globmemoset = begin + if compute_globmemoset + ScalarOneStepGlobalMemoset(X) + else + nothing + end + end + + # p = Progress(_n_instances, 1, "Computing EMD supports...") + Threads.@threads for i_instance in 1:_n_instances + # @logmsg LogDebug "Instance $(i_instance)/$(_n_instances)" + + # if i_instance == 1 || ((i_instance+1) % (floor(Int, ((_n_instances)/4))+1)) == 0 + # @logmsg LogOverview "Instance $(i_instance)/$(_n_instances)" + # end + + for (i_feature,aggregators) in enumerate(_grouped_featsnaggrs) + feature = _features[i_feature] + # @logmsg LogDebug "Feature $(i_feature)" + + fwdslice = fwdread_channel(_fwd, i_instance, i_feature) + + # @logmsg LogDebug fwdslice + + # Global relation (independent of the current world) + if compute_globmemoset && precompute_globmemoset + # @logmsg LogDebug "globalrel" + + # TODO optimize: all aggregators are likely reading the same raw values. + for (i_featsnaggr,aggr) in aggregators + # Threads.@threads for (i_featsnaggr,aggr) in aggregators + + gamma = fwdslice_onestep_accessible_aggregation(X, fwdslice, i_instance, globalrel, feature, aggr) + + # @logmsg LogDebug "Aggregator[$(i_featsnaggr)]=$(aggr) --> $(gamma)" + + globmemoset[i_instance, i_featsnaggr] = gamma + end + end + + if precompute_relmemoset + # Other relations + for (i_relation,relation) in enumerate(relations) + + # @logmsg LogDebug "Relation $(i_relation)/$(nrelations)" + + for (i_featsnaggr,aggr) in aggregators + relmemoset_init_world_slice(relmemoset, i_instance, i_featsnaggr, i_relation) + end + + for w in allworlds(X, i_instance) + + # @logmsg LogDebug "World" w + + # TODO optimize: all aggregators are likely reading the same raw values. + for (i_featsnaggr,aggr) in aggregators + + gamma = fwdslice_onestep_accessible_aggregation(X, fwdslice, i_instance, w, relation, feature, aggr) + + # @logmsg LogDebug "Aggregator" aggr gamma + + relmemoset[i_instance, w, i_featsnaggr, i_relation] = gamma + end + end + end + end + end + # next!(p) + end + ScalarOneStepMemoset(relmemoset, globmemoset, featsnaggrs) + end +end + +metaconditions(Xm::ScalarOneStepMemoset) = Xm.metaconditions +relations(Xm::ScalarOneStepMemoset) = Xm.relations +nmetaconditions(Xm::ScalarOneStepMemoset) = length(Xm.metaconditions) +nrelations(Xm::ScalarOneStepMemoset) = length(Xm.relations) + +relmemoset(Xm::ScalarOneStepMemoset) = Xm.relmemoset +globmemoset(Xm::ScalarOneStepMemoset) = Xm.globmemoset + +ninstances(Xm::ScalarOneStepMemoset) = ninstances(relmemoset(Xm)) + +function check( + f::ScalarExistentialFormula, + Xm::ScalarOneStepMemoset, + i_instance::Integer, + w::W; + kwargs... +) where {W<:AbstractWorld} + _rel = relation(f) + _metacond = metacond(f) + i_metacond = findfirst(isequal(_metacond), Xm.metaconditions) + if isnothing(i_metacond) + error("Could not find metacondition $(_metacond) in memoset of type $(typeof(Xm)).") + end + gamma = begin + if _rel + Base.getindex(globmemoset(Xm), i_instance, i_metacond) + else + i_rel = findfirst(isequal(_rel), Xm.relations) + if isnothing(i_rel) + error("Could not find relation $(_rel) in memoset of type $(typeof(Xm)).") + end + Base.getindex(relmemoset(Xm), i_instance, w, i_metacond, i_rel) + end + end + if !isnothing(gamma) + return apply_test_operator(test_operator(f), gamma, threshold(f)) + else + return nothing + end +end + +function instances(Xm::ScalarOneStepMemoset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) + ScalarOneStepMemoset( + metaconditions(Xm), + relations(Xm), + instances(relmemoset(Xm), inds, return_view), + (isnothing(globmemoset(Xm)) ? nothing : instances(globmemoset(Xm), inds, return_view)), + ) +end + +function concatdatasets(Xms::ScalarOneStepMemoset...) + @assert allequal(metaconditions.(Xms)) "Cannot concat " * + "ScalarOneStepMemoset's with different metaconditions: " * + "$(@show metaconditions.(Xms))" + @assert allequal(relations.(Xms)) "Cannot concat " * + "ScalarOneStepMemoset's with different relations: " * + "$(@show relations.(Xms))" + ScalarOneStepMemoset( + metaconditions(first(Xms)), + relations(first(Xms)), + concatdatasets(relmemoset.(Xms)), + concatdatasets(globmemoset.(Xms)), + ) +end + +function hasnans(Xm::ScalarOneStepMemoset) + hasnans(relmemoset(Xm)) || (!isnothing(globmemoset(Xm)) && hasnans(globmemoset(Xm))) +end + +isminifiable(Xm::ScalarOneStepMemoset) = isminifiable(relmemoset(Xm)) && (isnothing(globmemoset(Xm)) || isminifiable(globmemoset(Xm))) + +function minify(Xm::OSSD) where {OSSD<:ScalarOneStepMemoset} + (new_relmemoset, new_globmemoset), backmap = + minify([ + relmemoset(Xm), + globmemoset(Xm), + ]) + + Xm = OSSD( + new_relmemoset, + new_globmemoset, + featsnaggrs(Xm), + ) + Xm, backmap +end + +function displaystructure(Xm::ScalarOneStepMemoset; indent_str = "", include_ninstances = true) + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))-1) + pieces = [] + push!(pieces, " \n") + + if include_ninstances + push!(pieces, " " * padattribute("# instances:", "$(ninstances(Xm))\n")) + end + + push!(pieces, "$(padattribute("# metaconditions:", nmetaconditions(Xm)))") + push!(pieces, "$(padattribute("# relations:", nrelations(Xm)))") + + push!(pieces, "$(padattribute("metaconditions:", "$(eltype(metaconditions(Xm)))[$(join(syntaxstring.(metaconditions(Xm))))]", ",")))") + push!(pieces, "$(padattribute("relations:", relations(Xm)))") + + push!(pieces, " relational memoset ($(round(nonnothingshare(relmemoset(Xm))*100, digits=2))% memoized):\n") + push!(pieces, " " * displaystructure(relmemoset(Xm); indent_str = "$(indent_str)│ ", include_ninstances = false, include_nmetaconditions = false, include_nrelations = false)) + if !isnothing(globmemoset(Xm)) + push!(pieces, " global memoset ($(round(nonnothingshare(globmemoset(Xm))*100, digits=2))% memoized):\n") + push!(pieces, " " * displaystructure(globmemoset(Xm); indent_str = "$(indent_str)│ ", include_ninstances = false, include_nmetaconditions = false)) + else + push!(pieces, " global memoset: −\n") + end + + return "ScalarOneStepMemoset ($(humansize(Xm)))" * + join(pieces, "$(indent_str)├", "$(indent_str)└") * "\n" +end + +############################################################################################ + +""" +A generic, one-step memoization structure used for checking specific formulas +of scalar conditions on +datasets with scalar features. The formulas are of type ⟨R⟩ (f ⋈ t) + +TODO explain + +See also +[`Memoset`](@ref), +[`SuportedLogiset`](@ref), +[`AbstractLogiset`](@ref). +""" +struct ScalarOneStepRelationalMemoset{ + W<:AbstractWorld, + U, + FR<:AbstractFrame{W}, + D<:AbstractArray{<:AbstractDict{W,U}, 3}, +} <: AbstractScalarOneStepRelationalMemoset{W,U,FR} + + d :: D + + function ScalarOneStepRelationalMemoset{W,U,FR}( + d::D, + ) where {W<:AbstractWorld,U,FR<:AbstractFrame{W},D<:AbstractArray{<:AbstractDict{W,U}, 3}} + new{W,U,FR,D}(d) + end + + function ScalarOneStepRelationalMemoset( + X::AbstractLogiset{W,U,F,FR}, + metaconditions::AbstractVector{<:ScalarMetaCondition}, + relations::AbstractVector{<:AbstractRelation}, + perform_initialization = false + ) where {W,U,F<:AbstractFeature,FR<:AbstractFrame{W}} + nmetaconditions = length(metaconditions) + nrelations = length(relations) + d = begin + d = Array{Dict{W,U}, 3}(undef, ninstances(X), nmetaconditions, nrelations) + if perform_initialization + for idx in eachindex(d) + d[idx] = ThreadSafeDict{W,U}() + end + end + d + end + ScalarOneStepRelationalMemoset{W,U,FR}(d) + end +end + +innerstruct(Xm::ScalarOneStepRelationalMemoset) = Xm.d + +ninstances(Xm::ScalarOneStepRelationalMemoset) = size(Xm.d, 1) +nmetaconditions(Xm::ScalarOneStepRelationalMemoset) = size(Xm.d, 2) +nrelations(Xm::ScalarOneStepRelationalMemoset) = size(Xm.d, 3) + +capacity(Xm::ScalarOneStepRelationalMemoset) = Inf +nmemoizedvalues(Xm::ScalarOneStepRelationalMemoset) = sum(length.(Xm.d)) + +@inline function Base.getindex( + Xm :: ScalarOneStepRelationalMemoset{W}, + i_instance :: Integer, + w :: W, + i_metacond :: Integer, + i_relation :: Integer +) where {W<:AbstractWorld} + get(Xm.d[i_instance, i_metacond, i_relation], w, nothing) +end + +usesfullmemo(::ScalarOneStepRelationalMemoset) = false + +function hasnans(Xm::ScalarOneStepRelationalMemoset) + any(map(d->(any(_isnan.(collect(values(d))))), Xm.d)) +end + +function instances(Xm::ScalarOneStepRelationalMemoset{W,U,FR}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {W,U,FR} + ScalarOneStepRelationalMemoset{W,U,FR}(if return_view == Val(true) @view Xm.d[inds,:,:] else Xm.d[inds,:,:] end) +end + +function concatdatasets(Xms::ScalarOneStepRelationalMemoset...) + ScalarOneStepRelationalMemoset(cat([Xm.d for Xm in Xms]...; dims=1)) +end + + +function displaystructure(Xm::ScalarOneStepRelationalMemoset; indent_str = "", include_ninstances = true, include_nmetaconditions = true, include_nrelations = true) + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) + pieces = [] + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") + push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") + push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + if include_ninstances + push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") + end + if include_nmetaconditions + push!(pieces, "$(padattribute("# metaconditions:", nmetaconditions(Xm)))") + end + if include_nrelations + push!(pieces, "$(padattribute("# relations:", nrelations(Xm)))") + end + push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") + + return "ScalarOneStepRelationalMemoset ($(humansize(Xm)))" * + join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" +end + +# fwd_rs_init_world_slice(Xm::ScalarOneStepRelationalMemoset{W,U}, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) where {W,U} = +# Xm.d[i_instance, i_featsnaggr, i_relation] = Dict{W,U}() +# @inline function Base.setindex!(Xm::ScalarOneStepRelationalMemoset{W,U}, threshold::U, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {W,U} +# Xm.d[i_instance, i_featsnaggr, i_relation][w] = threshold +# end + +############################################################################################ + +# Note: the global Xm is world-agnostic +struct ScalarOneStepGlobalMemoset{ + W<:AbstractWorld, + U, + D<:AbstractArray{UU,2} where {UU<:Union{U,Nothing}} +} <: AbstractScalarOneStepGlobalMemoset{W,U} + + d :: D + + function ScalarOneStepGlobalMemoset{W,U}( + d::D + ) where {W<:AbstractWorld,U,D<:AbstractArray{UU,2} where {UU<:Union{U,Nothing}} + new{W,U,D}(d) + end + + function ScalarOneStepGlobalMemoset( + X::AbstractLogiset{W,U}, + metaconditions::AbstractVector{<:ScalarMetaCondition}, + perform_initialization = false + ) where {W<:AbstractWorld,U} + @assert worldtype(X) != OneWorld "TODO adjust this note: note that you should not use a global Xm when not using global decisions" + nmetaconditions = length(metaconditions) + d = Array{Union{U,Nothing},2}(undef, ninstances(X), length(metaconditions)) + if perform_initialization + fill!(d, nothing) + end + ScalarOneStepGlobalMemoset{W,U}(d) + end +end + +innerstruct(Xm::ScalarOneStepGlobalMemoset) = Xm.d + +ninstances(Xm::ScalarOneStepGlobalMemoset) = size(Xm.d, 1) +nmetaconditions(Xm::ScalarOneStepGlobalMemoset) = size(Xm.d, 2) + +capacity(Xm::ScalarOneStepGlobalMemoset) = prod(size(Xm.d)) +nmemoizedvalues(Xm::ScalarOneStepGlobalMemoset) = sum((!).((isnothing).(Xm.d))) + +@inline function Base.getindex( + Xm :: ScalarOneStepRelationalMemoset{W}, + i_instance :: Integer, + i_metacond :: Integer, +) where {W<:AbstractWorld} + Xm.d[i_instance, i_metacond] +end + +usesfullmemo(::ScalarOneStepGlobalMemoset) = false + +function hasnans(Xm::ScalarOneStepGlobalMemoset) + # @show any(_isnan.(Xm.d)) + any(_isnan.(Xm.d)) +end + +function instances(Xm::ScalarOneStepGlobalMemoset{W,U}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {W,U} + ScalarOneStepGlobalMemoset{W,U}(if return_view == Val(true) @view Xm.d[inds,:] else Xm.d[inds,:] end) +end + +function concatdatasets(Xms::ScalarOneStepGlobalMemoset...) + ScalarOneStepGlobalMemoset(cat([Xm.d for Xm in Xms]...; dims=1)) +end + + +function displaystructure(Xm::ScalarOneStepGlobalMemoset; indent_str = "", include_ninstances = true, include_nmetaconditions = true) + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) + pieces = [] + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") + push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") + push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + if include_ninstances + push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") + end + if include_nmetaconditions + push!(pieces, "$(padattribute("# metaconditions:", nmetaconditions(Xm)))") + end + push!(pieces, "$(padattribute("capacity:", capacity(Xm)))") + push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") + + return "ScalarOneStepGlobalMemoset ($(humansize(Xm)))" * + join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" +end + diff --git a/src/logisets/base/scalar/templated-formulas.jl b/src/logisets/base/scalar/templated-formulas.jl new file mode 100644 index 0000000..8d54b4b --- /dev/null +++ b/src/logisets/base/scalar/templated-formulas.jl @@ -0,0 +1,116 @@ +using SoleLogics: AbstractRelation + +using SoleModels: AbstractFeature, TestOperator, ScalarCondition + +""" +Abstract type for templated formulas on scalar conditions. +""" +abstract type ScalarFormula{U} <: AbstractTemplatedFormula end + +""" +Templated formula for f ⋈ t. +""" +struct ScalarPropositionFormula{U} <: ScalarFormula{U} + p :: ScalarCondition{U} +end + +proposition(d::ScalarPropositionFormula) = d.p +feature(d::ScalarPropositionFormula) = feature(proposition(d)) +test_operator(d::ScalarPropositionFormula) = test_operator(proposition(d)) +threshold(d::ScalarPropositionFormula) = threshold(proposition(d)) + +tree(d::ScalarPropositionFormula) = SyntaxTree(d.p) +negation(d::ScalarPropositionFormula{U}) where {U} = + ScalarPropositionFormula{U}(negation(p)) + +############################################################################################ + +abstract type ScalarOneStepFormula{U} <: ScalarFormula{U} end + +relation(d::ScalarOneStepFormula) = d.relation +proposition(d::ScalarOneStepFormula) = d.p +metacond(d::ScalarOneStepFormula) = metacond(proposition(d)) +feature(d::ScalarOneStepFormula) = feature(proposition(d)) +test_operator(d::ScalarOneStepFormula) = test_operator(proposition(d)) +threshold(d::ScalarOneStepFormula) = threshold(proposition(d)) + +""" +Templated formula for ⟨R⟩ f ⋈ t. +""" +struct ScalarExistentialFormula{U} <: ScalarOneStepFormula{U} + + # Relation, interpreted as an existential modal operator + relation :: AbstractRelation + + p :: ScalarCondition{U} + + function ScalarExistentialFormula{U}() where {U} + new{U}() + end + + function ScalarExistentialFormula{U}( + relation :: AbstractRelation, + p :: ScalarCondition{U} + ) where {U} + new{U}(relation, p) + end + + function ScalarExistentialFormula( + relation :: AbstractRelation, + p :: ScalarCondition{U} + ) where {U} + ScalarExistentialFormula{U}(relation, p) + end + + function ScalarExistentialFormula{U}( + relation :: AbstractRelation, + feature :: AbstractFeature, + test_operator :: TestOperator, + threshold :: U + ) where {U} + p = ScalarCondition(feature, test_operator, threshold) + ScalarExistentialFormula{U}(relation, p) + end + + function ScalarExistentialFormula( + relation :: AbstractRelation, + feature :: AbstractFeature, + test_operator :: TestOperator, + threshold :: U + ) where {U} + ScalarExistentialFormula{U}(relation, feature, test_operator, threshold) + end + + function ScalarExistentialFormula( + formula :: ScalarExistentialFormula{U}, + threshold_f :: Function + ) where {U} + q = ScalarCondition(formula.p, threshold_f(threshold(formula.p))) + ScalarExistentialFormula{U}(relation(formula), q) + end +end + +tree(d::ScalarExistentialFormula) = DiamondRelationalOperator(d.relation)(d.p) + +""" +Templated formula for [R] f ⋈ t. +""" +struct ScalarUniversalFormula{U} <: ScalarOneStepFormula{U} + relation :: AbstractRelation + p :: ScalarCondition{U} +end + +tree(d::ScalarUniversalFormula) = BoxRelationalOperator(d.relation)(d.p) + +function negation(formula::ScalarExistentialFormula{U}) where {U} + ScalarUniversalFormula{U}( + relation(formula), + negation(proposition(formula)) + ) +end +function negation(formula::ScalarUniversalFormula{U}) where {U} + ScalarExistentialFormula{U}( + relation(formula), + negation(proposition(formula)) + ) +end diff --git a/src/logisets/base/supported-logiset.jl b/src/logisets/base/supported-logiset.jl index de2d4fc..fef7d48 100644 --- a/src/logisets/base/supported-logiset.jl +++ b/src/logisets/base/supported-logiset.jl @@ -67,6 +67,9 @@ struct SupportedLogiset{ "Please, provide cascading supports so that the full memoization set appears " * "last. $(@show typeof.(supports))" + @assert allequal([ninstances(base), ninstances.(supports)...]) "Consistency " * + "check failed! Mismatching ninstances for base and memoset(s): $(ninstances(base)) and $(ninstances.(supports))" + N = length(supports) MS = typeof(supports) new{L,N,MS}(base, supports) @@ -79,13 +82,59 @@ struct SupportedLogiset{ SupportedLogiset(base, Tuple(supports)) end - # Helper + # Helper (avoids ambiguities) function SupportedLogiset( base::AbstractLogiset, + firstsupport::Union{<:AbstractVector{<:AbstractDict},<:AbstractMemoset,<:SupportedLogiset}, supports::Union{<:AbstractVector{<:AbstractDict},<:AbstractMemoset,<:SupportedLogiset}... ) + SupportedLogiset(base, [firstsupport, supports...]) + end + + function SupportedLogiset( + base :: AbstractLogiset; + use_full_memoization :: Union{Bool,Type{<:AbstractMemoset}} = true, + # + conditions :: Union{Nothing,AbstractVector{<:AbstractCondition}} = nothing, + relations :: Union{Nothing,AbstractVector{<:AbstractRelation}} = nothing, + use_onestep_memoization :: Union{Bool,Type{<:AbstractOneStepMemoset}} = !isnothing(conditions) && !isnothing(relations), + onestep_precompute_globmemoset :: Bool = (use_onestep_memoization != false), + onestep_precompute_globmemoset :: Bool = false, + ) + supports = [] + + @assert !xor(isnothing(conditions), isnothing(relations)) "Please, provide " * + "both conditions and relations in order to use a one-step memoset." + + if use_onestep_memoization != false + onestep_memoset_type = (use_onestep_memoization isa Bool ? default_onestep_memoset_type(base) : use_onestep_memoization) + push!(supports, + onestep_memoset_type( + base, + conditions, + relations, + precompute_globmemoset = onestep_precompute_globmemoset, + precompute_relmemoset = onestep_precompute_relmemoset + ) + ) + end + + if use_full_memoization != false + full_memoset_type = (use_full_memoization isa Bool ? default_full_memoset_type(base) : use_full_memoization) + push!(supports, full_memoset_type(base)) + end + SupportedLogiset(base, supports) end + + # TODO Helper + # function SupportedLogiset( + # X :: ... AbstractActiveScalarLogiset{W,V,FT,Bool,FR}; + # kwargs..., + # ) where {V,FT<:AbstractFeature{V},W<:AbstractWorld,FR<:AbstractFrame{W}} + # SupportedLogiset(Logiset(X); kwargs...) + # end + end base(X::SupportedLogiset) = X.base @@ -131,7 +180,6 @@ function allfeatvalues( allfeatvalues(base(X), i_instance, feature) end - usesfullmemo(X::SupportedLogiset) = usesfullmemo(last(supports(X))) fullmemo(X::SupportedLogiset) = usesfullmemo(X) ? last(supports(X)) : error("This " * "dataset does not have a full memoization set.") @@ -201,9 +249,11 @@ end # grouped_featsnaggrs(X::SupportedLogiset) = grouped_featsnaggrs(base(X)) # nfeatures(X::SupportedLogiset) = nfeatures(base(X)) # nrelations(X::SupportedLogiset) = nrelations(base(X)) -# ninstances(X::SupportedLogiset) = ninstances(base(X)) # relations(X::SupportedLogiset) = relations(base(X)) # fwd(X::SupportedLogiset) = fwd(base(X)) # worldtype(X::SupportedLogiset{V,W}) where {V,W} = W -# usesmemo(X::SupportedLogiset) = usesmemo(support(X)) +# initialworld(X::SupportedScalarLogiset, args...) = initialworld(base(X), args...) + +# TODO remove: +support(X::SupportedLogiset) = first(supports(X)) diff --git a/src/logisets/base/templated-formulas.jl b/src/logisets/base/templated-formulas.jl new file mode 100644 index 0000000..972ddbc --- /dev/null +++ b/src/logisets/base/templated-formulas.jl @@ -0,0 +1,35 @@ +import Base: show +import SoleLogics: tree, negation + +""" +Abstract type simple formulas of given templates. +""" +abstract type AbstractTemplatedFormula <: SoleLogics.AbstractFormula end + +""" +Templated formula for ⊤. +""" +struct TopFormula <: AbstractTemplatedFormula end +tree(::TopFormula) = SyntaxTree(⊤) +negation(::TopFormula) = BotFormula() + +""" +Templated formula for ⊥. +""" +struct BotFormula <: AbstractTemplatedFormula end +tree(::BotFormula) = SyntaxTree(⊥) +negation(::BotFormula) = TopFormula() + +""" +Templated formula for ⟨R⟩⊤. +""" +struct ExistentialTopFormula{R<:AbstractRelation} <: AbstractTemplatedFormula end +tree(::ExistentialTopFormula{R}) where {R<:AbstractRelation} = DiamondRelationalOperator{R}(⊤) +negation(::ExistentialTopFormula{R}) where {R<:AbstractRelation} = UniversalBotFormula{R}() + +""" +Templated formula for [R]⊥. +""" +struct UniversalBotFormula{R<:AbstractRelation} <: AbstractTemplatedFormula end +tree(::UniversalBotFormula{R}) where {R<:AbstractRelation} = BoxRelationalOperator{R}(⊥) +negation(::UniversalBotFormula{R}) where {R<:AbstractRelation} = ExistentialTopFormula{R}() diff --git a/src/logisets/dimensional-logisets/datasets/dimensional-fwds.jl b/src/logisets/dimensional-logisets/datasets/dimensional-fwds.jl index e4fa8ea..0405c16 100644 --- a/src/logisets/dimensional-logisets/datasets/dimensional-fwds.jl +++ b/src/logisets/dimensional-logisets/datasets/dimensional-fwds.jl @@ -2,7 +2,7 @@ import Base: size, ndims, getindex, setindex! ############################################################################################ ############################################################################################ -# world-specific FWD implementations +# Frame-specific FWD implementations ############################################################################################ ############################################################################################ @@ -29,11 +29,6 @@ struct UniformFullDimensionalFWD{ # Features features :: Vector{FT} - # Test operators associated with each feature, grouped by their respective aggregator - grouped_featsaggrsnops :: G1 - - grouped_featsnaggrs :: G2 - function UniformFullDimensionalFWD{T,W,N,D,FR}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{T},FR<:FullDimensionalFrame{N,W}} new{T,W,N,D,FR}(d) end diff --git a/src/logisets/dimensional-logisets/datasets/dimensional-supports.jl b/src/logisets/dimensional-logisets/datasets/dimensional-supports.jl index 6862ff4..fd82e3f 100644 --- a/src/logisets/dimensional-logisets/datasets/dimensional-supports.jl +++ b/src/logisets/dimensional-logisets/datasets/dimensional-supports.jl @@ -2,14 +2,14 @@ import Base: size, ndims, getindex, setindex! ############################################################################################ ############################################################################################ -# world-specific FWD supports implementations +# Frame-specific FWD supports implementations ############################################################################################ ############################################################################################ abstract type AbstractUniformFullDimensionalRelationalSupport{T,W<:AbstractWorld,FR<:AbstractFrame{W}} <: AbstractScalarOneStepRelationalMemoset{T,W,FR} end # TODO switch from nothing to missing? -usesmemo(fwd_rs::AbstractUniformFullDimensionalRelationalSupport) = Nothing <: Base.eltype(fwd_rs.d) +# usesmemo(fwd_rs::AbstractUniformFullDimensionalRelationalSupport) = Nothing <: Base.eltype(fwd_rs.d) capacity(fwd_rs::AbstractUniformFullDimensionalRelationalSupport) = error("Please, provide method capacity(...).") nmemoizedvalues(support::AbstractUniformFullDimensionalRelationalSupport) = (capacity(support) - count(isnothing, support.d)) @@ -29,6 +29,11 @@ struct UniformFullDimensionalRelationalSupport{ d :: D + # Test operators associated with each feature, grouped by their respective aggregator + grouped_featsaggrsnops :: G1 + + grouped_featsnaggrs :: G2 + function UniformFullDimensionalRelationalSupport{T,W,N,D}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{TT} where TT<:Union{T,Nothing}} new{T,W,N,D}(d) end diff --git a/src/logisets/dimensional-logisets/datasets/main.jl b/src/logisets/dimensional-logisets/datasets/main.jl index d9d9160..8e392a6 100644 --- a/src/logisets/dimensional-logisets/datasets/main.jl +++ b/src/logisets/dimensional-logisets/datasets/main.jl @@ -49,7 +49,7 @@ include("passive-dimensional-datasets.jl") include("dimensional-logiset.jl") -# World-specific featured world datasets and supports +# Frame-specific featured world datasets and supports include("dimensional-fwds.jl") _default_fwd_type(::Type{<:FullDimensionalFrame}) = UniformFullDimensionalFWD diff --git a/src/logisets/dimensional-logisets/scalar-gamma-access.jl b/src/logisets/dimensional-logisets/scalar-gamma-access.jl index 632ef5c..5c00339 100644 --- a/src/logisets/dimensional-logisets/scalar-gamma-access.jl +++ b/src/logisets/dimensional-logisets/scalar-gamma-access.jl @@ -14,7 +14,7 @@ end ############################################################################################ function onestep_accessible_aggregation( - X::SupportedScalarLogiset{VV,W}, + X::SupportedScalarLogiset, i_instance::Integer, w::W, relation::AbstractRelation, @@ -22,18 +22,18 @@ function onestep_accessible_aggregation( aggr::Aggregator, i_featsnaggr::Union{Nothing,Integer} = nothing, i_relation::Integer = findrelation(X, relation), -) where {VV,V<:VV,W<:AbstractWorld} +) where {V,W<:AbstractWorld} compute_modal_gamma(support(X), fd(X), i_instance, w, relation, feature, aggregator, i_featsnaggr, i_relation) end @inline function onestep_accessible_aggregation( - X::SupportedScalarLogiset{VV,W}, + X::SupportedScalarLogiset, i_instance::Integer, r::GlobalRel, f::AbstractFeature{V}, aggr::Aggregator, args... -) where {VV,V<:VV,W<:AbstractWorld} +) where {V,W<:AbstractWorld} compute_global_gamma(support(X), fd(X), i_instance, f, aggr, args...) end @@ -68,7 +68,7 @@ end ############################################################################################ function fwdslice_onestep_accessible_aggregation( - X::OneStepFeaturedMemoset{V,W}, + X::ScalarOneStepMemoset{V,W}, fd::Logiset{V,W}, fwdslice::FWDFeatureSlice, i_instance::Integer, @@ -86,7 +86,7 @@ function fwdslice_onestep_accessible_aggregation( end function fwdslice_onestep_accessible_aggregation( - X::OneStepFeaturedMemoset{V,W}, + X::ScalarOneStepMemoset{V,W}, fd::Logiset{V,W}, fwdslice::FWDFeatureSlice, i_instance::Integer, diff --git a/src/logisets/dimensional-logisets/scalar-one-step-featured-supporting-dataset.jl b/src/logisets/dimensional-logisets/scalar-one-step-featured-supporting-dataset.jl deleted file mode 100644 index aafa2cb..0000000 --- a/src/logisets/dimensional-logisets/scalar-one-step-featured-supporting-dataset.jl +++ /dev/null @@ -1,279 +0,0 @@ - -# Compute modal dataset propositions and 1-modal decisions -struct OneStepFeaturedMemoset{ - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W}, - VV<:Union{V,Nothing}, - FWDRS<:AbstractScalarOneStepRelationalMemoset{VV,W,FR}, - FWDGS<:Union{AbstractScalarOneStepGlobalMemoset{W,V},Nothing}, - G<:AbstractVector{Tuple{<:AbstractFeature,<:Aggregator}}, -} <: FeaturedMemoset{V,W,FR} - - # Relational support - fwd_rs :: FWDRS - - # Global support - fwd_gs :: FWDGS - - # Features and Aggregators - featsnaggrs :: G - - function OneStepFeaturedMemoset( - fwd_rs::FWDRS, - fwd_gs::FWDGS, - featsnaggrs::G, - ) where { - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W}, - VV<:Union{V,Nothing}, - FWDRS<:AbstractScalarOneStepRelationalMemoset{VV,W,FR}, - FWDGS<:Union{AbstractScalarOneStepGlobalMemoset{W,V},Nothing}, - G<:AbstractVector{Tuple{<:AbstractFeature,<:Aggregator}}, - } - @assert nfeatsnaggrs(fwd_rs) == length(featsnaggrs) "Can't instantiate $(ty) with unmatching nfeatsnaggrs for fwd_rs and provided featsnaggrs: $(nfeatsnaggrs(fwd_rs)) and $(length(featsnaggrs))" - if fwd_gs != nothing - @assert nfeatsnaggrs(fwd_gs) == length(featsnaggrs) "Can't instantiate $(ty) with unmatching nfeatsnaggrs for fwd_gs and provided featsnaggrs: $(nfeatsnaggrs(fwd_gs)) and $(length(featsnaggrs))" - @assert ninstances(fwd_gs) == ninstances(fwd_rs) "Can't instantiate $(ty) with unmatching ninstances for fwd_gs and fwd_rs support: $(ninstances(fwd_gs)) and $(ninstances(fwd_rs))" - end - new{V,W,FR,VV,FWDRS,FWDGS,G}(fwd_rs, fwd_gs, featsnaggrs) - end - - _default_rs_type(::Type{<:AbstractWorld}) = ScalarOneStepRelationalMemoset - _default_rs_type(::Type{<:Union{OneWorld,Interval,Interval2D}}) = UniformFullDimensionalRelationalSupport - - # A function that computes the support from an explicit modal dataset - Base.@propagate_inbounds function OneStepFeaturedMemoset( - fd :: Logiset{V,W}, - relational_support_type :: Type{<:AbstractScalarOneStepRelationalMemoset} = _default_rs_type(W); - compute_relation_glob = false, - use_memoization = false, - ) where {V,W<:AbstractWorld} - - # @logmsg LogOverview "Logiset -> SupportedScalarLogiset " - - _fwd = fwd(fd) - _features = features(fd) - _relations = relations(fd) - _grouped_featsnaggrs = grouped_featsnaggrs(fd) - featsnaggrs = features_grouped_featsaggrsnops2featsnaggrs(features(fd), grouped_featsaggrsnops(fd)) - - compute_fwd_gs = begin - if globalrel in _relations - throw_n_log("globalrel in relations: $(_relations)") - _relations = filter!(l->l≠globalrel, _relations) - true - elseif compute_relation_glob - true - else - false - end - end - - _n_instances = ninstances(fd) - nrelations = length(_relations) - nfeatsnaggrs = sum(length.(_grouped_featsnaggrs)) - - # Prepare fwd_rs - fwd_rs = relational_support_type(fd, use_memoization) - - # Prepare fwd_gs - fwd_gs = begin - if compute_fwd_gs - ScalarOneStepGlobalMemoset(fd) - else - nothing - end - end - - # p = Progress(_n_instances, 1, "Computing EMD supports...") - Threads.@threads for i_instance in 1:_n_instances - # @logmsg LogDebug "Instance $(i_instance)/$(_n_instances)" - - # if i_instance == 1 || ((i_instance+1) % (floor(Int, ((_n_instances)/4))+1)) == 0 - # @logmsg LogOverview "Instance $(i_instance)/$(_n_instances)" - # end - - for (i_feature,aggregators) in enumerate(_grouped_featsnaggrs) - feature = _features[i_feature] - # @logmsg LogDebug "Feature $(i_feature)" - - fwdslice = fwdread_channel(_fwd, i_instance, i_feature) - - # @logmsg LogDebug fwdslice - - # Global relation (independent of the current world) - if compute_fwd_gs - # @logmsg LogDebug "globalrel" - - # TODO optimize: all aggregators are likely reading the same raw values. - for (i_featsnaggr,aggr) in aggregators - # Threads.@threads for (i_featsnaggr,aggr) in aggregators - - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, globalrel, feature, aggr) - - # @logmsg LogDebug "Aggregator[$(i_featsnaggr)]=$(aggr) --> $(gamma)" - - fwd_gs[i_instance, i_featsnaggr] = gamma - end - end - - if !use_memoization - # Other relations - for (i_relation,relation) in enumerate(_relations) - - # @logmsg LogDebug "Relation $(i_relation)/$(nrelations)" - - for (i_featsnaggr,aggr) in aggregators - fwd_rs_init_world_slice(fwd_rs, i_instance, i_featsnaggr, i_relation) - end - - for w in allworlds(fd, i_instance) - - # @logmsg LogDebug "World" w - - # TODO optimize: all aggregators are likely reading the same raw values. - for (i_featsnaggr,aggr) in aggregators - - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, w, relation, feature, aggr) - - # @logmsg LogDebug "Aggregator" aggr gamma - - fwd_rs[i_instance, w, i_featsnaggr, i_relation] = gamma - end - end - end - end - end - # next!(p) - end - OneStepFeaturedMemoset(fwd_rs, fwd_gs, featsnaggrs) - end -end - -fwd_rs(X::OneStepFeaturedMemoset) = X.fwd_rs -fwd_gs(X::OneStepFeaturedMemoset) = X.fwd_gs -featsnaggrs(X::OneStepFeaturedMemoset) = X.featsnaggrs - -ninstances(X::OneStepFeaturedMemoset) = ninstances(fwd_rs(X)) -# nfeatsnaggrs(X::OneStepFeaturedMemoset) = nfeatsnaggrs(fwd_rs(X)) - -# TODO delegate to the two components... -function checksupportconsistency( - fd::Logiset{V,W}, - X::OneStepFeaturedMemoset{V,W}, -) where {V,W<:AbstractWorld} - @assert ninstances(fd) == ninstances(X) "Consistency check failed! Unmatching ninstances for fd and support: $(ninstances(fd)) and $(ninstances(X))" - # @assert nrelations(fd) == (nrelations(fwd_rs(X)) + (isnothing(fwd_gs(X)) ? 0 : 1)) "Consistency check failed! Unmatching nrelations for fd and support: $(nrelations(fd)) and $(nrelations(fwd_rs(X)))+$((isnothing(fwd_gs(X)) ? 0 : 1))" - @assert nrelations(fd) >= nrelations(fwd_rs(X)) "Consistency check failed! Inconsistent nrelations for fd and support: $(nrelations(fd)) < $(nrelations(fwd_rs(X)))" - _nfeatsnaggrs = nfeatsnaggrs(fd) - @assert _nfeatsnaggrs == length(featsnaggrs(X)) "Consistency check failed! Unmatching featsnaggrs for fd and support: $(featsnaggrs(fd)) and $(featsnaggrs(X))" - return true -end - -usesmemo(X::OneStepFeaturedMemoset) = usesglobalmemo(X) || usesmodalmemo(X) -usesglobalmemo(X::OneStepFeaturedMemoset) = false -usesmodalmemo(X::OneStepFeaturedMemoset) = usesmemo(fwd_rs(X)) - -Base.size(X::OneStepFeaturedMemoset) = (size(fwd_rs(X)), (isnothing(fwd_gs(X)) ? () : size(fwd_gs(X)))) - -find_featsnaggr_id(X::OneStepFeaturedMemoset, feature::AbstractFeature, aggregator::Aggregator) = findfirst(x->x==(feature, aggregator), featsnaggrs(X)) - -function instances(X::OneStepFeaturedMemoset, inds::AbstractVector{<:Integer}, args...; kwargs...) - OneStepFeaturedMemoset( - instances(fwd_rs(X), inds, args...; kwargs...), - (isnothing(fwd_gs(X)) ? nothing : instances(fwd_gs(X), inds, args...; kwargs...)), - featsnaggrs(X) - ) -end - - -function hasnans(X::OneStepFeaturedMemoset) - hasnans(fwd_rs(X)) || (!isnothing(fwd_gs(X)) && hasnans(fwd_gs(X))) -end - -isminifiable(X::OneStepFeaturedMemoset) = isminifiable(fwd_rs(X)) && (isnothing(fwd_gs(X)) || isminifiable(fwd_gs(X))) - -function minify(X::OSSD) where {OSSD<:OneStepFeaturedMemoset} - (new_fwd_rs, new_fwd_gs), backmap = - minify([ - fwd_rs(X), - fwd_gs(X), - ]) - - X = OSSD( - new_fwd_rs, - new_fwd_gs, - featsnaggrs(X), - ) - X, backmap -end - -function displaystructure(X::OneStepFeaturedMemoset; indent_str = "") - out = "$(typeof(X))\t$((Base.summarysize(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "├ fwd_rs\t$(Base.summarysize(fwd_rs(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t" - if usesmodalmemo(X) - out *= "(shape $(Base.size(fwd_rs(X))), $(nmemoizedvalues(fwd_rs(X))) values, " * - "$(round(nonnothingshare(fwd_rs(X))*100, digits=2))% memoized)\n" - else - out *= "(shape $(Base.size(fwd_rs(X))))\n" - end - out *= indent_str * "└ fwd_gs\t" - if !isnothing(fwd_gs(X)) - out *= "$(Base.summarysize(fwd_gs(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t" - if usesglobalmemo(X) - out *= "(shape $(Base.size(fwd_gs(X))), $(nmemoizedvalues(fwd_gs(X))) values, " * - "$(round(nonnothingshare(fwd_gs(X))*100, digits=2))% memoized)\n" - else - out *= "(shape $(Base.size(fwd_gs(X))))\n" - end - else - out *= "−" - end - out -end - - -############################################################################################ - -function compute_global_gamma( - X::OneStepFeaturedMemoset{V,W}, - fd::Logiset{V,W}, - i_instance::Integer, - feature::AbstractFeature, - aggregator::Aggregator, - i_featsnaggr::Integer = find_featsnaggr_id(X, feature, aggregator), -) where {V,W<:AbstractWorld} - _fwd_gs = fwd_gs(X) - # @assert !isnothing(_fwd_gs) "Error. SupportedScalarLogiset must be built with compute_relation_glob = true for it to be ready to test global decisions." - if usesglobalmemo(X) && isnothing(_fwd_gs[i_instance, i_featsnaggr]) - error("TODO finish this: memoization on the global table") - # gamma = TODO... - # i_feature = findfeature(fd, feature) - # fwdslice = fwdread_channel(fwd(fd), i_instance, i_feature) - _fwd_gs[i_instance, i_featsnaggr] = gamma - end - _fwd_gs[i_instance, i_featsnaggr] -end - -function compute_modal_gamma( - X::OneStepFeaturedMemoset{V,W}, - fd::Logiset{V,W}, - i_instance::Integer, - w::W, - r::AbstractRelation, - feature::AbstractFeature, - aggregator::Aggregator, - i_featsnaggr = find_featsnaggr_id(X, feature, aggregator), - i_relation = nothing, -)::V where {V,W<:AbstractWorld} - _fwd_rs = fwd_rs(X) - if usesmodalmemo(X) && isnothing(_fwd_rs[i_instance, w, i_featsnaggr, i_relation]) - i_feature = findfeature(fd, feature) - fwdslice = fwdread_channel(fwd(fd), i_instance, i_feature) - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, w, r, feature, aggregator) - fwd_rs[i_instance, w, i_featsnaggr, i_relation, gamma] - end - _fwd_rs[i_instance, w, i_featsnaggr, i_relation] -end diff --git a/test/datasets.jl b/test/datasets.jl index 359c22c..dc29fb8 100644 --- a/test/datasets.jl +++ b/test/datasets.jl @@ -125,6 +125,10 @@ nonscalar_supported_logiset = SupportedLogiset(nonscalar_logiset) @test_nowarn SoleModels.parsecondition(SoleModels.ScalarCondition, "p > 0.5"; featvaltype = String, featuretype = Feature) @test SoleModels.ScalarCondition(features[1], >, 0.5) == SoleModels.parsecondition(SoleModels.ScalarCondition, "p > 0.5"; featvaltype = String, featuretype = Feature) +############################################################################################ +# Memoset's +############################################################################################ + memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstances(bool_supported_logiset)] @test_nowarn check(φ, bool_logiset, 1, w) @@ -135,9 +139,9 @@ memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstanc @test_logs (:warn,) check(φ, bool_supported_logiset, 1, w; use_memo = memoset) -@test_nowarn bool_supported_logiset2 = SupportedLogiset(bool_logiset, memoset) -@test_nowarn bool_supported_logiset2 = SupportedLogiset(bool_logiset, (memoset,)) -@test_nowarn bool_supported_logiset2 = SupportedLogiset(bool_logiset, [memoset]) +bool_supported_logiset2 = @test_nowarn SupportedLogiset(bool_logiset, memoset) +bool_supported_logiset2 = @test_nowarn SupportedLogiset(bool_logiset, (memoset,)) +bool_supported_logiset2 = @test_nowarn SupportedLogiset(bool_logiset, [memoset]) @test_throws AssertionError SupportedLogiset(bool_supported_logiset2) @@ -169,3 +173,22 @@ memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstanc @time check(φ, scalar_logiset, 1, w; use_memo = nothing) @time check(φ, scalar_logiset, 1, w; use_memo = memoset) + +############################################################################################ +# Scalar memoset's +############################################################################################ + +using SoleModels: ScalarMetaCondition +using SoleModels: ScalarOneStepRelationalMemoset, ScalarOneStepGlobalMemoset + +# metaconditions = [ScalarMetaCondition(features[1], >)] +metaconditions = [ScalarMetaCondition(f, test_op) for f in features for test_op in [>,<]] + +@test_throws AssertionError ScalarOneStepGlobalMemoset{Interval,Float64}(metaconditions, rand(1,2)) +@test_nowarn ScalarOneStepGlobalMemoset{Interval,Float64}(metaconditions, rand(1,22)) + +perform_initialization = true +bool_relationalmemoset = @test_nowarn ScalarOneStepRelationalMemoset(bool_logiset, metaconditions, [globalrel], perform_initialization) +bool_globalmemoset = @test_nowarn ScalarOneStepGlobalMemoset(bool_logiset, metaconditions, perform_initialization) + +@test_nowarn SupportedLogiset(bool_logiset, bool_relationalmemoset) From 5091b5e32cf70cd9b0548670de7618ac56b3c440 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Wed, 14 Jun 2023 17:35:33 +0200 Subject: [PATCH 12/77] ScalarOneStepMemoset works! featvalue = featchannel + readfeature. memosets can be full or onestep. F->FT for feature type variables. Onto dimensional logisets --- src/SoleModels.jl | 34 +- .../_parse-dimensional-condition.jl | 0 .../dimensional-logisets/active-logiset.jl | 36 +- .../dimensional-fwds.jl | 46 +-- .../dimensional-logiset.jl | 24 +- .../dimensional-ontologies.jl | 0 .../dimensional-supports.jl | 0 src/dimensional-logisets/main.jl | 68 ++++ .../dimensional-logisets/ontology.jl | 0 .../passive-dimensional-logiset.jl | 137 +++++++ .../representatives/Full0DFrame.jl | 0 .../representatives/Full1DFrame+IA.jl | 0 .../representatives/Full1DFrame+RCC.jl | 0 .../representatives/Full1DFrame.jl | 0 .../representatives/Full2DFrame.jl | 0 .../dimensional-logisets/scalar-logiset.jl | 6 +- src/logisets/{base => }/check.jl | 49 ++- src/logisets/{base => }/conditions.jl | 26 +- .../dimensional-logisets/datasets/main.jl | 59 --- .../datasets/passive-dimensional-dataset.jl | 94 ----- .../dimensional-logisets/gamma-access.jl | 53 --- src/logisets/dimensional-logisets/main.jl | 35 -- .../scalar-gamma-access.jl | 108 ------ src/logisets/{base => }/features.jl | 4 +- src/logisets/{base => }/logiset.jl | 93 +++-- src/logisets/{base => }/main.jl | 15 +- src/logisets/{base => }/memosets.jl | 79 ++-- src/logisets/{base => }/multilogiset.jl | 0 src/logisets/{base => }/representatives.jl | 8 +- .../{base => }/scalar/canonical-conditions.jl | 0 src/logisets/{base => }/scalar/conditions.jl | 44 ++- src/logisets/{base => }/scalar/main.jl | 0 src/logisets/{base => }/scalar/memosets.jl | 48 +-- .../{base => }/scalar/one-step-memoset.jl | 351 +++++++++++++----- src/logisets/{base => }/scalar/random.jl | 0 .../{base => }/scalar/representatives.jl | 30 +- .../{base => }/scalar/templated-formulas.jl | 0 .../{base => }/scalar/test-operators.jl | 0 .../{base => }/scalar/var-features.jl | 22 +- src/logisets/{base => }/supported-logiset.jl | 48 ++- src/logisets/{base => }/templated-formulas.jl | 0 test/datasets.jl | 70 +++- test/runtests.jl | 2 +- 43 files changed, 872 insertions(+), 717 deletions(-) rename src/{logisets => }/dimensional-logisets/_parse-dimensional-condition.jl (100%) rename src/{logisets => }/dimensional-logisets/active-logiset.jl (92%) rename src/{logisets/dimensional-logisets/datasets => dimensional-logisets}/dimensional-fwds.jl (87%) rename src/{logisets/dimensional-logisets/datasets => dimensional-logisets}/dimensional-logiset.jl (94%) rename src/{logisets => }/dimensional-logisets/dimensional-ontologies.jl (100%) rename src/{logisets/dimensional-logisets/datasets => dimensional-logisets}/dimensional-supports.jl (100%) create mode 100644 src/dimensional-logisets/main.jl rename src/{logisets => }/dimensional-logisets/ontology.jl (100%) create mode 100644 src/dimensional-logisets/passive-dimensional-logiset.jl rename src/{logisets => }/dimensional-logisets/representatives/Full0DFrame.jl (100%) rename src/{logisets => }/dimensional-logisets/representatives/Full1DFrame+IA.jl (100%) rename src/{logisets => }/dimensional-logisets/representatives/Full1DFrame+RCC.jl (100%) rename src/{logisets => }/dimensional-logisets/representatives/Full1DFrame.jl (100%) rename src/{logisets => }/dimensional-logisets/representatives/Full2DFrame.jl (100%) rename src/{logisets => }/dimensional-logisets/scalar-logiset.jl (97%) rename src/logisets/{base => }/check.jl (68%) rename src/logisets/{base => }/conditions.jl (85%) delete mode 100644 src/logisets/dimensional-logisets/datasets/main.jl delete mode 100644 src/logisets/dimensional-logisets/datasets/passive-dimensional-dataset.jl delete mode 100644 src/logisets/dimensional-logisets/gamma-access.jl delete mode 100644 src/logisets/dimensional-logisets/main.jl delete mode 100644 src/logisets/dimensional-logisets/scalar-gamma-access.jl rename src/logisets/{base => }/features.jl (93%) rename src/logisets/{base => }/logiset.jl (81%) rename src/logisets/{base => }/main.jl (85%) rename src/logisets/{base => }/memosets.jl (65%) rename src/logisets/{base => }/multilogiset.jl (100%) rename src/logisets/{base => }/representatives.jl (87%) rename src/logisets/{base => }/scalar/canonical-conditions.jl (100%) rename src/logisets/{base => }/scalar/conditions.jl (87%) rename src/logisets/{base => }/scalar/main.jl (100%) rename src/logisets/{base => }/scalar/memosets.jl (59%) rename src/logisets/{base => }/scalar/one-step-memoset.jl (55%) rename src/logisets/{base => }/scalar/random.jl (100%) rename src/logisets/{base => }/scalar/representatives.jl (62%) rename src/logisets/{base => }/scalar/templated-formulas.jl (100%) rename src/logisets/{base => }/scalar/test-operators.jl (100%) rename src/logisets/{base => }/scalar/var-features.jl (98%) rename src/logisets/{base => }/supported-logiset.jl (81%) rename src/logisets/{base => }/templated-formulas.jl (100%) diff --git a/src/SoleModels.jl b/src/SoleModels.jl index e064dc6..9ab137a 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -62,31 +62,37 @@ export propositions export parsecondition export modalities -export Logiset, SupportedLogiset, slicedataset, concatdatasets +export slicedataset, concatdatasets + +export World, Feature, featvalue +export ValueCondition, FunctionalCondition +export parsecondition +export SupportedLogiset, nmemoizedvalues +export ExplicitBooleanLogiset, checkcondition +export ExplicitLogiset, ScalarCondition + +export ninstances, nfeatures +export MultiLogiset, nmodalities, modalities + +export UnivariateMin, UnivariateMax, + UnivariateSoftMin, UnivariateSoftMax, + MultivariateFeature + +export VarFeature, + UnivariateNamedFeature, + UnivariateFeature # Definitions for logical datasets (i.e., logisets) -include("logisets/base/main.jl") +include("logisets/main.jl") -# export ninstances, nmodalities, modalities, nfeatures # export get_ontology, # get_interval_ontology # export DimensionalLogiset, Logiset, SupportedScalarLogiset -# export parsecondition - -# export UnivariateMin, UnivariateMax, -# UnivariateSoftMin, UnivariateSoftMax, -# MultivariateFeature - -# export VarFeature, AbstractUnivariateFeature, -# UnivariateNamedFeature, -# UnivariateFeature - # include("logisets/dimensional-logisets/main.jl") -# using .DimensionalDatasets: parsecondition # using .DimensionalDatasets: nfeatures, nrelations, # # diff --git a/src/logisets/dimensional-logisets/_parse-dimensional-condition.jl b/src/dimensional-logisets/_parse-dimensional-condition.jl similarity index 100% rename from src/logisets/dimensional-logisets/_parse-dimensional-condition.jl rename to src/dimensional-logisets/_parse-dimensional-condition.jl diff --git a/src/logisets/dimensional-logisets/active-logiset.jl b/src/dimensional-logisets/active-logiset.jl similarity index 92% rename from src/logisets/dimensional-logisets/active-logiset.jl rename to src/dimensional-logisets/active-logiset.jl index c1a92f9..7570ca3 100644 --- a/src/logisets/dimensional-logisets/active-logiset.jl +++ b/src/dimensional-logisets/active-logiset.jl @@ -21,8 +21,8 @@ featvaltype(d::AbstractFeatureLookupSet) = featvaltype(typeof(d)) featstruct :: AbstractFeatureLookupSet{V,FR}, i_instance :: Integer, w :: W, - feature :: F, - ) where {V,F<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} + feature :: FT, + ) where {V,FT<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} Return the feature value for `f` at world `w` on the `i`-th instance. @@ -33,8 +33,8 @@ function featvalue( featstruct :: AbstractFeatureLookupSet{V,FR}, i_instance :: Integer, w :: W, - feature :: F, -)::V where {V,F<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} + feature :: FT, +)::V where {V,FT<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} error("Please, provide method featvalue(::$(typeof(featstruct)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), feature::$(typeof(feature)))::$(V).") end @@ -69,11 +69,11 @@ end # # The most generic featstruct structure is a matrix of dictionaries of size (ninstances × nfeatures) # struct GenericFWD{ # V, -# F<:AbstractFeature, +# FT<:AbstractFeature, # W<:AbstractWorld, # FR<:AbstractFrame{W}, # D<:AbstractVector{<:AbstractDict{<:W,<:AbstractVector{<:V}}} -# } <: AbstractFeatureLookupSet{V,F,FR} +# } <: AbstractFeatureLookupSet{V,FT,FR} # d :: D # nfeatures :: Integer # end @@ -124,7 +124,7 @@ end # Others... # Base.@propagate_inbounds @inline fwdread_channeaoeu(featstruct::GenericFWD{V}, i_instance::Integer, i_feature::Integer) where {V} = TODO # const GenericFeaturedChannel{V} = TODO -# fwd_channel_interpret_world(fwc::GenericFeaturedChannel{V}, w::AbstractWorld) where {V} = TODO +# readfeature(fwc::GenericFeaturedChannel{V}, w::AbstractWorld) where {V} = TODO # isminifiable(::AbstractFeatureLookupSet) = true @@ -142,15 +142,15 @@ struct Logiset{ V, W<:AbstractWorld, FR<:AbstractFrame{W}, - F<:AbstractFeature, + FT<:AbstractFeature, FWD<:AbstractFeatureLookupSet{V,FR}, -} <: AbstractLogiset{W,V,F,Bool,FR} +} <: AbstractLogiset{W,V,FT,Bool,FR} # Feature lookup structure featstruct :: FWD # Features - features :: Vector{F} + features :: Vector{FT} # Accessibility relations relations :: Vector{<:AbstractRelation} @@ -158,16 +158,16 @@ struct Logiset{ # Initial world(s) initialworld :: Union{Nothing,W,AbstractWorldSet{<:W}} - function Logiset{V,W,FR,F,FWD}( + function Logiset{V,W,FR,FT,FWD}( featstruct :: FWD, - features :: AbstractVector{F}, + features :: AbstractVector{FT}, relations :: AbstractVector{<:AbstractRelation}, ; allow_no_instances = false, initialworld = nothing, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W},FWD<:AbstractFeatureLookupSet{V,FR},F<:AbstractFeature} + ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W},FWD<:AbstractFeatureLookupSet{V,FR},FT<:AbstractFeature} features = collect(features) - ty = "Logiset{$(V),$(W),$(FR),$(F)}" + ty = "Logiset{$(V),$(W),$(FR),$(FT)}" @assert allow_no_instances || ninstances(featstruct) > 0 "Can't instantiate $(ty) with no instance. (featstruct's type $(typeof(featstruct)))" @assert nfeatures(featstruct) == length(features) "Can't instantiate $(ty) with different numbers of instances $(ninstances(featstruct)) and of features $(length(features))." check_initialworld(Logiset, initialworld, W) @@ -175,7 +175,7 @@ struct Logiset{ V, W, FR, - F, + FT, FWD, }( featstruct, @@ -192,9 +192,9 @@ struct Logiset{ kwargs... ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W},FWD<:AbstractFeatureLookupSet{V,FR}} features = collect(features) - F = Union{typeof.(features)...} - features = Vector{F}(features) - Logiset{V,W,FR,F,FWD}(featstruct, features, args...; kwargs...) + FT = Union{typeof.(features)...} + features = Vector{FT}(features) + Logiset{V,W,FR,FT,FWD}(featstruct, features, args...; kwargs...) end function Logiset{V,W}( diff --git a/src/logisets/dimensional-logisets/datasets/dimensional-fwds.jl b/src/dimensional-logisets/dimensional-fwds.jl similarity index 87% rename from src/logisets/dimensional-logisets/datasets/dimensional-fwds.jl rename to src/dimensional-logisets/dimensional-fwds.jl index 0405c16..716ccb5 100644 --- a/src/logisets/dimensional-logisets/datasets/dimensional-fwds.jl +++ b/src/dimensional-logisets/dimensional-fwds.jl @@ -170,28 +170,28 @@ Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimension fwd.d[:, i_feature] = fwdslice end -Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,OneWorld}, i_instance::Integer, i_feature::Integer) where {T} = +Base.@propagate_inbounds @inline featchannel(fwd::UniformFullDimensionalFWD{T,OneWorld}, i_instance::Integer, i_feature::Integer) where {T} = fwd.d[i_instance, i_feature] const OneWorldFeaturedChannel{T} = T -fwd_channel_interpret_world(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc +readfeature(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimensionalFWD{T,<:Interval}, i_feature::Integer, fwdslice::Array{T,3}) where {T} fwd.d[:, :, :, i_feature] = fwdslice end -Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,<:Interval}, i_instance::Integer, i_feature::Integer) where {T} = +Base.@propagate_inbounds @inline featchannel(fwd::UniformFullDimensionalFWD{T,<:Interval}, i_instance::Integer, i_feature::Integer) where {T} = @views fwd.d[:,:,i_instance, i_feature] const IntervalFeaturedChannel{T} = AbstractArray{T,2} -fwd_channel_interpret_world(fwc::IntervalFeaturedChannel{T}, w::Interval) where {T} = +readfeature(fwc::IntervalFeaturedChannel{T}, w::Interval) where {T} = fwc[w.x, w.y] Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, i_feature::Integer, fwdslice::Array{T,5}) where {T} fwd.d[:, :, :, :, :, i_feature] = fwdslice end -Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, i_instance::Integer, i_feature::Integer) where {T} = +Base.@propagate_inbounds @inline featchannel(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, i_instance::Integer, i_feature::Integer) where {T} = @views fwd.d[:,:,:,:,i_instance, i_feature] const Interval2DFeaturedChannel{T} = AbstractArray{T,4} -fwd_channel_interpret_world(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) where {T} = +readfeature(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) where {T} = fwc[w.x.x, w.x.y, w.y.x, w.y.y] const FWDFeatureSlice{T} = Union{ @@ -239,10 +239,10 @@ const FWDFeatureSlice{T} = Union{ # fwd.d[:, i_feature] = fwdslice # end -# Base.@propagate_inbounds @inline fwdread_channel(fwd::OneWorldFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = +# Base.@propagate_inbounds @inline featchannel(fwd::OneWorldFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = # fwd.d[i_instance, i_feature] # const OneWorldFeaturedChannel{T} = T -# fwd_channel_interpret_world(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc +# readfeature(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc ############################################################################################ # FWD, Interval: 4D array (x × y × ninstances × nfeatures) @@ -287,10 +287,10 @@ const FWDFeatureSlice{T} = Union{ # function instances(fwd::IntervalFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # IntervalFWD{T}(if return_view == Val(true) @view fwd.d[:,:,inds,:] else fwd.d[:,:,inds,:] end) # end -# Base.@propagate_inbounds @inline fwdread_channel(fwd::IntervalFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = +# Base.@propagate_inbounds @inline featchannel(fwd::IntervalFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = # @views fwd.d[:,:,i_instance, i_feature] # const IntervalFeaturedChannel{T} = AbstractArray{T,2} -# fwd_channel_interpret_world(fwc::IntervalFeaturedChannel{T}, w::Interval) where {T} = +# readfeature(fwc::IntervalFeaturedChannel{T}, w::Interval) where {T} = # fwc[w.x, w.y] ############################################################################################ @@ -337,35 +337,13 @@ const FWDFeatureSlice{T} = Union{ # function instances(fwd::Interval2DFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} # Interval2DFWD{T}(if return_view == Val(true) @view fwd.d[:,:,:,:,inds,:] else fwd.d[:,:,:,:,inds,:] end) # end -# Base.@propagate_inbounds @inline fwdread_channel(fwd::Interval2DFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = +# Base.@propagate_inbounds @inline featchannel(fwd::Interval2DFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = # @views fwd.d[:,:,:,:,i_instance, i_feature] # const Interval2DFeaturedChannel{T} = AbstractArray{T,4} -# fwd_channel_interpret_world(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) where {T} = +# readfeature(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) where {T} = # fwc[w.x.x, w.x.y, w.y.x, w.y.y] ############################################################################################ ############################################################################################ ############################################################################################ - - -# TODO add AbstractWorldSet type -function apply_aggregator(fwdslice::FWDFeatureSlice{T}, worlds::Any, aggregator::Agg) where {T,Agg<:Aggregator} - - # TODO try reduce(aggregator, worlds; init=bottom(aggregator, T)) - # TODO remove this SoleModels.aggregator_to_binary... - - if length(worlds |> collect) == 0 - aggregator_bottom(aggregator, T) - else - aggregator((w)->fwd_channel_interpret_world(fwdslice, w), worlds) - end - - # opt = SoleModels.aggregator_to_binary(aggregator) - # gamma = bottom(aggregator, T) - # for w in worlds - # e = fwd_channel_interpret_world(fwdslice, w) - # gamma = opt(gamma,e) - # end - # gamma -end diff --git a/src/logisets/dimensional-logisets/datasets/dimensional-logiset.jl b/src/dimensional-logisets/dimensional-logiset.jl similarity index 94% rename from src/logisets/dimensional-logisets/datasets/dimensional-logiset.jl rename to src/dimensional-logisets/dimensional-logiset.jl index 1fb7d29..1410aa3 100644 --- a/src/logisets/dimensional-logisets/datasets/dimensional-logiset.jl +++ b/src/dimensional-logisets/dimensional-logiset.jl @@ -13,7 +13,7 @@ struct DimensionalLogiset{ V<:Number, N, W<:AbstractWorld, - D<:PassiveDimensionalDataset{N,W}, + D<:PassiveDimensionalLogiset{N,W}, FT<:AbstractFeature{V}, G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, @@ -38,7 +38,7 @@ struct DimensionalLogiset{ ######################################################################################## function DimensionalLogiset{V,N,W}( - domain::PassiveDimensionalDataset{N}, + domain::PassiveDimensionalLogiset{N}, ontology::Ontology{W}, features::AbstractVector{<:AbstractFeature}, grouped_featsaggrsnops::AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}; @@ -82,24 +82,24 @@ struct DimensionalLogiset{ ######################################################################################## function DimensionalLogiset{V,N,W}( - domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, + domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, ontology :: Ontology{W}, features :: AbstractVector{<:AbstractFeature}, grouped_featsnops :: AbstractVector; kwargs..., ) where {V,N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{N,W}(domain) : domain) + domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalLogiset{N,W}(domain) : domain) grouped_featsaggrsnops = grouped_featsnops2grouped_featsaggrsnops(grouped_featsnops) DimensionalLogiset{V,N,W}(domain, ontology, features, grouped_featsaggrsnops; kwargs...) end function DimensionalLogiset{V,N,W}( - domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, + domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, ontology :: Ontology{W}, mixed_features :: AbstractVector; kwargs..., ) where {V,N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{N,W}(domain) : domain) + domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalLogiset{N,W}(domain) : domain) @assert all(isa.(mixed_features, MixedFeature)) "Unknown feature encountered! " * "$(filter(f->!isa(f, MixedFeature), mixed_features)), " * @@ -168,19 +168,19 @@ struct DimensionalLogiset{ ######################################################################################## function DimensionalLogiset{V,N}( - domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, + domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, ontology :: Ontology{W}, args...; kwargs..., ) where {V,N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{N,W}(domain) : domain) + domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalLogiset{N,W}(domain) : domain) DimensionalLogiset{V,N,W}(domain, ontology, args...; kwargs...) end ######################################################################################## function DimensionalLogiset{V}( - domain :: Union{PassiveDimensionalDataset,AbstractDimensionalDataset}, + domain :: Union{PassiveDimensionalLogiset,AbstractDimensionalDataset}, args...; kwargs..., ) where {V} @@ -190,7 +190,7 @@ struct DimensionalLogiset{ ######################################################################################## function DimensionalLogiset( - domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, + domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, ontology :: Ontology{W}, features :: AbstractVector{<:AbstractFeature}, args...; @@ -206,12 +206,12 @@ struct DimensionalLogiset{ preserves_type(::typeof(maximum)) = true # TODO fix function DimensionalLogiset( - domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, + domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, ontology :: Ontology{W}, mixed_features :: AbstractVector; kwargs..., ) where {N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{dimensionality(domain),W}(domain) : domain) + domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalLogiset{dimensionality(domain),W}(domain) : domain) @assert all((f)->(preserves_type(f)), mixed_features) "Please, specify the feature output type V upon construction, as in: DimensionalLogiset{V}(...)." # TODO highlight and improve V = eltype(domain) DimensionalLogiset{V}(domain, ontology, mixed_features; kwargs...) diff --git a/src/logisets/dimensional-logisets/dimensional-ontologies.jl b/src/dimensional-logisets/dimensional-ontologies.jl similarity index 100% rename from src/logisets/dimensional-logisets/dimensional-ontologies.jl rename to src/dimensional-logisets/dimensional-ontologies.jl diff --git a/src/logisets/dimensional-logisets/datasets/dimensional-supports.jl b/src/dimensional-logisets/dimensional-supports.jl similarity index 100% rename from src/logisets/dimensional-logisets/datasets/dimensional-supports.jl rename to src/dimensional-logisets/dimensional-supports.jl diff --git a/src/dimensional-logisets/main.jl b/src/dimensional-logisets/main.jl new file mode 100644 index 0000000..cd89bb9 --- /dev/null +++ b/src/dimensional-logisets/main.jl @@ -0,0 +1,68 @@ +module DimensionalDatasets + + +import Base: size, show, getindex, iterate, length, push!, eltype + +using BenchmarkTools +using ComputedFieldTypes +using DataStructures +using ThreadSafeDicts +using ProgressMeter + +using SoleBase + +using SoleLogics +using SoleLogics: AbstractFormula, AbstractWorld, AbstractRelation +using SoleLogics: AbstractFrame, AbstractDimensionalFrame, FullDimensionalFrame +import SoleLogics: worldtype, accessibles, allworlds, alphabet, initialworld + +using SoleData +import SoleData: _isnan, hasnans, nvariables, max_channel_size, channel_size +import SoleData: instance, get_instance, slicedataset, instances +import SoleData: dimensionality + +############################################################################################ + +function check_initialworld(FD::Type{<:AbstractLogiset}, initialworld, W) + @assert isnothing(initialworld) || initialworld isa W "Cannot instantiate " * + "$(FD) with worldtype = $(W) but initialworld of type $(typeof(initialworld))." +end + +using SoleModels.utils + +# # Dataset structures +include("passive-dimensional-logiset.jl") + +include("dimensional-logiset.jl") + +# Frame-specific featured world datasets and supports +include("dimensional-fwds.jl") + +include("dimensional-supports.jl") + +# export parsecondition + +# # Conditions on features for dimensional datasets +# include("_parse-dimensional-condition.jl") + +# # Concrete type for ontologies +# include("ontology.jl") # TODO frame inside the ontology? + +# export DimensionalLogiset, Logiset, SupportedScalarLogiset + +# const GenericModalDataset = Union{AbstractDimensionalDataset,AbstractLogiset,MultiLogiset} + +# # Dimensional ontologies +# include("dimensional-ontologies.jl") + +using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame +using SoleLogics: X, Y, Z + +# Representatives for dimensional frames +include("representatives/Full0DFrame.jl") +include("representatives/Full1DFrame.jl") +include("representatives/Full1DFrame+IA.jl") +include("representatives/Full1DFrame+RCC.jl") +include("representatives/Full2DFrame.jl") + +end diff --git a/src/logisets/dimensional-logisets/ontology.jl b/src/dimensional-logisets/ontology.jl similarity index 100% rename from src/logisets/dimensional-logisets/ontology.jl rename to src/dimensional-logisets/ontology.jl diff --git a/src/dimensional-logisets/passive-dimensional-logiset.jl b/src/dimensional-logisets/passive-dimensional-logiset.jl new file mode 100644 index 0000000..b20c263 --- /dev/null +++ b/src/dimensional-logisets/passive-dimensional-logiset.jl @@ -0,0 +1,137 @@ +using SoleData: slicedataset +import SoleData: get_instance, ninstances, nvariables, channel_size, max_channel_size, dimensionality, eltype +using SoleData: AbstractDimensionalDataset, + AbstractDimensionalInstance, + AbstractDimensionalChannel, + UniformDimensionalDataset, + DimensionalInstance, + DimensionalChannel + +using SoleLogics: TruthValue + +""" +A logiset with a dimensional domain. TODO explain +""" +struct PassiveDimensionalLogiset{ + N, + W<:AbstractWorld, + DOM<:AbstractDimensionalDataset, + FR<:AbstractDimensionalFrame{N,W}, +} <: AbstractLogiset{W,U where U,FT where FT<:VarFeature,FR} + + d::DOM + + function PassiveDimensionalLogiset{N,W,DOM,FR}( + d::DOM, + ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset,FR<:AbstractDimensionalFrame{N,W}} + ty = "PassiveDimensionalLogiset{$(N),$(W),$(DOM),$(FR)}" + @assert N == dimensionality(d) "ERROR! Dimensionality mismatch: can't instantiate $(ty) with underlying structure $(DOM). $(N) == $(dimensionality(d)) should hold." + @assert SoleLogics.goeswithdim(W, N) "ERROR! Dimensionality mismatch: can't interpret worldtype $(W) on PassiveDimensionalLogiset of dimensionality = $(N)" + new{N,W,DOM,FR}(d) + end + + function PassiveDimensionalLogiset{N,W,DOM}( + d::DOM, + ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset} + FR = typeof(_frame(d)) + _W = worldtype(FR) + @assert W <: _W "This should hold: $(W) <: $(_W)" + PassiveDimensionalLogiset{N,_W,DOM,FR}(d) + end + + function PassiveDimensionalLogiset{N,W}( + d::DOM, + ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset} + PassiveDimensionalLogiset{N,W,DOM}(d) + end + + function PassiveDimensionalLogiset( + d::AbstractDimensionalDataset, + # worldtype::Type{<:AbstractWorld}, + ) + W = worldtype(_frame(d)) + PassiveDimensionalLogiset{dimensionality(d),W}(d) + end +end + +function featchannel( + X::PassiveDimensionalLogiset, + i_instance::Integer, + f::AbstractFeature, +) + get_instance(X.d, i_instance) +end + +function readfeature( + X::PassiveDimensionalLogiset, + featchannel::Any, + w::W, + f::AbstractFeature{U}, +) where {U,W<:AbstractWorld} + w_values = interpret_world(w, featchannel) + computefeature(f, w_values)::U +end + +ninstances(X::PassiveDimensionalLogiset) = ninstances(X.d) +frame(X::PassiveDimensionalLogiset, i_instance::Integer) = _frame(X.d, i_instance) + +function allfeatvalues( + X::PassiveDimensionalLogiset, + i_instance, + f, +) + [readfeature(X, featchannel(X, i_instance, f), w, f) for w in allworlds(X, i_instance)] +end + +Base.size(X::PassiveDimensionalLogiset) = Base.size(X.d) + +nvariables(X::PassiveDimensionalLogiset) = nvariables(X.d) +channel_size(X::PassiveDimensionalLogiset) = channel_size(X.d) +max_channel_size(X::PassiveDimensionalLogiset) = max_channel_size(X.d) +dimensionality(X::PassiveDimensionalLogiset) = dimensionality(X.d) +eltype(X::PassiveDimensionalLogiset) = eltype(X.d) + +get_instance(X::PassiveDimensionalLogiset, args...) = get_instance(X.d, args...) + +function instances( + X::PassiveDimensionalLogiset, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false); + kwargs... +) where {N,W} + PassiveDimensionalLogiset{N,W}(instances(X.d, inds, args...; kwargs...)) +end + +function concatdatasets(Xs::PassiveDimensionalLogiset{N,W}...) where {N,W} + PassiveDimensionalLogiset(concatdatasets([X.d for X in Xs]...)) +end + + +function displaystructure(Xm::PassiveDimensionalLogiset; indent_str = "", include_ninstances = true, include_nmetaconditions = true, include_nrelations = true) + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) + pieces = [] + push!(pieces, "PassiveDimensionalLogiset ($(memoizationinfo(Xm)), $(humansize(Xm)))") + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") + push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") + push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + push!(pieces, "$(padattribute("dimensionality:", dimensionality(Xm)))") + if include_ninstances + push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") + end + push!(pieces, "$(padattribute("# variables:", nvariables(Xm)))") + push!(pieces, "$(padattribute("channel_size:", channel_size(Xm)))") + push!(pieces, "$(padattribute("size × eltype:", "$(size(Xm.d)) × $(eltype(Xm.d))"))") + + return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" +end + +hasnans(X::PassiveDimensionalLogiset) = hasnans(X.d) + +worldtype(X::PassiveDimensionalLogiset{N,W}) where {N,W} = W + + +############################################################################################ + +_frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance::Integer) = _frame(X) +_frame(X::Union{UniformDimensionalDataset,AbstractArray}) = FullDimensionalFrame(channel_size(X)) diff --git a/src/logisets/dimensional-logisets/representatives/Full0DFrame.jl b/src/dimensional-logisets/representatives/Full0DFrame.jl similarity index 100% rename from src/logisets/dimensional-logisets/representatives/Full0DFrame.jl rename to src/dimensional-logisets/representatives/Full0DFrame.jl diff --git a/src/logisets/dimensional-logisets/representatives/Full1DFrame+IA.jl b/src/dimensional-logisets/representatives/Full1DFrame+IA.jl similarity index 100% rename from src/logisets/dimensional-logisets/representatives/Full1DFrame+IA.jl rename to src/dimensional-logisets/representatives/Full1DFrame+IA.jl diff --git a/src/logisets/dimensional-logisets/representatives/Full1DFrame+RCC.jl b/src/dimensional-logisets/representatives/Full1DFrame+RCC.jl similarity index 100% rename from src/logisets/dimensional-logisets/representatives/Full1DFrame+RCC.jl rename to src/dimensional-logisets/representatives/Full1DFrame+RCC.jl diff --git a/src/logisets/dimensional-logisets/representatives/Full1DFrame.jl b/src/dimensional-logisets/representatives/Full1DFrame.jl similarity index 100% rename from src/logisets/dimensional-logisets/representatives/Full1DFrame.jl rename to src/dimensional-logisets/representatives/Full1DFrame.jl diff --git a/src/logisets/dimensional-logisets/representatives/Full2DFrame.jl b/src/dimensional-logisets/representatives/Full2DFrame.jl similarity index 100% rename from src/logisets/dimensional-logisets/representatives/Full2DFrame.jl rename to src/dimensional-logisets/representatives/Full2DFrame.jl diff --git a/src/logisets/dimensional-logisets/scalar-logiset.jl b/src/dimensional-logisets/scalar-logiset.jl similarity index 97% rename from src/logisets/dimensional-logisets/scalar-logiset.jl rename to src/dimensional-logisets/scalar-logiset.jl index 1bbd4b8..6821c3b 100644 --- a/src/logisets/dimensional-logisets/scalar-logiset.jl +++ b/src/dimensional-logisets/scalar-logiset.jl @@ -5,10 +5,10 @@ Active scalar datasets are active logical datasets with scalar features. const AbstractActiveScalarLogiset{ W<:AbstractWorld, V<:Number, - F<:AbstractFeature{V}, + FT<:AbstractFeature{V}, T<:TruthValue, FR<:AbstractFrame{W,T} -} = AbstractLogiset{W,V,F,T,FR} +} = AbstractLogiset{W,V,FT,T,FR} function grouped_featsaggrsnops(X::AbstractScalarLogiset) return error("Please, provide method grouped_featsaggrsnops(::$(typeof(X))).") @@ -92,11 +92,9 @@ end function features_grouped_featsaggrsnops2featsnaggrs(features, grouped_featsaggrsnops) featsnaggrs = Tuple{<:AbstractFeature,<:Aggregator}[] - i_featsnaggr = 1 for (feat,aggrsnops) in zip(features, grouped_featsaggrsnops) for aggr in keys(aggrsnops) push!(featsnaggrs, (feat,aggr)) - i_featsnaggr += 1 end end featsnaggrs diff --git a/src/logisets/base/check.jl b/src/logisets/check.jl similarity index 68% rename from src/logisets/base/check.jl rename to src/logisets/check.jl index 0da497e..22e5880 100644 --- a/src/logisets/base/check.jl +++ b/src/logisets/check.jl @@ -24,10 +24,10 @@ function check( X::AbstractLogiset{W,U}, i_instance::Integer, w::Union{Nothing,W,AbstractVector{<:W}} = nothing; - use_memo::Union{Nothing,AbstractMemoset{W},AbstractVector{<:AbstractDict{<:F,<:WorldSet}}} = nothing, + use_memo::Union{Nothing,AbstractMemoset{W},AbstractVector{<:AbstractDict{<:FT,<:WorldSet}}} = nothing, perform_normalization::Bool = true, memo_max_height::Union{Nothing,Int} = nothing, -) where {W<:AbstractWorld,U,F<:SoleLogics.AbstractFormula} +) where {W<:AbstractWorld,U,FT<:SoleLogics.AbstractFormula} if isnothing(w) # w = SoleLogics.initialworld(X, i_instance) @@ -45,6 +45,19 @@ function check( readformula(memo_structure::AbstractMemoset, φ::AbstractFormula) = Base.getindex(memo_structure, i_instance, SoleLogics.tree(φ)) hasformula(memo_structure::AbstractMemoset, φ::AbstractFormula) = haskey(memo_structure, i_instance, SoleLogics.tree(φ)) + onestep_memoset = begin + if X isa SupportedLogiset && supporttypes(X) <: Tuple{<:AbstractOneStepMemoset,<:AbstractFullMemoset} + supports(X)[1] + else + nothing + end + end + + if perform_normalization + # Only allow flippings when no onestep is used. + φ = normalize(φ; profile = :modelchecking, allow_proposition_flipping = isnothing(onestep_memoset)) + end + X, memo_structure = begin if X isa SupportedLogiset && usesfullmemo(X) if !isnothing(use_memo) @@ -61,37 +74,45 @@ function check( end end - # if X isa SupportedLogiset - # X = base(X) - # end - if !isnothing(memo_max_height) forget_list = Vector{SoleLogics.SyntaxTree}() end - if perform_normalization - φ = normalize(φ; profile = :modelchecking) - end - fr = frame(X, i_instance) if !hasformula(memo_structure, φ) for ψ in unique(SoleLogics.subformulas(φ)) - # @show ψ if !isnothing(memo_max_height) && height(ψ) > memo_max_height push!(forget_list, ψ) end + if !hasformula(memo_structure, ψ) tok = token(ψ) - setformula(memo_structure, ψ, begin - if tok isa SoleLogics.AbstractOperator + + worldset = begin + if !isnothing(onestep_memoset) && SoleLogics.height(ψ) == 1 && tok isa SoleLogics.AbstractRelationalOperator && + SoleLogics.ismodal(tok) && SoleLogics.isunary(tok) && SoleLogics.isdiamond(tok) && + token(first(children(ψ))) isa Proposition + # println("ONESTEP!") + # println(syntaxstring(ψ)) + _rel = SoleLogics.relation(tok) + condition = atom(token(first(children(ψ)))) + _metacond = metacond(condition) + _feature = feature(condition) + _featchannel = featchannel(X, i_instance, _feature) + filter(world->begin + gamma = featchannel_onestep_aggregation(X, onestep_memoset, _featchannel, i_instance, world, _rel, _metacond) + apply_test_operator(test_operator(_metacond), gamma, threshold(condition)) + end, allworlds(fr)) + elseif tok isa SoleLogics.AbstractOperator collect(SoleLogics.collateworlds(fr, tok, map(f->readformula(memo_structure, f), children(ψ)))) elseif tok isa Proposition filter(w->check(tok, X, i_instance, w), collect(allworlds(fr))) else error("Unexpected token encountered in _check: $(typeof(tok))") end - end) + end + setformula(memo_structure, ψ, worldset) end # @show syntaxstring(ψ), readformula(memo_structure, ψ) end diff --git a/src/logisets/base/conditions.jl b/src/logisets/conditions.jl similarity index 85% rename from src/logisets/base/conditions.jl rename to src/logisets/conditions.jl index 8f3a330..9d808bf 100644 --- a/src/logisets/base/conditions.jl +++ b/src/logisets/conditions.jl @@ -6,7 +6,7 @@ import SoleLogics: negation, propositions import Base: isequal, hash, in, isfinite, length """ - abstract type AbstractCondition{F<:AbstractFeature} end + abstract type AbstractCondition{FT<:AbstractFeature} end Abstract type for representing conditions that can be interpreted and evaluated on worlds of instances of a logical dataset. In logical contexts, @@ -18,7 +18,7 @@ See also [`ScalarMetaCondition`](@ref), [`ScalarCondition`](@ref). """ -abstract type AbstractCondition{F<:AbstractFeature} end +abstract type AbstractCondition{FT<:AbstractFeature} end # Check a condition (e.g, on a world of a logiset instance) function checkcondition(c::AbstractCondition, args...; kwargs...) @@ -29,10 +29,10 @@ end # function checkcondition( # c::AbstractCondition, -# X::AbstractLogiset{W,U,F}, +# X::AbstractLogiset{W,U,FT}, # i_instance::Integer, # w::W, -# ) where {W<:AbstractWorld,U,F<:AbstractFeature} +# ) where {W<:AbstractWorld,U,FT<:AbstractFeature} # error("Please, provide method checkcondition(c::$(typeof(c)), X::$(typeof(X)), i_instance::$(typeof(i_instance)), w::$(typeof(w))).") # end @@ -61,16 +61,16 @@ end ############################################################################################ """ - struct ValueCondition{F<:AbstractFeature} <: AbstractCondition{F} - feature::F + struct ValueCondition{FT<:AbstractFeature} <: AbstractCondition{FT} + feature::FT end A condition which yields a truth value equal to the value of a feature. See also [`AbstractFeature`](@ref). """ -struct ValueCondition{F<:AbstractFeature} <: AbstractCondition{F} - feature::F +struct ValueCondition{FT<:AbstractFeature} <: AbstractCondition{FT} + feature::FT end checkcondition(c::ValueCondition, args...; kwargs...) = featvalue(c.feature, args...; kwargs...) @@ -89,17 +89,17 @@ end ############################################################################################ """ - struct FunctionalCondition{F<:AbstractFeature} <: AbstractCondition{F} - feature::F - f::F + struct FunctionalCondition{FT<:AbstractFeature} <: AbstractCondition{FT} + feature::FT + f::FT end A condition which yields a truth value equal to the value of a function. See also [`AbstractFeature`](@ref). """ -struct FunctionalCondition{F<:AbstractFeature} <: AbstractCondition{F} - feature::F +struct FunctionalCondition{FT<:AbstractFeature} <: AbstractCondition{FT} + feature::FT f::Function end diff --git a/src/logisets/dimensional-logisets/datasets/main.jl b/src/logisets/dimensional-logisets/datasets/main.jl deleted file mode 100644 index 8e392a6..0000000 --- a/src/logisets/dimensional-logisets/datasets/main.jl +++ /dev/null @@ -1,59 +0,0 @@ - -import Base: size, show, getindex, iterate, length, push!, eltype - -using BenchmarkTools -using ComputedFieldTypes -using DataStructures -using ThreadSafeDicts -using ProgressMeter - -using SoleBase -using SoleBase: LogOverview, LogDebug, LogDetail, throw_n_log -using Logging: @logmsg - -using SoleLogics -using SoleLogics: AbstractFormula, AbstractWorld, AbstractRelation -using SoleLogics: AbstractFrame, AbstractDimensionalFrame, FullDimensionalFrame -import SoleLogics: worldtype, accessibles, allworlds, alphabet, initialworld - -using SoleData -import SoleData: _isnan, hasnans, nvariables, max_channel_size, channel_size -import SoleData: instance, get_instance, slicedataset, instances -import SoleData: dimensionality - -using SoleModels -using SoleModels: Aggregator, AbstractCondition -using SoleModels: BoundedScalarConditions -using SoleModels: CanonicalFeatureGeq, CanonicalFeatureGeqSoft, CanonicalFeatureLeq, CanonicalFeatureLeqSoft -using SoleModels: AbstractLogiset, AbstractMultiModalFrame -using SoleModels: MultiLogiset, AbstractLogiset -using SoleModels: apply_test_operator, existential_aggregator, aggregator_bottom, aggregator_to_binary -import SoleModels: representatives, ScalarMetaCondition, ScalarCondition, featvaltype -import SoleModels: ninstances, nrelations, nfeatures, check, instances, minify -import SoleModels: nmodalities, frames, displaystructure, frame -import SoleModels: grouped_featsaggrsnops, features, grouped_metaconditions, alphabet, findfeature, findrelation, isminifiable - -using SoleModels: grouped_featsnops2grouped_featsaggrsnops, - grouped_featsaggrsnops2grouped_featsnops, - features_grouped_featsaggrsnops2featsnaggrs_grouped_featsnaggrs, - features_grouped_featsaggrsnops2featsnaggrs, - features_grouped_featsaggrsnops2grouped_featsnaggrs -############################################################################################ - -function check_initialworld(FD::Type{<:AbstractLogiset}, initialworld, W) - @assert isnothing(initialworld) || initialworld isa W "Cannot instantiate " * - "$(FD) with worldtype = $(W) but initialworld of type $(typeof(initialworld))." -end - -include("passive-dimensional-datasets.jl") - -include("dimensional-logiset.jl") - -# Frame-specific featured world datasets and supports -include("dimensional-fwds.jl") - -_default_fwd_type(::Type{<:FullDimensionalFrame}) = UniformFullDimensionalFWD - -include("dimensional-supports.jl") - -include("check.jl") diff --git a/src/logisets/dimensional-logisets/datasets/passive-dimensional-dataset.jl b/src/logisets/dimensional-logisets/datasets/passive-dimensional-dataset.jl deleted file mode 100644 index 2acef34..0000000 --- a/src/logisets/dimensional-logisets/datasets/passive-dimensional-dataset.jl +++ /dev/null @@ -1,94 +0,0 @@ -using SoleData: slicedataset -import SoleData: get_instance, ninstances, nvariables, channel_size, max_channel_size, dimensionality, eltype -using SoleData: AbstractDimensionalDataset, - AbstractDimensionalInstance, - AbstractDimensionalChannel, - UniformDimensionalDataset, - DimensionalInstance, - DimensionalChannel - -using SoleLogics: TruthValue - -# A modal dataset can be *active* or *passive*. -# -# A passive modal dataset is one that you can interpret decisions on, but cannot necessarily -# enumerate decisions for, as it doesn't have objects for storing the logic (relations, features, etc.). -# Dimensional datasets are passive. - -struct PassiveDimensionalDataset{ - N, - W<:AbstractWorld, - DOM<:AbstractDimensionalDataset, - FR<:AbstractDimensionalFrame{N,W}, -} <: AbstractLogiset{W,FT where FT<:VarFeature,Bool,FR} # Note: truth value could by different - - d::DOM - - function PassiveDimensionalDataset{N,W,DOM,FR}( - d::DOM, - ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset,FR<:AbstractDimensionalFrame{N,W}} - ty = "PassiveDimensionalDataset{$(N),$(W),$(DOM),$(FR)}" - @assert N == dimensionality(d) "ERROR! Dimensionality mismatch: can't instantiate $(ty) with underlying structure $(DOM). $(N) == $(dimensionality(d)) should hold." - @assert SoleLogics.goeswithdim(W, N) "ERROR! Dimensionality mismatch: can't interpret worldtype $(W) on PassiveDimensionalDataset of dimensionality = $(N)" - new{N,W,DOM,FR}(d) - end - - function PassiveDimensionalDataset{N,W,DOM}( - d::DOM, - ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset} - FR = typeof(_frame(d)) - _W = worldtype(FR) - @assert W <: _W "This should hold: $(W) <: $(_W)" - PassiveDimensionalDataset{N,_W,DOM,FR}(d) - end - - function PassiveDimensionalDataset{N,W}( - d::DOM, - ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset} - PassiveDimensionalDataset{N,W,DOM}(d) - end - - function PassiveDimensionalDataset( - d::AbstractDimensionalDataset, - # worldtype::Type{<:AbstractWorld}, - ) - W = worldtype(_frame(d)) - PassiveDimensionalDataset{dimensionality(d),W}(d) - end -end - -@inline function Base.getindex( - X::PassiveDimensionalDataset{N,W}, - i_instance::Integer, - w::W, - f::AbstractFeature{U}, - args..., -) where {N,W<:AbstractWorld,U} - w_values = interpret_world(w, get_instance(X.d, i_instance)) - computefeature(f, w_values)::U -end - -Base.size(X::PassiveDimensionalDataset) = Base.size(X.d) - -nvariables(X::PassiveDimensionalDataset) = nvariables(X.d) -ninstances(X::PassiveDimensionalDataset) = ninstances(X.d) -channel_size(X::PassiveDimensionalDataset) = channel_size(X.d) -max_channel_size(X::PassiveDimensionalDataset) = max_channel_size(X.d) -dimensionality(X::PassiveDimensionalDataset) = dimensionality(X.d) -eltype(X::PassiveDimensionalDataset) = eltype(X.d) - -get_instance(X::PassiveDimensionalDataset, args...) = get_instance(X.d, args...) - -instances(X::PassiveDimensionalDataset{N,W}, inds::AbstractVector{<:Integer}, args...; kwargs...) where {N,W} = - PassiveDimensionalDataset{N,W}(instances(X.d, inds, args...; kwargs...)) - -hasnans(X::PassiveDimensionalDataset) = hasnans(X.d) - -worldtype(X::PassiveDimensionalDataset{N,W}) where {N,W} = W - -frame(X::PassiveDimensionalDataset, i_instance::Integer) = _frame(X.d, i_instance) - -############################################################################################ - -_frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance::Integer) = _frame(X) -_frame(X::Union{UniformDimensionalDataset,AbstractArray}) = FullDimensionalFrame(channel_size(X)) diff --git a/src/logisets/dimensional-logisets/gamma-access.jl b/src/logisets/dimensional-logisets/gamma-access.jl deleted file mode 100644 index 78720d7..0000000 --- a/src/logisets/dimensional-logisets/gamma-access.jl +++ /dev/null @@ -1,53 +0,0 @@ -using SoleLogics: AbstractWorld, AbstractRelation -using SoleModels: AbstractFeature, Aggregator - -############################################################################################ - -@inline function onestep_accessible_aggregation( - X::PassiveDimensionalDataset{N,W}, - i_instance::Integer, - w::W, - r::AbstractRelation, - f::AbstractFeature{V}, - aggr::Aggregator, - args..., -) where {N,V,W<:AbstractWorld} - vs = [X[i_instance, w2, f] for w2 in representatives(X, i_instance, w, r, f, aggr)] - return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) -end - -@inline function onestep_accessible_aggregation( - X::PassiveDimensionalDataset{N,W}, - i_instance::Integer, - r::GlobalRel, - f::AbstractFeature{V}, - aggr::Aggregator, - args... -) where {N,V,W<:AbstractWorld} - vs = [X[i_instance, w2, f] for w2 in representatives(X, i_instance, r, f, aggr)] - return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) -end - -############################################################################################ - -@inline function onestep_accessible_aggregation( - X::DimensionalLogiset{VV,N,W}, - i_instance::Integer, - w::W, - r::AbstractRelation, - f::AbstractFeature{V}, - aggr::Aggregator, - args..., -) where {VV,N,V<:VV,W<:AbstractWorld} - onestep_accessible_aggregation(domain(X), i_instance, w, r, f, aggr, args...) -end -@inline function onestep_accessible_aggregation( - X::DimensionalLogiset{VV,N,W}, - i_instance::Integer, - r::GlobalRel, - f::AbstractFeature{V}, - aggr::Aggregator, - args..., -) where {VV,N,V<:VV,W<:AbstractWorld} - onestep_accessible_aggregation(domain(X), i_instance, r, f, aggr, args...) -end diff --git a/src/logisets/dimensional-logisets/main.jl b/src/logisets/dimensional-logisets/main.jl deleted file mode 100644 index f5b5d62..0000000 --- a/src/logisets/dimensional-logisets/main.jl +++ /dev/null @@ -1,35 +0,0 @@ -module DimensionalDatasets - -import SoleLogics: worldtype - -using SoleModels.utils - -# export parsecondition - -# # Conditions on features for dimensional datasets -# include("_parse-dimensional-condition.jl") - -# # Concrete type for ontologies -# include("ontology.jl") # TODO frame inside the ontology? - -# export DimensionalLogiset, Logiset, SupportedScalarLogiset - -# # Dataset structures -# include("datasets/main.jl") - -# const GenericModalDataset = Union{AbstractDimensionalDataset,AbstractLogiset,MultiLogiset} - -# # Dimensional ontologies -# include("dimensional-ontologies.jl") - -using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame -using SoleLogics: X, Y, Z - -# Representatives for dimensional frames -include("representatives/Full0DFrame.jl") -include("representatives/Full1DFrame.jl") -include("representatives/Full1DFrame+IA.jl") -include("representatives/Full1DFrame+RCC.jl") -include("representatives/Full2DFrame.jl") - -end diff --git a/src/logisets/dimensional-logisets/scalar-gamma-access.jl b/src/logisets/dimensional-logisets/scalar-gamma-access.jl deleted file mode 100644 index 5c00339..0000000 --- a/src/logisets/dimensional-logisets/scalar-gamma-access.jl +++ /dev/null @@ -1,108 +0,0 @@ - -############################################################################################ - -@inline function onestep_accessible_aggregation(X::Logiset{VV,W}, i_instance::Integer, w::W, r::AbstractRelation, f::AbstractFeature{V}, aggr::Aggregator, args...) where {VV,V<:VV,W<:AbstractWorld} - vs = [X[i_instance, w2, f] for w2 in representatives(X, i_instance, w, r, f, aggr)] - return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) -end - -@inline function onestep_accessible_aggregation(X::Logiset{VV,W}, i_instance::Integer, r::GlobalRel, f::AbstractFeature{V}, aggr::Aggregator, args...) where {VV,V<:VV,W<:AbstractWorld} - vs = [X[i_instance, w2, f] for w2 in representatives(X, i_instance, r, f, aggr)] - return (length(vs) == 0 ? aggregator_bottom(aggr, V) : aggr(vs)) -end - -############################################################################################ - -function onestep_accessible_aggregation( - X::SupportedScalarLogiset, - i_instance::Integer, - w::W, - relation::AbstractRelation, - feature::AbstractFeature{V}, - aggr::Aggregator, - i_featsnaggr::Union{Nothing,Integer} = nothing, - i_relation::Integer = findrelation(X, relation), -) where {V,W<:AbstractWorld} - compute_modal_gamma(support(X), fd(X), i_instance, w, relation, feature, aggregator, i_featsnaggr, i_relation) -end - -@inline function onestep_accessible_aggregation( - X::SupportedScalarLogiset, - i_instance::Integer, - r::GlobalRel, - f::AbstractFeature{V}, - aggr::Aggregator, - args... -) where {V,W<:AbstractWorld} - compute_global_gamma(support(X), fd(X), i_instance, f, aggr, args...) -end - -############################################################################################ - -function fwdslice_onestep_accessible_aggregation(fd::Logiset, fwdslice::FWDFeatureSlice, i_instance, r::GlobalRel, f, aggr, args...) - # accessible_worlds = allworlds(fd, i_instance) - accessible_worlds = representatives(fd, i_instance, r, f, aggr) - gamma = apply_aggregator(fwdslice, accessible_worlds, aggr) -end - -function fwdslice_onestep_accessible_aggregation(fd::Logiset, fwdslice::FWDFeatureSlice, i_instance, w, r::AbstractRelation, f, aggr, args...) - # accessible_worlds = accessibles(fd, i_instance, w, r) - accessible_worlds = representatives(fd, i_instance, w, r, f, aggr) - gamma = apply_aggregator(fwdslice, accessible_worlds, aggr) -end - -# TODO remove -# function fwdslice_onestep_accessible_aggregation(fd::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_instance, args...) -# fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_instance, args...) -# end - - -function fwdslice_onestep_accessible_aggregation(X::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_instance, r::GlobalRel, f, aggr, args...) - fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_instance, r, f, aggr, args...) -end - -function fwdslice_onestep_accessible_aggregation(X::SupportedScalarLogiset, fwdslice::FWDFeatureSlice, i_instance, w, r::AbstractRelation, f, aggr, args...) - fwdslice_onestep_accessible_aggregation(support(X), fd(X), fwdslice, i_instance, w, r, f, aggr, args...) -end - -############################################################################################ - -function fwdslice_onestep_accessible_aggregation( - X::ScalarOneStepMemoset{V,W}, - fd::Logiset{V,W}, - fwdslice::FWDFeatureSlice, - i_instance::Integer, - r::GlobalRel, - feature::AbstractFeature, - aggr::Aggregator, - i_featsnaggr::Integer = find_featsnaggr_id(X, feature, aggr), -) where {V,W<:AbstractWorld} - _fwd_gs = fwd_gs(X) - if isnothing(_fwd_gs[i_instance, i_featsnaggr]) - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, r, feature, aggr) - _fwd_gs[i_instance, i_featsnaggr] = gamma - end - _fwd_gs[i_instance, i_featsnaggr] -end - -function fwdslice_onestep_accessible_aggregation( - X::ScalarOneStepMemoset{V,W}, - fd::Logiset{V,W}, - fwdslice::FWDFeatureSlice, - i_instance::Integer, - w::W, - r::AbstractRelation, - feature::AbstractFeature, - aggr::Aggregator, - i_featsnaggr = find_featsnaggr_id(X, feature, aggr), - i_relation = nothing, # TODO fix -)::V where {V,W<:AbstractWorld} - _fwd_rs = fwd_rs(X) - if isnothing(_fwd_rs[i_instance, w, i_featsnaggr, i_relation]) - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, w, r, feature, aggr) - _fwd_rs[i_instance, w, i_featsnaggr, i_relation] = gamma - end - _fwd_rs[i_instance, w, i_featsnaggr, i_relation] -end - -############################################################################################ diff --git a/src/logisets/base/features.jl b/src/logisets/features.jl similarity index 93% rename from src/logisets/base/features.jl rename to src/logisets/features.jl index 95693f4..a2d468f 100644 --- a/src/logisets/base/features.jl +++ b/src/logisets/features.jl @@ -26,11 +26,11 @@ Base.isequal(a::AbstractFeature, b::AbstractFeature) = syntaxstring(a) == syntax Base.hash(a::AbstractFeature) = Base.hash(syntaxstring(a)) function parsefeature( - F::Type{<:AbstractFeature}, + FT::Type{<:AbstractFeature}, expression::String; kwargs... ) - error("Please, provide method parsefeature(::$(F), expression::$(typeof(expression)); kwargs...).") + error("Please, provide method parsefeature(::$(FT), expression::$(typeof(expression)); kwargs...).") end ############################################################################################ diff --git a/src/logisets/base/logiset.jl b/src/logisets/logiset.jl similarity index 81% rename from src/logisets/base/logiset.jl rename to src/logisets/logiset.jl index ae0db3e..9735be5 100644 --- a/src/logisets/base/logiset.jl +++ b/src/logisets/logiset.jl @@ -8,9 +8,9 @@ import SoleLogics: worldtype, frametype abstract type AbstractLogiset{ W<:AbstractWorld, U, - F<:AbstractFeature, + FT<:AbstractFeature, FR<:AbstractFrame{W}, - } <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:F},T where T<:TruthValue,FR}} end + } <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:FT},T where T<:TruthValue,FR}} end Abstract type for logisets, that is, logical datasets for symbolic learning where each instance is a @@ -28,17 +28,37 @@ See also abstract type AbstractLogiset{ W<:AbstractWorld, U, - F<:AbstractFeature, + FT<:AbstractFeature, FR<:AbstractFrame{W}, -} <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:F},T where T<:TruthValue,FR}} end +} <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:FT},T where T<:TruthValue,FR}} end +function featchannel( + X::AbstractLogiset{W}, + i_instance::Integer, + f::AbstractFeature, +) where {W<:AbstractWorld} + error("Please, provide method featchannel(::$(typeof(X)), i_instance::$(typeof(i_instance)), f::$(typeof(f))).") +end + +function readfeature( + X::AbstractLogiset{W}, + featchannel::Any, + w::W, + f::AbstractFeature, +) where {W<:AbstractWorld} + error("Please, provide method readfeature(::$(typeof(X)), featchannel::$(typeof(featchannel)), w::$(typeof(w)), f::$(typeof(f))).") +end + +""" +TODO +""" function featvalue( X::AbstractLogiset{W}, i_instance::Integer, w::W, f::AbstractFeature, ) where {W<:AbstractWorld} - error("Please, provide method featvalue(::$(typeof(X)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), f::$(typeof(f))).") + readfeature(X, featchannel(X, i_instance, f), w, f) end function frame(X::AbstractLogiset, i_instance::Integer) @@ -122,10 +142,10 @@ worldtype(X::AbstractLogiset) = worldtype(typeof(X)) featvaltype(::Type{<:AbstractLogiset{W,U}}) where {W<:AbstractWorld,U} = U featvaltype(X::AbstractLogiset) = featvaltype(typeof(X)) -featuretype(::Type{<:AbstractLogiset{W,U,F}}) where {W<:AbstractWorld,U,F<:AbstractFeature} = F +featuretype(::Type{<:AbstractLogiset{W,U,FT}}) where {W<:AbstractWorld,U,FT<:AbstractFeature} = FT featuretype(X::AbstractLogiset) = featuretype(typeof(X)) -frametype(::Type{<:AbstractLogiset{W,U,F,FR}}) where {W<:AbstractWorld,U,F<:AbstractFeature,FR<:AbstractFrame} = FR +frametype(::Type{<:AbstractLogiset{W,U,FT,FR}}) where {W<:AbstractWorld,U,FT<:AbstractFeature,FR<:AbstractFrame} = FR frametype(X::AbstractLogiset) = frametype(typeof(X)) representatives(X::AbstractLogiset, i_instance::Integer, args...) = representatives(frame(X, i_instance), args...) @@ -137,9 +157,9 @@ representatives(X::AbstractLogiset, i_instance::Integer, args...) = representati # abstract type AbstractBaseLogiset{ # W<:AbstractWorld, # U, -# F<:AbstractFeature, +# FT<:AbstractFeature, # FR<:AbstractFrame{W}, -# } <: AbstractLogiset{W,U,F,FR} end +# } <: AbstractLogiset{W,U,FT,FR} end # (Base) logisets can be associated to support logisets that perform memoization in order # to speed up model checking times. @@ -151,19 +171,19 @@ representatives(X::AbstractLogiset, i_instance::Integer, args...) = representati # abstract type AbstractBaseLogiset{ # W<:AbstractWorld, # U, -# F<:AbstractFeature, +# FT<:AbstractFeature, # FR<:AbstractFrame{W}, -# } <: AbstractLogiset{W,U,F,FR} end +# } <: AbstractLogiset{W,U,FT,FR} end """ struct ExplicitBooleanLogiset{ W<:AbstractWorld, - F<:AbstractFeature, + FT<:AbstractFeature, FR<:AbstractFrame{W}, - } <: AbstractLogiset{W,Bool,F,FR} + } <: AbstractLogiset{W,Bool,FT,FR} - d :: Vector{Tuple{Dict{W,Vector{F}},FR}} + d :: Vector{Tuple{Dict{W,Vector{FT}},FR}} end @@ -175,10 +195,10 @@ See also """ struct ExplicitBooleanLogiset{ W<:AbstractWorld, - F<:AbstractFeature, + FT<:AbstractFeature, FR<:AbstractFrame{W}, - D<:AbstractVector{<:Tuple{<:Dict{<:W,<:Vector{<:F}},<:FR}} -} <: AbstractLogiset{W,Bool,F,FR} + D<:AbstractVector{<:Tuple{<:Dict{<:W,<:Vector{<:FT}},<:FR}} +} <: AbstractLogiset{W,Bool,FT,FR} d :: D @@ -186,15 +206,23 @@ end ninstances(X::ExplicitBooleanLogiset) = length(X.d) -function featvalue( + +function featchannel( X::ExplicitBooleanLogiset{W}, i_instance::Integer, - w::W, f::AbstractFeature, ) where {W<:AbstractWorld} - Base.in(f, X.d[i_instance][1][w]) + X.d[i_instance][1] end +function readfeature( + X::ExplicitBooleanLogiset{W}, + featchannel::Any, + w::W, + f::AbstractFeature, +) where {W<:AbstractWorld} + Base.in(f, featchannel[w]) +end function frame( X::ExplicitBooleanLogiset{W}, @@ -258,11 +286,11 @@ hasnans(X::ExplicitBooleanLogiset) = false struct ExplicitLogiset{ W<:AbstractWorld, U, - F<:AbstractFeature, + FT<:AbstractFeature, FR<:AbstractFrame{W}, - } <: AbstractLogiset{W,U,F,FR} + } <: AbstractLogiset{W,U,FT,FR} - d :: Vector{Tuple{Dict{W,Dict{F,U}},FR}} + d :: Vector{Tuple{Dict{W,Dict{FT,U}},FR}} end @@ -275,10 +303,10 @@ See also struct ExplicitLogiset{ W<:AbstractWorld, U, - F<:AbstractFeature, + FT<:AbstractFeature, FR<:AbstractFrame{W}, - D<:AbstractVector{<:Tuple{<:Dict{<:W,<:Dict{<:F,<:U}},<:FR}} -} <: AbstractLogiset{W,U,F,FR} + D<:AbstractVector{<:Tuple{<:Dict{<:W,<:Dict{<:FT,<:U}},<:FR}} +} <: AbstractLogiset{W,U,FT,FR} d :: D @@ -286,15 +314,22 @@ end ninstances(X::ExplicitLogiset) = length(X.d) -function featvalue( +function featchannel( X::ExplicitLogiset{W}, i_instance::Integer, - w::W, f::AbstractFeature, ) where {W<:AbstractWorld} - X.d[i_instance][1][w][f] + X.d[i_instance][1] end +function readfeature( + X::ExplicitLogiset{W}, + featchannel::Any, + w::W, + f::AbstractFeature, +) where {W<:AbstractWorld} + featchannel[w][f] +end function frame( X::ExplicitLogiset{W}, diff --git a/src/logisets/base/main.jl b/src/logisets/main.jl similarity index 85% rename from src/logisets/base/main.jl rename to src/logisets/main.jl index 4105cce..83210ca 100644 --- a/src/logisets/base/main.jl +++ b/src/logisets/main.jl @@ -43,12 +43,13 @@ include("check.jl") include("scalar/main.jl") function default_relmemoset_type(X::AbstractLogiset) - frames = [frame(X, i_instance) for i_instance in 1:ninstances(X)] - if allequal(frames) && first(unique(frames)) isa FullDimensionalFrame - UniformFullDimensionalRelationalSupport - else + # frames = [frame(X, i_instance) for i_instance in 1:ninstances(X)] + # if allequal(frames) && first(unique(frames)) isa FullDimensionalFrame + # if X isa UniformFullDimensionalFWD TODO + # UniformFullDimensionalRelationalSupport + # else ScalarOneStepRelationalMemoset - end + # end end function default_onestep_memoset_type(X::AbstractLogiset) @@ -60,9 +61,9 @@ function default_onestep_memoset_type(X::AbstractLogiset) end function default_full_memoset_type(X::AbstractLogiset) # if ... - # ScalarMemoset TODO + # ScalarChainedMemoset TODO # else - Memoset + FullMemoset # end end diff --git a/src/logisets/base/memosets.jl b/src/logisets/memosets.jl similarity index 65% rename from src/logisets/base/memosets.jl rename to src/logisets/memosets.jl index 0fede69..c75ff95 100644 --- a/src/logisets/base/memosets.jl +++ b/src/logisets/memosets.jl @@ -2,25 +2,25 @@ abstract type AbstractMemoset{ W<:AbstractWorld, U, - F<:AbstractFeature, + FT<:AbstractFeature, FR<:AbstractFrame, - } <: AbstractLogiset{W,U,F,FR} end + } <: AbstractLogiset{W,U,FT,FR} end Abstract type for memoization structures to be used when checking formulas on logisets. See also -[`Memoset`](@ref), +[`FullMemoset`](@ref), [`SuportedLogiset`](@ref), [`AbstractLogiset`](@ref). """ abstract type AbstractMemoset{ W<:AbstractWorld, U, - F<:AbstractFeature, + FT<:AbstractFeature, FR<:AbstractFrame, -} <: AbstractLogiset{W,U,F,FR} end -# } <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:F},T where T<:TruthValue,FR where FR<:AbstractFrame}} end +} <: AbstractLogiset{W,U,FT,FR} end +# } <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:FT},T where T<:TruthValue,FR where FR<:AbstractFrame}} end function capacity(Xm::AbstractMemoset) error("Please, provide method capacity(::$(typeof(Xm))).") @@ -34,6 +34,14 @@ function nonnothingshare(Xm::AbstractMemoset) (isinf(capacity(Xm)) ? NaN : nmemoizedvalues(Xm)/capacity(Xm)) end +function memoizationinfo(Xm::AbstractMemoset) + if isinf(capacity(Xm)) + "$(nmemoizedvalues(Xm)) memoized values" + else + "$(nmemoizedvalues(Xm))/$(capacity(Xm)) = $(round(nonnothingshare(Xm)*100, digits=2))% memoized values" + end +end + function displaystructure(Xm::AbstractMemoset; indent_str = "", include_ninstances = true) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) pieces = [] @@ -47,16 +55,21 @@ function displaystructure(Xm::AbstractMemoset; indent_str = "", include_ninstanc end push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") - return "Memoset ($(humansize(Xm)))" * + return "FullMemoset ($(humansize(Xm)))" * join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" end ############################################################################################ """ -Abstract type for one-step memoization structure for checking formulas of type `⟨R⟩ (f ⋈ t)`. +Abstract type for one-step memoization structures for checking formulas of type `⟨R⟩ p`. +""" +abstract type AbstractOneStepMemoset{W<:AbstractWorld,U,FT<:AbstractFeature,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,FT,FR} end + +""" +Abstract type for full memoization structures for checking generic formulas. """ -abstract type AbstractOneStepMemoset{W<:AbstractWorld,U,F<:AbstractFeature,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,F,FR} end +abstract type AbstractFullMemoset{W<:AbstractWorld,U,FT<:AbstractFeature,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,FT,FR} end ############################################################################################ @@ -75,42 +88,42 @@ See also [`AbstractMemoset`](@ref), [`AbstractLogiset`](@ref). """ -struct Memoset{ +struct FullMemoset{ W<:AbstractWorld, D<:AbstractVector{<:AbstractDict{<:AbstractFormula,<:WorldSet{W}}}, -} <: AbstractMemoset{W,U where U,F where F<:AbstractFeature,FR where FR<:AbstractFrame{W}} +} <: AbstractFullMemoset{W,U where U,FT where FT<:AbstractFeature,FR where FR<:AbstractFrame{W}} d :: D - function Memoset{W,D}( + function FullMemoset{W,D}( d::D ) where {W,D<:AbstractVector{<:AbstractDict{<:AbstractFormula,<:Union{<:AbstractVector{W},Nothing}}}} new{W,D}(d) end - function Memoset( + function FullMemoset( d::D ) where {W,D<:AbstractVector{<:AbstractDict{<:AbstractFormula,<:Union{<:AbstractVector{W},Nothing}}}} new{W,D}(d) end - function Memoset( - X::AbstractLogiset{W,U,F,FR}, + function FullMemoset( + X::AbstractLogiset{W,U,FT,FR}, perform_initialization = false, - ) where {W,U,F<:AbstractFeature,FR<:AbstractFrame{W}} + ) where {W,U,FT<:AbstractFeature,FR<:AbstractFrame{W}} d = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i in 1:ninstances(X)] D = typeof(d) - Memoset{W,D}(d) + FullMemoset{W,D}(d) end end -ninstances(Xm::Memoset) = length(Xm.d) +ninstances(Xm::FullMemoset) = length(Xm.d) -capacity(Xm::Memoset) = Inf -nmemoizedvalues(Xm::Memoset) = sum(length.(Xm.d)) +capacity(Xm::FullMemoset) = Inf +nmemoizedvalues(Xm::FullMemoset) = sum(length.(Xm.d)) @inline function Base.haskey( - Xm :: Memoset{W}, + Xm :: FullMemoset{W}, i_instance :: Integer, f :: AbstractFormula, ) where {W<:AbstractWorld} @@ -118,20 +131,20 @@ nmemoizedvalues(Xm::Memoset) = sum(length.(Xm.d)) end @inline function Base.getindex( - Xm :: Memoset{W}, + Xm :: FullMemoset{W}, i_instance :: Integer, ) where {W<:AbstractWorld} Xm.d[i_instance] end @inline function Base.getindex( - Xm :: Memoset{W}, + Xm :: FullMemoset{W}, i_instance :: Integer, f :: AbstractFormula, ) where {W<:AbstractWorld} Xm.d[i_instance][f] end @inline function Base.setindex!( - Xm :: Memoset{W}, + Xm :: FullMemoset{W}, i_instance :: Integer, f :: AbstractFormula, ws :: WorldSet{W}, @@ -141,7 +154,7 @@ end function check( f::AbstractFormula, - Xm::Memoset{W}, + Xm::FullMemoset{W}, i_instance::Integer, w::W; kwargs... @@ -150,21 +163,21 @@ function check( end function instances( - Xm::Memoset, + Xm::FullMemoset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false); kwargs... ) - Memoset(if return_view == Val(true) @view Xm.d[inds] else Xm.d[inds] end) + FullMemoset(if return_view == Val(true) @view Xm.d[inds] else Xm.d[inds] end) end -function concatdatasets(Xms::Memoset...) - Memoset(vcat([Xm.d for Xm in Xms]...)) +function concatdatasets(Xms::FullMemoset...) + FullMemoset(vcat([Xm.d for Xm in Xms]...)) end -usesfullmemo(::Memoset) = true -fullmemo(Xm::Memoset) = Xm +usesfullmemo(::FullMemoset) = true +fullmemo(Xm::FullMemoset) = Xm -hasnans(::Memoset) = false +hasnans(::FullMemoset) = false -# Base.size(::Memoset) = () +# Base.size(::FullMemoset) = () diff --git a/src/logisets/base/multilogiset.jl b/src/logisets/multilogiset.jl similarity index 100% rename from src/logisets/base/multilogiset.jl rename to src/logisets/multilogiset.jl diff --git a/src/logisets/base/representatives.jl b/src/logisets/representatives.jl similarity index 87% rename from src/logisets/base/representatives.jl rename to src/logisets/representatives.jl index 08507b3..8bc02e1 100644 --- a/src/logisets/base/representatives.jl +++ b/src/logisets/representatives.jl @@ -1,8 +1,10 @@ using SoleLogics: AbstractFrame, AbstractMultiModalFrame, AbstractRelation, accessibles +# TODO: AbstractFrame -> AbstractMultiModalFrame, and provide the same for AbstractUniModalFrame + """ function representatives( - fr::AbstractMultiModalFrame{W}, + fr::AbstractFrame{W}, S::W, ::AbstractRelation, ::AbstractCondition @@ -25,10 +27,10 @@ Note that this method fallsback to `accessibles`. See also [`accessibles`](@ref), [`ScalarCondition`](@ref), -[`AbstractMultiModalFrame`](@ref). +[`AbstractFrame`](@ref). """ function representatives( # Dispatch on feature/aggregator pairs - fr::AbstractMultiModalFrame{W}, + fr::AbstractFrame{W}, w::W, r::AbstractRelation, ::AbstractCondition diff --git a/src/logisets/base/scalar/canonical-conditions.jl b/src/logisets/scalar/canonical-conditions.jl similarity index 100% rename from src/logisets/base/scalar/canonical-conditions.jl rename to src/logisets/scalar/canonical-conditions.jl diff --git a/src/logisets/base/scalar/conditions.jl b/src/logisets/scalar/conditions.jl similarity index 87% rename from src/logisets/base/scalar/conditions.jl rename to src/logisets/scalar/conditions.jl index 69d683d..f100af5 100644 --- a/src/logisets/base/scalar/conditions.jl +++ b/src/logisets/scalar/conditions.jl @@ -4,8 +4,8 @@ const DEFAULT_SCALARCOND_FEATTYPE = SoleModels.VarFeature """ - struct ScalarMetaCondition{F<:AbstractFeature,O<:TestOperator} <: AbstractCondition{F} - feature::F + struct ScalarMetaCondition{FT<:AbstractFeature,O<:TestOperator} <: AbstractCondition{FT} + feature::FT test_operator::O end @@ -22,10 +22,10 @@ See also [`negation`](@ref), [`ScalarCondition`](@ref). """ -struct ScalarMetaCondition{F<:AbstractFeature,O<:TestOperator} <: AbstractCondition{F} +struct ScalarMetaCondition{FT<:AbstractFeature,O<:TestOperator} <: AbstractCondition{FT} # Feature: a scalar function that can be computed on a world - feature::F + feature::FT # Test operator (e.g. ≥) test_operator::O @@ -33,8 +33,8 @@ struct ScalarMetaCondition{F<:AbstractFeature,O<:TestOperator} <: AbstractCondit end # TODO -# featuretype(::Type{<:ScalarMetaCondition{F}}) where {F<:AbstractFeature} = F -# featuretype(m::ScalarMetaCondition) = featuretype(typeof(F)) +# featuretype(::Type{<:ScalarMetaCondition{FT}}) where {FT<:AbstractFeature} = FT +# featuretype(m::ScalarMetaCondition) = featuretype(typeof(FT)) feature(m::ScalarMetaCondition) = m.feature test_operator(m::ScalarMetaCondition) = m.test_operator @@ -64,8 +64,20 @@ _st_featop_abbr(feature::AbstractFeature, test_operator::TestOperator; kwargs. ############################################################################################ +function groupbyfeature(metaconditions::AbstractVector{<:ScalarMetaCondition}) + _features = unique(feature.(metaconditions)) + grouped_features = [begin + these_metaconds = filter(m->feature(m) == _feature, metaconditions) + # these_testops = unique(test_operator.(these_metaconds)) + (_feature, these_metaconds) + end for _feature in _features] + grouped_features +end + +############################################################################################ + """ - struct ScalarCondition{U,F,M<:ScalarMetaCondition{F}} <: AbstractCondition{F} + struct ScalarCondition{U,FT,M<:ScalarMetaCondition{FT}} <: AbstractCondition{FT} metacond::M a::U end @@ -83,7 +95,7 @@ See also [`negation`](@ref), [`ScalarMetaCondition`](@ref). """ -struct ScalarCondition{U,F,M<:ScalarMetaCondition{F}} <: AbstractCondition{F} +struct ScalarCondition{U,FT,M<:ScalarMetaCondition{FT}} <: AbstractCondition{FT} # Metacondition metacond::M @@ -94,15 +106,15 @@ struct ScalarCondition{U,F,M<:ScalarMetaCondition{F}} <: AbstractCondition{F} function ScalarCondition( metacond :: M, threshold :: U - ) where {F<:AbstractFeature,M<:ScalarMetaCondition{F},U} - new{U,F,M}(metacond, threshold) + ) where {FT<:AbstractFeature,M<:ScalarMetaCondition{FT},U} + new{U,FT,M}(metacond, threshold) end function ScalarCondition( condition :: ScalarCondition{U,M}, threshold :: U - ) where {F<:AbstractFeature,M<:ScalarMetaCondition{F},U} - new{U,F,M}(metacond(condition), threshold) + ) where {FT<:AbstractFeature,M<:ScalarMetaCondition{FT},U} + new{U,FT,M}(metacond(condition), threshold) end function ScalarCondition( @@ -172,8 +184,8 @@ function parsecondition( expression::String; featuretype::Union{Nothing,Type} = nothing, kwargs... -) where {U,F,C<:ScalarCondition{U,F}} - @assert isnothing(featuretype) || featuretype == F "Cannot parse condition of type $(C) with " * +) where {U,FT,C<:ScalarCondition{U,FT}} + @assert isnothing(featuretype) || featuretype == FT "Cannot parse condition of type $(C) with " * "featuretype = $(featuretype). (expression = $(repr(expression)))" _parsecondition(C, expression; kwargs...) end @@ -182,7 +194,7 @@ function _parsecondition( ::Type{C}, expression::String; kwargs... -) where {U,F,C<:ScalarCondition{U,F}} +) where {U,FT,C<:ScalarCondition{U,FT}} r = Regex("^\\s*(\\S+)\\s+([^\\s\\d]+)\\s*(\\S+)\\s*\$") slices = match(r, expression) @@ -191,7 +203,7 @@ function _parsecondition( slices = string.(slices) - feature = parsefeature(F, slices[1]; featvaltype = U, kwargs...) + feature = parsefeature(FT, slices[1]; featvaltype = U, kwargs...) test_operator = eval(Meta.parse(slices[2])) threshold = eval(Meta.parse(slices[3])) diff --git a/src/logisets/base/scalar/main.jl b/src/logisets/scalar/main.jl similarity index 100% rename from src/logisets/base/scalar/main.jl rename to src/logisets/scalar/main.jl diff --git a/src/logisets/base/scalar/memosets.jl b/src/logisets/scalar/memosets.jl similarity index 59% rename from src/logisets/base/scalar/memosets.jl rename to src/logisets/scalar/memosets.jl index caa8cc4..1a55e3c 100644 --- a/src/logisets/base/scalar/memosets.jl +++ b/src/logisets/scalar/memosets.jl @@ -2,49 +2,49 @@ using UniqueVectors """ A full memoization structure used for checking formulas of scalar conditions on -datasets with scalar features. This structure is the equivalent to [`Memoset`](@ref), +datasets with scalar features. This structure is the equivalent to [`FullMemoset`](@ref), but with scalar features some important optimizations can be done. TODO explain See also -[`Memoset`](@ref), +[`FullMemoset`](@ref), [`SuportedLogiset`](@ref), [`AbstractLogiset`](@ref). """ -struct ScalarMemoset{ +struct ScalarChainedMemoset{ W<:AbstractWorld, U, FR<:AbstractFrame{W}, D<:AbstractVector{<:AbstractDict{<:AbstractFormula,U}}, -} <: AbstractMemoset{W,U,F where F<:AbstractFeature,FR} +} <: AbstractFullMemoset{W,U,FT where FT<:AbstractFeature,FR} d :: D - function ScalarMemoset{W,U,FR,D}( + function ScalarChainedMemoset{W,U,FR,D}( d::D ) where {W<:AbstractWorld,U,FR<:AbstractFrame{W},D<:AbstractVector{<:AbstractDict{<:AbstractFormula,U}}} new{W,U,FR,D}(d) end - function ScalarMemoset( - X::AbstractLogiset{W,U,F,FR}, + function ScalarChainedMemoset( + X::AbstractLogiset{W,U,FT,FR}, # perform_initialization = false, - ) where {W<:AbstractWorld,U,F<:AbstractFeature,FR<:AbstractFrame{W}} + ) where {W<:AbstractWorld,U,FT<:AbstractFeature,FR<:AbstractFrame{W}} d = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i in 1:ninstances(X)] D = typeof(d) - ScalarMemoset{W,U,FR,D}(d) + ScalarChainedMemoset{W,U,FR,D}(d) end end -ninstances(Xm::ScalarMemoset) = length(Xm.d) +ninstances(Xm::ScalarChainedMemoset) = length(Xm.d) -capacity(Xm::ScalarMemoset) = Inf -nmemoizedvalues(Xm::ScalarMemoset) = sum(length.(Xm.d)) +capacity(Xm::ScalarChainedMemoset) = Inf +nmemoizedvalues(Xm::ScalarChainedMemoset) = sum(length.(Xm.d)) @inline function Base.haskey( - Xm :: ScalarMemoset, + Xm :: ScalarChainedMemoset, i_instance :: Integer, f :: AbstractFormula, ) @@ -52,20 +52,20 @@ nmemoizedvalues(Xm::ScalarMemoset) = sum(length.(Xm.d)) end @inline function Base.getindex( - Xm :: ScalarMemoset, + Xm :: ScalarChainedMemoset, i_instance :: Integer, ) Xm.d[i_instance] end @inline function Base.getindex( - Xm :: ScalarMemoset, + Xm :: ScalarChainedMemoset, i_instance :: Integer, f :: AbstractFormula, ) Xm.d[i_instance][f] end @inline function Base.setindex!( - Xm :: ScalarMemoset, + Xm :: ScalarChainedMemoset, i_instance :: Integer, f :: AbstractFormula, threshold :: U, @@ -75,7 +75,7 @@ end function check( f::AbstractFormula, - Xm::ScalarMemoset{W}, + Xm::ScalarChainedMemoset{W}, i_instance::Integer, w::W; kwargs... @@ -84,19 +84,19 @@ function check( end function instances( - Xm::ScalarMemoset, + Xm::ScalarChainedMemoset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false); kwargs... ) - ScalarMemoset(if return_view == Val(true) @view Xm.d[inds] else Xm.d[inds] end) + ScalarChainedMemoset(if return_view == Val(true) @view Xm.d[inds] else Xm.d[inds] end) end -function concatdatasets(Xms::ScalarMemoset...) - ScalarMemoset(vcat([Xm.d for Xm in Xms]...)) +function concatdatasets(Xms::ScalarChainedMemoset...) + ScalarChainedMemoset(vcat([Xm.d for Xm in Xms]...)) end -usesfullmemo(::ScalarMemoset) = true -fullmemo(Xm::ScalarMemoset) = Xm +usesfullmemo(::ScalarChainedMemoset) = true +fullmemo(Xm::ScalarChainedMemoset) = Xm -hasnans(::ScalarMemoset) = false +hasnans(::ScalarChainedMemoset) = false diff --git a/src/logisets/base/scalar/one-step-memoset.jl b/src/logisets/scalar/one-step-memoset.jl similarity index 55% rename from src/logisets/base/scalar/one-step-memoset.jl rename to src/logisets/scalar/one-step-memoset.jl index 19000b0..c4094a0 100644 --- a/src/logisets/base/scalar/one-step-memoset.jl +++ b/src/logisets/scalar/one-step-memoset.jl @@ -1,9 +1,91 @@ +@inline function onestep_aggregation( + X::AbstractLogiset{W}, + i_instance::Integer, + w::W, + r::AbstractRelation, + f::VarFeature{U}, + aggr::Aggregator, + args... +) where {W<:AbstractWorld,U} + vs = [featvalue(X, i_instance, w2, f) for w2 in representatives(X, i_instance, w, r, f, aggr)] + return (length(vs) == 0 ? aggregator_bottom(aggr, U) : aggr(vs)) +end + +function featchannel_onestep_aggregation(X::SupportedLogiset, args...) + onestep_supps = filter(supp->supp isa AbstractOneStepMemoset, supports(X)) + if length(onestep_supps) > 0 + @assert length(onestep_supps) == 1 "Currently, using more " * + "than one AbstractOneStepMemoset is not allowed." + featchannel_onestep_aggregation(base(X), onestep_supps(X)[1], args...) + else + featchannel_onestep_aggregation(base(X), args...) + end +end + +function featchannel_onestep_aggregation( + X::AbstractLogiset, + featchannel, + i_instance, + r::GlobalRel, + f::AbstractFeature, + aggregator::Aggregator +) + # accessible_worlds = allworlds(X, i_instance) + accessible_worlds = representatives(X, i_instance, r, f, aggregator) + gamma = apply_aggregator(X, featchannel, accessible_worlds, f, aggregator) +end + +function featchannel_onestep_aggregation( + X::AbstractLogiset, + featchannel, + i_instance, + w, + r::AbstractRelation, + f::AbstractFeature, + aggregator::Aggregator +) + # accessible_worlds = accessibles(X, i_instance, w, r) + accessible_worlds = representatives(X, i_instance, w, r, f, aggregator) + gamma = apply_aggregator(X, featchannel, accessible_worlds, f, aggregator) +end + +# TODO add AbstractWorldSet type +function apply_aggregator( + X::AbstractLogiset{W,U}, + featchannel, + worlds::Any, + f::AbstractFeature, + aggregator::Aggregator +) where {W<:AbstractWorld,U} + + # TODO try reduce(aggregator, worlds; init=bottom(aggregator, U)) + # TODO remove this SoleModels.aggregator_to_binary... + + if length(worlds |> collect) == 0 + aggregator_bottom(aggregator, U) + else + aggregator((w)->readfeature(X, featchannel, w, f), worlds) + end + + # opt = SoleModels.aggregator_to_binary(aggregator) + # gamma = bottom(aggregator, U) + # for w in worlds + # e = readfeature(X, featchannel, w, f) + # gamma = opt(gamma,e) + # end + # gamma +end + +############################################################################################ +############################################################################################ +############################################################################################ + """ -Abstract type for one-step memoization structure for checking formulas of type `⟨R⟩ (f ⋈ t)`, +Abstract type for one-step memoization structures for checking formulas of type `⟨R⟩ (f ⋈ t)`, for a generic relation `R`. """ -abstract type AbstractScalarOneStepRelationalMemoset{W<:AbstractWorld,U,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,F where F<:AbstractFeature,FR} end +abstract type AbstractScalarOneStepRelationalMemoset{W<:AbstractWorld,U,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,FT where FT<:AbstractFeature,FR} end @inline function Base.getindex( memoset :: AbstractScalarOneStepRelationalMemoset{W}, @@ -25,7 +107,7 @@ end Abstract type for one-step memoization structure for checking "global" formulas of type `⟨G⟩ (f ⋈ t)`. """ -abstract type AbstractScalarOneStepGlobalMemoset{W<:AbstractWorld,U} <: AbstractMemoset{W,U,F where F<:AbstractFeature,FR where FR<:AbstractFrame{W}} end +abstract type AbstractScalarOneStepGlobalMemoset{W<:AbstractWorld,U} <: AbstractMemoset{W,U,FT where FT<:AbstractFeature,FR where FR<:AbstractFrame{W}} end @inline function Base.getindex( memoset :: AbstractScalarOneStepGlobalMemoset{W}, @@ -62,7 +144,7 @@ struct ScalarOneStepMemoset{ UU<:Union{U,Nothing}, RM<:AbstractScalarOneStepRelationalMemoset{W,UU,FR}, GM<:Union{AbstractScalarOneStepGlobalMemoset{W,U},Nothing}, -} <: AbstractOneStepMemoset{W,U,F where F<:AbstractFeature,FR} +} <: AbstractOneStepMemoset{W,U,FT where FT<:AbstractFeature,FR} metaconditions :: UniqueVector{<:ScalarMetaCondition} relations :: UniqueVector{<:AbstractRelation} @@ -86,9 +168,14 @@ struct ScalarOneStepMemoset{ RM<:AbstractScalarOneStepRelationalMemoset{W,UU,FR}, GM<:Union{AbstractScalarOneStepGlobalMemoset{W,U},Nothing}, } + ty = "ScalarOneStepMemoset" metaconditions = UniqueVector(metaconditions) relations = UniqueVector(relations) + if identityrel in relations + @warn "Using identity relation in a relational memoset. This is not optimal." + end + if globalrel in relations && isnothing(globmemoset) @warn "Using global relation in a relational memoset. This is not optimal." end @@ -96,9 +183,10 @@ struct ScalarOneStepMemoset{ @assert nmetaconditions(relmemoset) == length(metaconditions) "Can't instantiate " * "$(ty) with mismatching nmetaconditions for relmemoset and " * "provided metaconditions: $(nmetaconditions(relmemoset)) and $(length(metaconditions))" - @assert nrelations(relmemoset) == length(relations) "Can't instantiate " * - "$(ty) with mismatching nrelations for relmemoset and " * - "provided relations: $(nrelations(relmemoset)) and $(length(relations))" + # Global relation breaks this: + # @assert nrelations(relmemoset) == length(relations) "Can't instantiate " * + # "$(ty) with mismatching nrelations for relmemoset and " * + # "provided relations: $(nrelations(relmemoset)) and $(length(relations))" if !isnothing(globmemoset) @assert nmetaconditions(globmemoset) == length(metaconditions) "Can't " * @@ -123,100 +211,87 @@ struct ScalarOneStepMemoset{ precompute_relmemoset :: Bool = false, ) where {W<:AbstractWorld,U} - _fwd = fwd(X) - - _features = features(X) - _grouped_featsnaggrs = grouped_featsnaggrs(X) - featsnaggrs = features_grouped_featsaggrsnops2featsnaggrs(features(X), grouped_featsaggrsnops(X)) - + # Only compute global memoset if the global relation is in the relation set. compute_globmemoset = begin if globalrel in relations - relations = filter!(l->l≠globalrel, relations) + relations = filter(l->l≠globalrel, relations) true else false end end - _n_instances = ninstances(X) - nrelations = length(relations) - nmetaconditions = sum(length.(_grouped_featsnaggrs)) - # Prepare relmemoset - perform_initialization = !precompute_relmemoset - relmemoset = relational_memoset_type(X, perform_initialization) + perform_initialization = true # !precompute_relmemoset + relmemoset = relational_memoset_type(X, metaconditions, relations, perform_initialization) # Prepare globmemoset globmemoset = begin if compute_globmemoset - ScalarOneStepGlobalMemoset(X) + ScalarOneStepGlobalMemoset(X, metaconditions, perform_initialization) else nothing end end - # p = Progress(_n_instances, 1, "Computing EMD supports...") - Threads.@threads for i_instance in 1:_n_instances - # @logmsg LogDebug "Instance $(i_instance)/$(_n_instances)" - - # if i_instance == 1 || ((i_instance+1) % (floor(Int, ((_n_instances)/4))+1)) == 0 - # @logmsg LogOverview "Instance $(i_instance)/$(_n_instances)" - # end + if (compute_globmemoset && precompute_globmemoset) || precompute_relmemoset - for (i_feature,aggregators) in enumerate(_grouped_featsnaggrs) - feature = _features[i_feature] - # @logmsg LogDebug "Feature $(i_feature)" + metaconditions = UniqueVector(metaconditions) + relations = UniqueVector(relations) - fwdslice = fwdread_channel(_fwd, i_instance, i_feature) + n_instances = ninstances(X) + nrelations = length(relations) + nmetaconditions = length(metaconditions) - # @logmsg LogDebug fwdslice + grouped_metaconditions = groupbyfeature(metaconditions) - # Global relation (independent of the current world) - if compute_globmemoset && precompute_globmemoset - # @logmsg LogDebug "globalrel" + grouped_metaconditions = map(((feature,these_metaconditions),)->begin + these_metaconditions = map(_metacond->begin + i_metacond = findfirst(isequal(_metacond), metaconditions) + aggregator = existential_aggregator(test_operator(_metacond)) + (i_metacond, aggregator, _metacond) + end, these_metaconditions) + (feature,these_metaconditions) + end, grouped_metaconditions) - # TODO optimize: all aggregators are likely reading the same raw values. - for (i_featsnaggr,aggr) in aggregators - # Threads.@threads for (i_featsnaggr,aggr) in aggregators + # p = Progress(n_instances, 1, "Computing EMD supports...") + Threads.@threads for i_instance in 1:n_instances - gamma = fwdslice_onestep_accessible_aggregation(X, fwdslice, i_instance, globalrel, feature, aggr) + for (_feature, these_metaconditions) in grouped_metaconditions - # @logmsg LogDebug "Aggregator[$(i_featsnaggr)]=$(aggr) --> $(gamma)" + _featchannel = featchannel(X, i_instance, _feature) - globmemoset[i_instance, i_featsnaggr] = gamma - end - end + # Global relation (independent of the current world) + if compute_globmemoset && precompute_globmemoset - if precompute_relmemoset - # Other relations - for (i_relation,relation) in enumerate(relations) + for (i_metacond, aggregator, _metacond) in these_metaconditions - # @logmsg LogDebug "Relation $(i_relation)/$(nrelations)" + gamma = featchannel_onestep_aggregation(X, _featchannel, i_instance, globalrel, _feature, aggregator) - for (i_featsnaggr,aggr) in aggregators - relmemoset_init_world_slice(relmemoset, i_instance, i_featsnaggr, i_relation) + globmemoset[i_instance, i_metacond] = gamma end + end - for w in allworlds(X, i_instance) - - # @logmsg LogDebug "World" w + # Other, generic relations + if precompute_relmemoset + for (i_relation, relation) in enumerate(relations) - # TODO optimize: all aggregators are likely reading the same raw values. - for (i_featsnaggr,aggr) in aggregators + for w in allworlds(X, i_instance) - gamma = fwdslice_onestep_accessible_aggregation(X, fwdslice, i_instance, w, relation, feature, aggr) + for (i_metacond, aggregator, _metacond) in these_metaconditions - # @logmsg LogDebug "Aggregator" aggr gamma + gamma = featchannel_onestep_aggregation(X, _featchannel, i_instance, w, relation, _feature, aggregator) - relmemoset[i_instance, w, i_featsnaggr, i_relation] = gamma + relmemoset[i_instance, w, i_metacond, i_relation] = gamma + end end end end end + # next!(p) end - # next!(p) end - ScalarOneStepMemoset(relmemoset, globmemoset, featsnaggrs) + ScalarOneStepMemoset(metaconditions, relations, relmemoset, globmemoset) end end @@ -230,32 +305,94 @@ globmemoset(Xm::ScalarOneStepMemoset) = Xm.globmemoset ninstances(Xm::ScalarOneStepMemoset) = ninstances(relmemoset(Xm)) +function featchannel_onestep_aggregation( + X::AbstractLogiset{W,U}, + Xm::ScalarOneStepMemoset, + featchannel, + i_instance::Integer, + w::W, + rel::AbstractRelation, + metacond::ScalarMetaCondition, + i_metacond::Union{Nothing,Integer} = nothing, + i_relation::Union{Nothing,Integer} = nothing +)::U where {U,W<:AbstractWorld} + + if isnothing(i_metacond) + i_metacond = findfirst(isequal(metacond), metaconditions(Xm)) + end + + if isnothing(i_metacond) + i_metacond = findfirst((m)->feature(m) == feature(metacond) && existential_aggregator(test_operator(m)) == existential_aggregator(test_operator(metacond)), metaconditions(Xm)) + if isnothing(i_metacond) + i_neg_metacond = findfirst(isequal(negation(metacond)), metaconditions(Xm)) + error("Could not find metacondition $(metacond) in memoset of type $(typeof(Xm)) " * + "($(!isnothing(i_neg_metacond) ? "but negation was found " * + "with i_metacond = $(i_neg_metacond)!" : "")).") + end + end + + _feature = feature(metacond) + _test_operator = test_operator(metacond) + aggregator = existential_aggregator(_test_operator) + + gamma = begin + if rel == globalrel + _globmemoset = globmemoset(Xm) + if isnothing(_globmemoset[i_instance, i_metacond]) + gamma = featchannel_onestep_aggregation(X, featchannel, i_instance, rel, _feature, aggregator) + _globmemoset[i_instance, i_metacond] = gamma + end + _globmemoset[i_instance, i_metacond] + else + i_relation = isnothing(i_relation) ? findfirst(isequal(rel), Xm.relations) : i_relation + if isnothing(i_relation) + error("Could not find relation $(rel) in memoset of type $(typeof(Xm)).") + end + _relmemoset = relmemoset(Xm) + if isnothing(_relmemoset[i_instance, w, i_metacond, i_relation]) + gamma = featchannel_onestep_aggregation(X, featchannel, i_instance, w, rel, _feature, aggregator) + _relmemoset[i_instance, w, i_metacond, i_relation] = gamma + end + _relmemoset[i_instance, w, i_metacond, i_relation] + end + end +end + function check( f::ScalarExistentialFormula, Xm::ScalarOneStepMemoset, i_instance::Integer, - w::W; - kwargs... + w::W, + rel, + metacond, ) where {W<:AbstractWorld} - _rel = relation(f) - _metacond = metacond(f) - i_metacond = findfirst(isequal(_metacond), Xm.metaconditions) + rel = relation(f) + metacond = metacond(f) + + i_metacond = findfirst(isequal(metacond), metaconditions(Xm)) if isnothing(i_metacond) - error("Could not find metacondition $(_metacond) in memoset of type $(typeof(Xm)).") + i_metacond = findfirst((m)->feature(m) == feature(metacond) && existential_aggregator(test_operator(m)) == existential_aggregator(test_operator(metacond)), metaconditions(Xm)) + if isnothing(i_metacond) + i_neg_metacond = findfirst(isequal(negation(metacond)), metaconditions(Xm)) + error("Could not find metacondition $(metacond) in memoset of type $(typeof(Xm)) " * + "($(!isnothing(i_neg_metacond) ? "but negation was found " * + "with i_metacond = $(i_neg_metacond)!" : "")).") + end end + gamma = begin - if _rel + if rel Base.getindex(globmemoset(Xm), i_instance, i_metacond) else - i_rel = findfirst(isequal(_rel), Xm.relations) + i_rel = findfirst(isequal(rel), Xm.relations) if isnothing(i_rel) - error("Could not find relation $(_rel) in memoset of type $(typeof(Xm)).") + error("Could not find relation $(rel) in memoset of type $(typeof(Xm)).") end Base.getindex(relmemoset(Xm), i_instance, w, i_metacond, i_rel) end end if !isnothing(gamma) - return apply_test_operator(test_operator(f), gamma, threshold(f)) + return apply_test_operator(test_operator(metacond), gamma, threshold(f)) else return nothing end @@ -309,29 +446,26 @@ end function displaystructure(Xm::ScalarOneStepMemoset; indent_str = "", include_ninstances = true) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))-1) pieces = [] - push!(pieces, " \n") + push!(pieces, "ScalarOneStepMemoset ($(humansize(Xm)))\n") if include_ninstances - push!(pieces, " " * padattribute("# instances:", "$(ninstances(Xm))\n")) + push!(pieces, " $(padattribute("# instances:", ninstances(Xm)))\n") end - push!(pieces, "$(padattribute("# metaconditions:", nmetaconditions(Xm)))") - push!(pieces, "$(padattribute("# relations:", nrelations(Xm)))") + push!(pieces, " $(padattribute("# metaconditions:", nmetaconditions(Xm)))\n") + push!(pieces, " $(padattribute("# relations:", nrelations(Xm)))\n") - push!(pieces, "$(padattribute("metaconditions:", "$(eltype(metaconditions(Xm)))[$(join(syntaxstring.(metaconditions(Xm))))]", ",")))") - push!(pieces, "$(padattribute("relations:", relations(Xm)))") + push!(pieces, " $(padattribute("metaconditions:", "$(eltype(metaconditions(Xm)))[$(join([(syntaxstring.(metaconditions(Xm))[1:4])..., "...", syntaxstring.(metaconditions(Xm))[end-4:end]...], ","))]")))\n") + push!(pieces, " $(padattribute("relations:", relations(Xm)))\n") - push!(pieces, " relational memoset ($(round(nonnothingshare(relmemoset(Xm))*100, digits=2))% memoized):\n") - push!(pieces, " " * displaystructure(relmemoset(Xm); indent_str = "$(indent_str)│ ", include_ninstances = false, include_nmetaconditions = false, include_nrelations = false)) + push!(pieces, "[R] " * displaystructure(relmemoset(Xm); indent_str = "$(indent_str)│ ", include_ninstances = false, include_nmetaconditions = false, include_nrelations = false)) if !isnothing(globmemoset(Xm)) - push!(pieces, " global memoset ($(round(nonnothingshare(globmemoset(Xm))*100, digits=2))% memoized):\n") - push!(pieces, " " * displaystructure(globmemoset(Xm); indent_str = "$(indent_str)│ ", include_ninstances = false, include_nmetaconditions = false)) + push!(pieces, "[G] " * displaystructure(globmemoset(Xm); indent_str = "$(indent_str) ", include_ninstances = false, include_nmetaconditions = false)) else push!(pieces, " global memoset: −\n") end - return "ScalarOneStepMemoset ($(humansize(Xm)))" * - join(pieces, "$(indent_str)├", "$(indent_str)└") * "\n" + return join(pieces, "$(indent_str)├", "$(indent_str)└") end ############################################################################################ @@ -344,7 +478,7 @@ datasets with scalar features. The formulas are of type ⟨R⟩ (f ⋈ t) TODO explain See also -[`Memoset`](@ref), +[`FullMemoset`](@ref), [`SuportedLogiset`](@ref), [`AbstractLogiset`](@ref). """ @@ -364,11 +498,11 @@ struct ScalarOneStepRelationalMemoset{ end function ScalarOneStepRelationalMemoset( - X::AbstractLogiset{W,U,F,FR}, + X::AbstractLogiset{W,U,FT,FR}, metaconditions::AbstractVector{<:ScalarMetaCondition}, relations::AbstractVector{<:AbstractRelation}, - perform_initialization = false - ) where {W,U,F<:AbstractFeature,FR<:AbstractFrame{W}} + perform_initialization = true + ) where {W,U,FT<:AbstractFeature,FR<:AbstractFrame{W}} nmetaconditions = length(metaconditions) nrelations = length(relations) d = begin @@ -403,6 +537,17 @@ nmemoizedvalues(Xm::ScalarOneStepRelationalMemoset) = sum(length.(Xm.d)) get(Xm.d[i_instance, i_metacond, i_relation], w, nothing) end +@inline function Base.setindex!( + Xm :: ScalarOneStepRelationalMemoset{W}, + gamma, + i_instance :: Integer, + w :: W, + i_metacond :: Integer, + i_relation :: Integer, +) where {W<:AbstractWorld} + Xm.d[i_instance, i_metacond, i_relation][w] = gamma +end + usesfullmemo(::ScalarOneStepRelationalMemoset) = false function hasnans(Xm::ScalarOneStepRelationalMemoset) @@ -421,6 +566,7 @@ end function displaystructure(Xm::ScalarOneStepRelationalMemoset; indent_str = "", include_ninstances = true, include_nmetaconditions = true, include_nrelations = true) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) pieces = [] + push!(pieces, "ScalarOneStepRelationalMemoset ($(memoizationinfo(Xm)), $(humansize(Xm)))") push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") @@ -434,14 +580,12 @@ function displaystructure(Xm::ScalarOneStepRelationalMemoset; indent_str = "", i if include_nrelations push!(pieces, "$(padattribute("# relations:", nrelations(Xm)))") end - push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") + push!(pieces, "$(padattribute("size × eltype:", "$(size(Xm.d)) × $(eltype(Xm.d))"))") + # push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") - return "ScalarOneStepRelationalMemoset ($(humansize(Xm)))" * - join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" + return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" end -# fwd_rs_init_world_slice(Xm::ScalarOneStepRelationalMemoset{W,U}, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) where {W,U} = -# Xm.d[i_instance, i_featsnaggr, i_relation] = Dict{W,U}() # @inline function Base.setindex!(Xm::ScalarOneStepRelationalMemoset{W,U}, threshold::U, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {W,U} # Xm.d[i_instance, i_featsnaggr, i_relation][w] = threshold # end @@ -459,14 +603,14 @@ struct ScalarOneStepGlobalMemoset{ function ScalarOneStepGlobalMemoset{W,U}( d::D - ) where {W<:AbstractWorld,U,D<:AbstractArray{UU,2} where {UU<:Union{U,Nothing}} + ) where {W<:AbstractWorld,U,D<:AbstractArray{UU,2} where {UU<:Union{U,Nothing}}} new{W,U,D}(d) end function ScalarOneStepGlobalMemoset( X::AbstractLogiset{W,U}, metaconditions::AbstractVector{<:ScalarMetaCondition}, - perform_initialization = false + perform_initialization = true ) where {W<:AbstractWorld,U} @assert worldtype(X) != OneWorld "TODO adjust this note: note that you should not use a global Xm when not using global decisions" nmetaconditions = length(metaconditions) @@ -487,13 +631,23 @@ capacity(Xm::ScalarOneStepGlobalMemoset) = prod(size(Xm.d)) nmemoizedvalues(Xm::ScalarOneStepGlobalMemoset) = sum((!).((isnothing).(Xm.d))) @inline function Base.getindex( - Xm :: ScalarOneStepRelationalMemoset{W}, + Xm :: ScalarOneStepGlobalMemoset{W}, i_instance :: Integer, i_metacond :: Integer, ) where {W<:AbstractWorld} Xm.d[i_instance, i_metacond] end + +@inline function Base.setindex!( + Xm :: ScalarOneStepGlobalMemoset{W}, + gamma, + i_instance :: Integer, + i_metacond :: Integer, +) where {W<:AbstractWorld} + Xm.d[i_instance, i_metacond] = gamma +end + usesfullmemo(::ScalarOneStepGlobalMemoset) = false function hasnans(Xm::ScalarOneStepGlobalMemoset) @@ -513,6 +667,7 @@ end function displaystructure(Xm::ScalarOneStepGlobalMemoset; indent_str = "", include_ninstances = true, include_nmetaconditions = true) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) pieces = [] + push!(pieces, "ScalarOneStepGlobalMemoset ($(memoizationinfo(Xm)), $(humansize(Xm)))") push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") @@ -523,10 +678,10 @@ function displaystructure(Xm::ScalarOneStepGlobalMemoset; indent_str = "", inclu if include_nmetaconditions push!(pieces, "$(padattribute("# metaconditions:", nmetaconditions(Xm)))") end - push!(pieces, "$(padattribute("capacity:", capacity(Xm)))") - push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") + push!(pieces, "$(padattribute("size × eltype:", "$(size(Xm.d)) × $(eltype(Xm.d))"))") + # push!(pieces, "$(padattribute("capacity:", capacity(Xm)))") + # push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") - return "ScalarOneStepGlobalMemoset ($(humansize(Xm)))" * - join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" + return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" end diff --git a/src/logisets/base/scalar/random.jl b/src/logisets/scalar/random.jl similarity index 100% rename from src/logisets/base/scalar/random.jl rename to src/logisets/scalar/random.jl diff --git a/src/logisets/base/scalar/representatives.jl b/src/logisets/scalar/representatives.jl similarity index 62% rename from src/logisets/base/scalar/representatives.jl rename to src/logisets/scalar/representatives.jl index 27a6e8e..911320b 100644 --- a/src/logisets/base/scalar/representatives.jl +++ b/src/logisets/scalar/representatives.jl @@ -1,8 +1,10 @@ -using SoleLogics: AbstractMultiModalFrame, AbstractRelation, GlobalRel, IdentityRel, accessibles +using SoleLogics: AbstractUniModalFrame, AbstractFrame, AbstractRelation, GlobalRel, IdentityRel, accessibles + +# TODO: AbstractFrame -> AbstractMultiModalFrame, and provide the same for AbstractUniModalFrame """ function representatives( - fr::AbstractMultiModalFrame{W}, + fr::AbstractFrame{W}, S::W, ::AbstractRelation, ::ScalarMetaCondition @@ -20,17 +22,25 @@ truth. A few cases arise depending on the relation, the feature and the test operator (or, better, its *aggregator*). """ function representatives( # Dispatch on feature/aggregator pairs - fr::AbstractMultiModalFrame{W}, + fr::AbstractFrame{W}, w::W, r::AbstractRelation, - c::ScalarMetaCondition + metacond::ScalarMetaCondition +) where {W<:AbstractWorld} + representatives(fr, w, r, feature(metacond), existential_aggregator(test_operator(metacond))) +end + +function representatives( + fr::AbstractUniModalFrame{W}, + w::W, + metacond::ScalarMetaCondition ) where {W<:AbstractWorld} - representatives(fr, w, r, feature(c), existential_aggregator(test_operator(c))) + representatives(fr, w, feature(metacond), existential_aggregator(test_operator(metacond))) end # Fallbacks to `accessibles` function representatives( - fr::AbstractMultiModalFrame{W}, + fr::AbstractFrame{W}, w::W, r::AbstractRelation, ::AbstractFeature, @@ -41,7 +51,7 @@ end # Global relation: dispatch on feature/aggregator pairs function representatives( - fr::AbstractMultiModalFrame{W}, + fr::AbstractFrame{W}, w::W, r::GlobalRel, f::AbstractFeature, @@ -52,7 +62,7 @@ end # Global relation: fallbacks to `accessibles` function representatives( - fr::AbstractMultiModalFrame{W}, + fr::AbstractFrame{W}, r::GlobalRel, f::AbstractFeature, a::Aggregator @@ -61,6 +71,6 @@ function representatives( end # # TODO remove but probably we need this to stay because of ambiguities! -# representatives(fr::AbstractMultiModalFrame{W}, w::W, r::IdentityRel, ::AbstractFeature, ::Aggregator) where {W<:AbstractWorld} = accessibles(fr, w, r) +# representatives(fr::AbstractFrame{W}, w::W, r::IdentityRel, ::AbstractFeature, ::Aggregator) where {W<:AbstractWorld} = accessibles(fr, w, r) # TODO need this? -# `representatives(fr::AbstractMultiModalFrame{W}, S::AbstractWorldSet{W}, ::GlobalRel, ::ScalarMetaCondition)` +# `representatives(fr::AbstractFrame{W}, S::AbstractWorldSet{W}, ::GlobalRel, ::ScalarMetaCondition)` diff --git a/src/logisets/base/scalar/templated-formulas.jl b/src/logisets/scalar/templated-formulas.jl similarity index 100% rename from src/logisets/base/scalar/templated-formulas.jl rename to src/logisets/scalar/templated-formulas.jl diff --git a/src/logisets/base/scalar/test-operators.jl b/src/logisets/scalar/test-operators.jl similarity index 100% rename from src/logisets/base/scalar/test-operators.jl rename to src/logisets/scalar/test-operators.jl diff --git a/src/logisets/base/scalar/var-features.jl b/src/logisets/scalar/var-features.jl similarity index 98% rename from src/logisets/base/scalar/var-features.jl rename to src/logisets/scalar/var-features.jl index e1a9547..3f5c9e5 100644 --- a/src/logisets/base/scalar/var-features.jl +++ b/src/logisets/scalar/var-features.jl @@ -365,33 +365,33 @@ const BASE_FEATURE_ALIASES = Dict{String,Union{Type,Function}}( ) function parsefeature( - ::Type{F}, + ::Type{FT}, expression::String; featvaltype::Union{Nothing,Type} = nothing, kwargs... -) where {F<:VarFeature} +) where {FT<:VarFeature} if isnothing(featvaltype) featvaltype = DEFAULT_VARFEATVALTYPE @warn "Please, specify a type for the feature values (featvaltype = ...). " * "$(featvaltype) will be used, but note that this may raise type errors. " * "(expression = $(repr(expression)))" end - _parsefeature(F{featvaltype}, expression; kwargs...) + _parsefeature(FT{featvaltype}, expression; kwargs...) end function parsefeature( - ::Type{F}, + ::Type{FT}, expression::String; featvaltype::Union{Nothing,Type} = nothing, kwargs... -) where {U,F<:VarFeature{U}} - @assert isnothing(featvaltype) || featvaltype == U "Cannot parse feature of type $(F) with " * +) where {U,FT<:VarFeature{U}} + @assert isnothing(featvaltype) || featvaltype == U "Cannot parse feature of type $(FT) with " * "featvaltype = $(featvaltype). (expression = $(repr(expression)))" - _parsefeature(F, expression; kwargs...) + _parsefeature(FT, expression; kwargs...) end function _parsefeature( - ::Type{F}, + ::Type{FT}, expression::String; opening_bracket::String = UVF_OPENING_BRACKET, closing_bracket::String = UVF_CLOSING_BRACKET, @@ -399,7 +399,7 @@ function _parsefeature( variable_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, variable_name_prefix::Union{Nothing,String} = nothing, kwargs... -) where {U,F<:VarFeature{U}} +) where {U,FT<:VarFeature{U}} @assert isnothing(variable_names_map) || isnothing(variable_name_prefix) "" * "Cannot parse variable with both variable_names_map and variable_name_prefix. " * "(expression = $(repr(expression)))" @@ -458,8 +458,8 @@ function _parsefeature( end end - # if !(feature isa F) - # @warn "Could not parse expression $(repr(expression)) as feature of type $(F); " * + # if !(feature isa FT) + # @warn "Could not parse expression $(repr(expression)) as feature of type $(FT); " * # " $(typeof(feature)) was used." # end diff --git a/src/logisets/base/supported-logiset.jl b/src/logisets/supported-logiset.jl similarity index 81% rename from src/logisets/base/supported-logiset.jl rename to src/logisets/supported-logiset.jl index fef7d48..bbff96e 100644 --- a/src/logisets/base/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -1,20 +1,21 @@ """ -A logiset associated to a number of cascading memoization structures, that are used -when checking formulas. +A logiset associated to a number of cascading full or one-step +memoization structures, that are used when checking formulas. # Examples TODO add example showing that checking is faster when using this structure. See also [`SuportedLogiset`](@ref), -[`AbstractMemoset`](@ref), +[`AbstractFullMemoset`](@ref), +[`AbstractOneStepMemoset`](@ref), [`AbstractLogiset`](@ref). """ struct SupportedLogiset{ L<:AbstractLogiset, N, - MS<:NTuple{N,AbstractMemoset}, -} <: AbstractLogiset{W where W<:AbstractWorld,U where U,F where F<:AbstractFeature,FR where FR<:AbstractFrame{W where W<:AbstractWorld}} + MS<:NTuple{N,Union{AbstractOneStepMemoset,AbstractFullMemoset}}, +} <: AbstractLogiset{W where W<:AbstractWorld,U where U,FT where FT<:AbstractFeature,FR where FR<:AbstractFrame{W where W<:AbstractWorld}} # Core dataset base :: L @@ -24,15 +25,22 @@ struct SupportedLogiset{ function SupportedLogiset( base::L, supports::_MS - ) where {L<:AbstractLogiset,_N,_MS<:NTuple{_N,<:Union{<:AbstractVector{<:AbstractDict},<:AbstractMemoset,<:SupportedLogiset}}} + ) where {L<:AbstractLogiset,_MS<:Tuple} + + wrong_supports = filter(supp->!(supp isa Union{<:AbstractVector{<:AbstractDict},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}), supports) + + @assert length(wrong_supports) == 0 "Cannot instantiate SupportedLogiset " * + "with wrong support type(s): $(join(typeof.(wrong_supports), ", ")). " * + "Only full and one-step memosets are allowed." @assert !(base isa SupportedLogiset) "Cannot instantiate SupportedLogiset " * "with a SupportedLogiset base." if length(supports) == 0 - supports = (Memoset(base),) + full_memoset_type = default_full_memoset_type(base) + supports = (full_memoset_type(base),) end supports = Tuple(vcat(map(supp->begin if supp isa AbstractVector{<:AbstractDict{<:AbstractFormula,<:WorldSet}} - [Memoset(supp)] + [FullMemoset(supp)] elseif supp isa SupportedLogiset @assert base == SoleModels.base(supp) "Cannot inherit supports from " * "SupportedLogiset with different base." @@ -77,7 +85,7 @@ struct SupportedLogiset{ function SupportedLogiset( base::AbstractLogiset, - supports::AbstractVector{<:Union{AbstractVector{<:AbstractDict},AbstractMemoset,SupportedLogiset}} + supports::AbstractVector ) SupportedLogiset(base, Tuple(supports)) end @@ -85,23 +93,23 @@ struct SupportedLogiset{ # Helper (avoids ambiguities) function SupportedLogiset( base::AbstractLogiset, - firstsupport::Union{<:AbstractVector{<:AbstractDict},<:AbstractMemoset,<:SupportedLogiset}, - supports::Union{<:AbstractVector{<:AbstractDict},<:AbstractMemoset,<:SupportedLogiset}... + firstsupport::Union{<:AbstractVector{<:AbstractDict},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}, + supports::Union{<:AbstractVector{<:AbstractDict},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}... ) - SupportedLogiset(base, [firstsupport, supports...]) + SupportedLogiset(base, Union{<:AbstractVector{<:AbstractDict},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}[firstsupport, supports...]) end function SupportedLogiset( base :: AbstractLogiset; - use_full_memoization :: Union{Bool,Type{<:AbstractMemoset}} = true, + use_full_memoization :: Union{Bool,Type{<:Union{AbstractOneStepMemoset,AbstractFullMemoset}}} = true, # conditions :: Union{Nothing,AbstractVector{<:AbstractCondition}} = nothing, relations :: Union{Nothing,AbstractVector{<:AbstractRelation}} = nothing, use_onestep_memoization :: Union{Bool,Type{<:AbstractOneStepMemoset}} = !isnothing(conditions) && !isnothing(relations), onestep_precompute_globmemoset :: Bool = (use_onestep_memoization != false), - onestep_precompute_globmemoset :: Bool = false, + onestep_precompute_relmemoset :: Bool = false, ) - supports = [] + supports = Union{AbstractOneStepMemoset,AbstractFullMemoset}[] @assert !xor(isnothing(conditions), isnothing(relations)) "Please, provide " * "both conditions and relations in order to use a one-step memoset." @@ -131,7 +139,7 @@ struct SupportedLogiset{ # function SupportedLogiset( # X :: ... AbstractActiveScalarLogiset{W,V,FT,Bool,FR}; # kwargs..., - # ) where {V,FT<:AbstractFeature{V},W<:AbstractWorld,FR<:AbstractFrame{W}} + # ) where {V,FT<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} # SupportedLogiset(Logiset(X); kwargs...) # end @@ -140,6 +148,9 @@ end base(X::SupportedLogiset) = X.base supports(X::SupportedLogiset) = X.supports +basetype(X::SupportedLogiset{L,N,MS}) where {L,N,MS} = L +supporttypes(X::SupportedLogiset{L,N,MS}) where {L,N,MS} = MS + nsupports(X::SupportedLogiset) = length(X.supports) capacity(X::SupportedLogiset) = sum(capacity.(supports(X))) @@ -230,11 +241,10 @@ function displaystructure(X::SupportedLogiset; indent_str = "", include_ninstanc push!(pieces, " " * padattribute("# instances:", "$(ninstances(X))\n")) end push!(pieces, " " * padattribute("usesfullmemo:", "$(usesfullmemo(X))\n")) - push!(pieces, " base:\n") - push!(pieces, " " * displaystructure(base(X); indent_str = "$(indent_str)│ ", include_ninstances = false)) + push!(pieces, "[BASE] " * displaystructure(base(X); indent_str = "$(indent_str)│ ", include_ninstances = false)) push!(pieces, " " * padattribute("# supports:", "$(nsupports(X))\n")) for (i_supp,supp) in enumerate(supports(X)) - push!(pieces, "[$(i_supp)] $(displaystructure(supp; indent_str = (i_supp == nsupports(X) ? "$(indent_str) " : "$(indent_str)│ "), include_ninstances = false))\n") + push!(pieces, "[$(i_supp)] $(displaystructure(supp; indent_str = (i_supp == nsupports(X) ? "$(indent_str) " : "$(indent_str)│ "), include_ninstances = false))") end return "SupportedLogiset ($(humansize(X)))" * join(pieces, "$(indent_str)├", "$(indent_str)└") * "\n" diff --git a/src/logisets/base/templated-formulas.jl b/src/logisets/templated-formulas.jl similarity index 100% rename from src/logisets/base/templated-formulas.jl rename to src/logisets/templated-formulas.jl diff --git a/test/datasets.jl b/test/datasets.jl index dc29fb8..5693024 100644 --- a/test/datasets.jl +++ b/test/datasets.jl @@ -155,13 +155,13 @@ rng = Random.MersenneTwister(1) alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]) syntaxstring.(alph) _formulas = [randformulatree(rng, 4, alph, [NEGATION, CONJUNCTION, IMPLICATION, DIAMOND, BOX]) for i in 1:10] -syntaxstring.(_formulas) +# syntaxstring.(_formulas) c1 = @test_nowarn [check(φ, bool_logiset, 1, w) for φ in _formulas] c2 = @test_nowarn [check(φ, bool_logiset, 1, w; use_memo = nothing) for φ in _formulas] c3 = @test_nowarn [check(φ, bool_logiset, 1, w; use_memo = memoset) for φ in _formulas] -c4 = @test_nowarn [check(φ, bool_supported_logiset, 1, w) for φ in _formulas] -c5 = @test_nowarn [check(φ, bool_supported_logiset, 1, w; use_memo = nothing) for φ in _formulas] +c4 = @test_nowarn [check(φ, SupportedLogiset(bool_logiset), 1, w) for φ in _formulas] +c5 = @test_nowarn [check(φ, SupportedLogiset(bool_logiset), 1, w; use_memo = nothing) for φ in _formulas] # c6 = @test_logs (:warn,) [check(φ, bool_supported_logiset, 1, w; use_memo = memoset) for φ in _formulas] @test c1 == c2 == c3 == c4 == c5 @@ -184,11 +184,69 @@ using SoleModels: ScalarOneStepRelationalMemoset, ScalarOneStepGlobalMemoset # metaconditions = [ScalarMetaCondition(features[1], >)] metaconditions = [ScalarMetaCondition(f, test_op) for f in features for test_op in [>,<]] -@test_throws AssertionError ScalarOneStepGlobalMemoset{Interval,Float64}(metaconditions, rand(1,2)) -@test_nowarn ScalarOneStepGlobalMemoset{Interval,Float64}(metaconditions, rand(1,22)) +@test_nowarn ScalarOneStepGlobalMemoset{Interval,Float64}(rand(1,22)) perform_initialization = true bool_relationalmemoset = @test_nowarn ScalarOneStepRelationalMemoset(bool_logiset, metaconditions, [globalrel], perform_initialization) bool_globalmemoset = @test_nowarn ScalarOneStepGlobalMemoset(bool_logiset, metaconditions, perform_initialization) -@test_nowarn SupportedLogiset(bool_logiset, bool_relationalmemoset) +@test_throws MethodError SupportedLogiset(bool_logiset, bool_relationalmemoset) + +using SoleModels: ScalarOneStepMemoset + +relations = [identityrel, globalrel] + +bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(metaconditions, relations, bool_relationalmemoset, bool_globalmemoset) + +bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(metaconditions, relations, bool_relationalmemoset, bool_globalmemoset) +bool_onestepmemoset_empty = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations) +bool_onestepmemoset_full = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations; precompute_globmemoset = true, precompute_relmemoset = true) + +@test_nowarn SupportedLogiset(bool_logiset, bool_onestepmemoset) +@test_nowarn SupportedLogiset(bool_logiset, (bool_onestepmemoset,)) +@test_nowarn SupportedLogiset(bool_logiset, (bool_onestepmemoset, bool_onestepmemoset)) +@test_nowarn SupportedLogiset(bool_logiset, [bool_onestepmemoset, bool_onestepmemoset]) + +@test_nowarn SupportedLogiset(bool_logiset, bool_onestepmemoset, memoset) +@test_nowarn SupportedLogiset(bool_logiset, (bool_onestepmemoset, memoset)) +@test_nowarn SupportedLogiset(bool_logiset, [bool_onestepmemoset, memoset]) + +# bool_logiset_2layer = SupportedLogiset(bool_logiset, bool_onestepmemoset) +memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstances(bool_logiset)] +bool_logiset_2layer = SupportedLogiset(bool_logiset) +# bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset, memoset]) +bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset_empty, memoset]) +# bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset_full, memoset]) + +rng = Random.MersenneTwister(1) +alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]) +syntaxstring.(alph) +_formulas = [randformulatree(rng, 10, alph, SoleLogics.BASE_MULTIMODAL_OPERATORS) for i in 1:20]; + +# Below are the times with a testset of 1000 formulas +############################################################################################ +# 223.635 ms (1459972 allocations: 59.18 MiB) +############################################################################################ +c1 = @test_nowarn [check(φ, bool_logiset, 1, w) for φ in _formulas] +############################################################################################ +c1 = @test_nowarn [check(φ, bool_logiset, 1, w; perform_normalization = false) for φ in _formulas] + +############################################################################################ +# 107.169 ms (545163 allocations: 14.71 MiB) +############################################################################################ +c2 = @test_nowarn [check(φ, bool_logiset_2layer, 1, w) for φ in _formulas] +############################################################################################ +c2 = @test_nowarn [check(φ, bool_logiset_2layer, 1, w; perform_normalization = false) for φ in _formulas] + +############################################################################################ +# 34.990 ms (301175 allocations: 14.93 MiB) +############################################################################################ +memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstances(bool_logiset)] +bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset_empty, memoset]) +c4 = @test_nowarn [check(φ, bool_logiset_3layer, 1, w; perform_normalization = false) for φ in _formulas] +############################################################################################ + +@test c1 == c2 == c4 + + +@test SoleModels.nmemoizedvalues(bool_logiset_3layer.supports[1].relmemoset) > 0 diff --git a/test/runtests.jl b/test/runtests.jl index 7faef65..c7c602f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -16,11 +16,11 @@ end println("Julia version: ", VERSION) test_suites = [ - ("Models", ["base.jl", ]), ("Datasets", [ "datasets.jl", # "dimensional-datasets.jl", ]), + ("Models", ["base.jl", ]), ("Miscellaneous", ["misc.jl", "minify.jl"]), # ("Parse", ["parse.jl", ]), ] From 67677d200ead5264613200f899bf418facf7e9fb Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 16 Jun 2023 02:04:58 +0200 Subject: [PATCH 13/77] Add dimensional logisets and memosets. They seem to work. --- Project.toml | 2 +- src/SoleModels.jl | 41 +- src/dimensional-logisets/dimensional-fwds.jl | 349 -------------- .../dimensional-supports.jl | 420 ---------------- src/dimensional-logisets/main.jl | 68 --- src/logisets/check.jl | 23 +- .../dataset-bindings.jl | 77 +++ .../dimensional-structures/logiset.jl | 448 ++++++++++++++++++ src/logisets/dimensional-structures/main.jl | 74 +++ .../onestep-memosets.jl | 291 ++++++++++++ .../representatives/Full0DFrame.jl | 0 .../representatives/Full1DFrame+IA.jl | 0 .../representatives/Full1DFrame+RCC.jl | 0 .../representatives/Full1DFrame.jl | 0 .../representatives/Full2DFrame.jl | 0 src/logisets/logiset.jl | 86 ++-- src/logisets/main.jl | 48 +- src/logisets/memosets.jl | 19 +- src/logisets/multilogiset.jl | 11 - src/logisets/scalar/dataset-bindings.jl | 60 +++ src/logisets/scalar/main.jl | 5 +- src/logisets/scalar/memosets.jl | 8 + ...one-step-memoset.jl => onestep-memoset.jl} | 117 +++-- src/logisets/scalar/test-operators.jl | 4 - src/logisets/scalar/var-features.jl | 2 +- src/logisets/supported-logiset.jl | 79 ++- .../_parse-dimensional-condition.jl | 0 .../active-logiset.jl | 60 +-- .../dimensional-logiset.jl | 0 .../dimensional-ontologies.jl | 0 src/other/main.jl | 17 + .../ontology.jl | 0 .../passive-dimensional-logiset.jl | 16 +- .../scalar-logiset.jl | 0 src/utils.jl | 15 + test/dimensional-logisets.jl | 64 +++ test/{datasets.jl => logisets.jl} | 4 +- test/runtests.jl | 6 +- 38 files changed, 1378 insertions(+), 1036 deletions(-) delete mode 100644 src/dimensional-logisets/dimensional-fwds.jl delete mode 100644 src/dimensional-logisets/dimensional-supports.jl delete mode 100644 src/dimensional-logisets/main.jl create mode 100644 src/logisets/dimensional-structures/dataset-bindings.jl create mode 100644 src/logisets/dimensional-structures/logiset.jl create mode 100644 src/logisets/dimensional-structures/main.jl create mode 100644 src/logisets/dimensional-structures/onestep-memosets.jl rename src/{dimensional-logisets => logisets/dimensional-structures}/representatives/Full0DFrame.jl (100%) rename src/{dimensional-logisets => logisets/dimensional-structures}/representatives/Full1DFrame+IA.jl (100%) rename src/{dimensional-logisets => logisets/dimensional-structures}/representatives/Full1DFrame+RCC.jl (100%) rename src/{dimensional-logisets => logisets/dimensional-structures}/representatives/Full1DFrame.jl (100%) rename src/{dimensional-logisets => logisets/dimensional-structures}/representatives/Full2DFrame.jl (100%) create mode 100644 src/logisets/scalar/dataset-bindings.jl rename src/logisets/scalar/{one-step-memoset.jl => onestep-memoset.jl} (87%) rename src/{dimensional-logisets => other}/_parse-dimensional-condition.jl (100%) rename src/{dimensional-logisets => other}/active-logiset.jl (83%) rename src/{dimensional-logisets => other}/dimensional-logiset.jl (100%) rename src/{dimensional-logisets => other}/dimensional-ontologies.jl (100%) create mode 100644 src/other/main.jl rename src/{dimensional-logisets => other}/ontology.jl (100%) rename src/{dimensional-logisets => other}/passive-dimensional-logiset.jl (93%) rename src/{dimensional-logisets => other}/scalar-logiset.jl (100%) create mode 100644 test/dimensional-logisets.jl rename test/{datasets.jl => logisets.jl} (97%) diff --git a/Project.toml b/Project.toml index 9a5288f..caece32 100644 --- a/Project.toml +++ b/Project.toml @@ -5,7 +5,7 @@ version = "0.1.0" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -ComputedFieldTypes = "459fdd68-db75-56b8-8c15-d717a790f88e" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" diff --git a/src/SoleModels.jl b/src/SoleModels.jl index 9ab137a..0147a2c 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -15,6 +15,8 @@ using Lazy include("utils.jl") +using .utils + export outcometype, outputtype export Rule, Branch @@ -82,42 +84,15 @@ export VarFeature, UnivariateNamedFeature, UnivariateFeature -# Definitions for logical datasets (i.e., logisets) -include("logisets/main.jl") - - -# export get_ontology, -# get_interval_ontology - -# export DimensionalLogiset, Logiset, SupportedScalarLogiset - -# include("logisets/dimensional-logisets/main.jl") +export computefeature +export scalarlogiset +export initlogiset, ninstances, max_channel_size, worldtype, dimensionality, allworlds, featvalue -# using .DimensionalDatasets: nfeatures, nrelations, -# # -# relations, -# # -# GenericModalDataset, -# AbstractLogiset, -# AbstractActiveScalarLogiset, -# DimensionalLogiset, -# Logiset, -# SupportedScalarLogiset +export ScalarMetaCondition -# using .DimensionalDatasets: AbstractWorld, AbstractRelation -# using .DimensionalDatasets: AbstractWorldSet, WorldSet -# using .DimensionalDatasets: FullDimensionalFrame -# using .DimensionalDatasets: Ontology, worldtype - -# using .DimensionalDatasets: get_ontology, -# get_interval_ontology - -# using .DimensionalDatasets: OneWorld, OneWorldOntology - -# using .DimensionalDatasets: Interval, Interval2D - -# using .DimensionalDatasets: IARelations +# Definitions for logical datasets (i.e., logisets) +include("logisets/main.jl") end diff --git a/src/dimensional-logisets/dimensional-fwds.jl b/src/dimensional-logisets/dimensional-fwds.jl deleted file mode 100644 index 716ccb5..0000000 --- a/src/dimensional-logisets/dimensional-fwds.jl +++ /dev/null @@ -1,349 +0,0 @@ -import Base: size, ndims, getindex, setindex! - -############################################################################################ -############################################################################################ -# Frame-specific FWD implementations -############################################################################################ -############################################################################################ - -abstract type AbstractUniformFullDimensionalFWD{T,N,W<:AbstractWorld,FR<:FullDimensionalFrame{N,W}} <: AbstractFeatureLookupSet{T,W,FR} end - -channel_size(fwd::AbstractUniformFullDimensionalFWD) = error("TODO add message inviting to add channel_size") -frame(fwd::AbstractUniformFullDimensionalFWD, i_instance::Integer) = FullDimensionalFrame(channel_size(fwd)) - -############################################################################################ -############################################################################################ - -struct UniformFullDimensionalFWD{ - T, - W<:AbstractWorld, - N, - D<:AbstractArray{T}, - FR<:FullDimensionalFrame{N,W}, - G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, - G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, -} <: AbstractUniformFullDimensionalFWD{T,N,W,FR} - - d :: D - - # Features - features :: Vector{FT} - - function UniformFullDimensionalFWD{T,W,N,D,FR}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{T},FR<:FullDimensionalFrame{N,W}} - new{T,W,N,D,FR}(d) - end - - function UniformFullDimensionalFWD{T,W,N}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{T}} - new{T,W,N,D,FullDimensionalFrame{N,W}}(d) - end - -############################################################################################ - - function UniformFullDimensionalFWD(X::DimensionalLogiset{T,N,W}) where {T,N,W<:OneWorld} - UniformFullDimensionalFWD{T,W,N}(Array{T,2}(undef, ninstances(X), nfeatures(X))) - end - function UniformFullDimensionalFWD(X::DimensionalLogiset{T,N,W}) where {T,N,W<:Interval} - UniformFullDimensionalFWD{T,W,N}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, ninstances(X), nfeatures(X))) - end - function UniformFullDimensionalFWD(X::DimensionalLogiset{T,N,W}) where {T,N,W<:Interval2D} - UniformFullDimensionalFWD{T,W,N}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, ninstances(X), nfeatures(X))) - end - -end - -Base.size(fwd::UniformFullDimensionalFWD, args...) = size(fwd.d, args...) -Base.ndims(fwd::UniformFullDimensionalFWD, args...) = ndims(fwd.d, args...) - -ninstances(fwd::UniformFullDimensionalFWD) = size(fwd, ndims(fwd)-1) -nfeatures(fwd::UniformFullDimensionalFWD) = size(fwd, ndims(fwd)) - -############################################################################################ - -channel_size(fwd::UniformFullDimensionalFWD{T,OneWorld}) where {T} = () -channel_size(fwd::UniformFullDimensionalFWD{T,<:Interval}) where {T} = (size(fwd, 1),) -channel_size(fwd::UniformFullDimensionalFWD{T,<:Interval2D}) where {T} = (size(fwd, 1),size(fwd, 3)) - -############################################################################################ - -function capacity(fwd::UniformFullDimensionalFWD{T,OneWorld}) where {T} - prod(size(fwd)) -end -function capacity(fwd::UniformFullDimensionalFWD{T,<:Interval}) where {T} - prod([ - ninstances(fwd), - nfeatures(fwd), - div(size(fwd, 1)*(size(fwd, 2)),2), - ]) -end -function capacity(fwd::UniformFullDimensionalFWD{T,<:Interval2D}) where {T} - prod([ - ninstances(fwd), - nfeatures(fwd), - div(size(fwd, 1)*(size(fwd, 2)),2), - div(size(fwd, 3)*(size(fwd, 4)),2), - ]) -end - -############################################################################################ - -function hasnans(fwd::UniformFullDimensionalFWD{T,OneWorld}) where {T} - any(_isnan.(fwd.d)) -end -function hasnans(fwd::UniformFullDimensionalFWD{T,<:Interval}) where {T} - any([hasnans(fwd.d[x,y,:,:]) - for x in 1:size(fwd, 1) for y in (x+1):size(fwd, 2)]) -end -function hasnans(fwd::UniformFullDimensionalFWD{T,<:Interval2D}) where {T} - any([hasnans(fwd.d[xx,xy,yx,yy,:,:]) - for xx in 1:size(fwd, 1) for xy in (xx+1):size(fwd, 2) - for yx in 1:size(fwd, 3) for yy in (yx+1):size(fwd, 4)]) -end - -############################################################################################ - -function fwd_init_world_slice(fwd::UniformFullDimensionalFWD, i_instance::Integer, w::AbstractWorld) - nothing -end - -############################################################################################ - -@inline function Base.getindex( - fwd :: UniformFullDimensionalFWD{T,OneWorld}, - i_instance :: Integer, - w :: OneWorld, - i_feature :: Integer -) where {T} - fwd.d[i_instance, i_feature] -end - -@inline function Base.getindex( - fwd :: UniformFullDimensionalFWD{T,<:Interval}, - i_instance :: Integer, - w :: Interval, - i_feature :: Integer -) where {T} - fwd.d[w.x, w.y, i_instance, i_feature] -end - -@inline function Base.getindex( - fwd :: UniformFullDimensionalFWD{T,<:Interval2D}, - i_instance :: Integer, - w :: Interval2D, - i_feature :: Integer -) where {T} - fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] -end - -############################################################################################ - -@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,OneWorld}, threshold::T, w::OneWorld, i_instance::Integer, i_feature::Integer) where {T} - fwd.d[i_instance, i_feature] = threshold -end - -@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,<:Interval}, threshold::T, w::Interval, i_instance::Integer, i_feature::Integer) where {T} - fwd.d[w.x, w.y, i_instance, i_feature] = threshold -end - -@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, threshold::T, w::Interval2D, i_instance::Integer, i_feature::Integer) where {T} - fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] = threshold -end - -############################################################################################ - -function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:OneWorld,N} - UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[inds,:] else fwd.d[inds,:] end) -end - -function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:Interval,N} - UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[:,:,inds,:] else fwd.d[:,:,inds,:] end) -end - -function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:Interval2D,N} - UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[:,:,:,:,inds,:] else fwd.d[:,:,:,:,inds,:] end) -end - -############################################################################################ - -# TODO fix - -Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimensionalFWD{T,OneWorld}, i_feature::Integer, fwdslice::Array{T,1}) where {T} - fwd.d[:, i_feature] = fwdslice -end - -Base.@propagate_inbounds @inline featchannel(fwd::UniformFullDimensionalFWD{T,OneWorld}, i_instance::Integer, i_feature::Integer) where {T} = - fwd.d[i_instance, i_feature] -const OneWorldFeaturedChannel{T} = T -readfeature(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc - -Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimensionalFWD{T,<:Interval}, i_feature::Integer, fwdslice::Array{T,3}) where {T} - fwd.d[:, :, :, i_feature] = fwdslice -end -Base.@propagate_inbounds @inline featchannel(fwd::UniformFullDimensionalFWD{T,<:Interval}, i_instance::Integer, i_feature::Integer) where {T} = - @views fwd.d[:,:,i_instance, i_feature] -const IntervalFeaturedChannel{T} = AbstractArray{T,2} -readfeature(fwc::IntervalFeaturedChannel{T}, w::Interval) where {T} = - fwc[w.x, w.y] - - -Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, i_feature::Integer, fwdslice::Array{T,5}) where {T} - fwd.d[:, :, :, :, :, i_feature] = fwdslice -end -Base.@propagate_inbounds @inline featchannel(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, i_instance::Integer, i_feature::Integer) where {T} = - @views fwd.d[:,:,:,:,i_instance, i_feature] -const Interval2DFeaturedChannel{T} = AbstractArray{T,4} -readfeature(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) where {T} = - fwc[w.x.x, w.x.y, w.y.x, w.y.y] - -const FWDFeatureSlice{T} = Union{ - # FWDFeatureSlice(DimensionalLogiset{T where T,0,OneWorld}) - T, # Note: should be, but it throws error OneWorldFeaturedChannel{T}, - IntervalFeaturedChannel{T}, - Interval2DFeaturedChannel{T}, - # FWDFeatureSlice(DimensionalLogiset{T where T,2,<:Interval2D}) -} - - -############################################################################################ - - -# channel_size(fwd::OneWorldFWD) = () - -# ninstances(fwd::OneWorldFWD) = size(fwd.d, 1) -# nfeatures(fwd::OneWorldFWD) = size(fwd.d, 2) - -# function fwd_init(::Type{OneWorldFWD}, X::DimensionalLogiset{T,0,OneWorld}) where {T} -# OneWorldFWD{T}(Array{T,2}(undef, ninstances(X), nfeatures(X))) -# end - -# function fwd_init_world_slice(fwd::OneWorldFWD, i_instance::Integer, w::AbstractWorld) -# nothing -# end - -# hasnans(fwd::OneWorldFWD) = any(_isnan.(fwd.d)) - -# Base.@propagate_inbounds @inline featvalue( -# fwd :: OneWorldFWD{T}, -# i_instance :: Integer, -# w :: OneWorld, -# i_feature :: Integer) where {T} = fwd.d[i_instance, i_feature] - -# @inline function Base.setindex!(fwd::OneWorldFWD{T},, threshold::T w::OneWorld, i_instance::Integer, i_feature::Integer) where {T} -# fwd.d[i_instance, i_feature] = threshold -# end - -# function instances(fwd::OneWorldFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# OneWorldFWD{T}(if return_view == Val(true) @view fwd.d[inds,:] else fwd.d[inds,:] end) -# end - -# Base.@propagate_inbounds @inline function fwdslice_set(fwd::OneWorldFWD{T}, i_feature::Integer, fwdslice::Array{T,1}) where {T} -# fwd.d[:, i_feature] = fwdslice -# end - -# Base.@propagate_inbounds @inline featchannel(fwd::OneWorldFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = -# fwd.d[i_instance, i_feature] -# const OneWorldFeaturedChannel{T} = T -# readfeature(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc - -############################################################################################ -# FWD, Interval: 4D array (x × y × ninstances × nfeatures) -############################################################################################ - -# struct IntervalFWD{T} <: UniformFullDimensionalFWD{T,1,<:Interval} -# d :: Array{T,4} -# end - -# channel_size(fwd::IntervalFWD) = (size(fwd, 1),) - -# ninstances(fwd::IntervalFWD) = size(fwd, 3) -# nfeatures(fwd::IntervalFWD) = size(fwd, 4) - -# function fwd_init(::Type{IntervalFWD}, X::DimensionalLogiset{T,1,<:Interval}) where {T} -# IntervalFWD{T}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, ninstances(X), nfeatures(X))) -# end - -# function fwd_init_world_slice(fwd::IntervalFWD, i_instance::Integer, w::AbstractWorld) -# nothing -# end - -# function hasnans(fwd::IntervalFWD) -# # @show ([hasnans(fwd.d[x,y,:,:]) for x in 1:size(fwd, 1) for y in (x+1):size(fwd, 2)]) -# any([hasnans(fwd.d[x,y,:,:]) for x in 1:size(fwd, 1) for y in (x+1):size(fwd, 2)]) -# end - -# Base.@propagate_inbounds @inline featvalue( -# fwd :: IntervalFWD{T}, -# i_instance :: Integer, -# w :: Interval, -# i_feature :: Integer) where {T} = fwd.d[w.x, w.y, i_instance, i_feature] - -# @inline function Base.setindex!(fwd::IntervalFWD{T},, threshold::T w::Interval, i_instance::Integer, i_feature::Integer) where {T} -# fwd.d[w.x, w.y, i_instance, i_feature] = threshold -# end - -# Base.@propagate_inbounds @inline function fwdslice_set(fwd::IntervalFWD{T}, i_feature::Integer, fwdslice::Array{T,3}) where {T} -# fwd.d[:, :, :, i_feature] = fwdslice -# end - -# function instances(fwd::IntervalFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# IntervalFWD{T}(if return_view == Val(true) @view fwd.d[:,:,inds,:] else fwd.d[:,:,inds,:] end) -# end -# Base.@propagate_inbounds @inline featchannel(fwd::IntervalFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = -# @views fwd.d[:,:,i_instance, i_feature] -# const IntervalFeaturedChannel{T} = AbstractArray{T,2} -# readfeature(fwc::IntervalFeaturedChannel{T}, w::Interval) where {T} = -# fwc[w.x, w.y] - -############################################################################################ -# FWD, Interval: 6D array (x.x × x.y × y.x × y.y × ninstances × nfeatures) -############################################################################################ - -# struct Interval2DFWD{T} <: UniformFullDimensionalFWD{T,2,<:Interval2D} -# d :: Array{T,6} -# end - -# channel_size(fwd::Interval2DFWD) = (size(fwd, 1),size(fwd, 3)) - -# ninstances(fwd::Interval2DFWD) = size(fwd, 5) -# nfeatures(fwd::Interval2DFWD) = size(fwd, 6) - - -# function fwd_init(::Type{Interval2DFWD}, X::DimensionalLogiset{T,2,<:Interval2D}) where {T} -# Interval2DFWD{T}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, ninstances(X), nfeatures(X))) -# end - -# function fwd_init_world_slice(fwd::Interval2DFWD, i_instance::Integer, w::AbstractWorld) -# nothing -# end - -# function hasnans(fwd::Interval2DFWD) -# # @show ([hasnans(fwd.d[xx,xy,yx,yy,:,:]) for xx in 1:size(fwd, 1) for xy in (xx+1):size(fwd, 2) for yx in 1:size(fwd, 3) for yy in (yx+1):size(fwd, 4)]) -# any([hasnans(fwd.d[xx,xy,yx,yy,:,:]) for xx in 1:size(fwd, 1) for xy in (xx+1):size(fwd, 2) for yx in 1:size(fwd, 3) for yy in (yx+1):size(fwd, 4)]) -# end - -# Base.@propagate_inbounds @inline featvalue( -# fwd :: Interval2DFWD{T}, -# i_instance :: Integer, -# w :: Interval2D, -# i_feature :: Integer) where {T} = fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] - -# @inline function Base.setindex!(fwd::Interval2DFWD{T},, threshold::T w::Interval2D, i_instance::Integer, i_feature::Integer) where {T} -# fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] = threshold -# end - -# Base.@propagate_inbounds @inline function fwdslice_set(fwd::Interval2DFWD{T}, i_feature::Integer, fwdslice::Array{T,5}) where {T} -# fwd.d[:, :, :, :, :, i_feature] = fwdslice -# end - -# function instances(fwd::Interval2DFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# Interval2DFWD{T}(if return_view == Val(true) @view fwd.d[:,:,:,:,inds,:] else fwd.d[:,:,:,:,inds,:] end) -# end -# Base.@propagate_inbounds @inline featchannel(fwd::Interval2DFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = -# @views fwd.d[:,:,:,:,i_instance, i_feature] -# const Interval2DFeaturedChannel{T} = AbstractArray{T,4} -# readfeature(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) where {T} = -# fwc[w.x.x, w.x.y, w.y.x, w.y.y] - -############################################################################################ - -############################################################################################ -############################################################################################ diff --git a/src/dimensional-logisets/dimensional-supports.jl b/src/dimensional-logisets/dimensional-supports.jl deleted file mode 100644 index fd82e3f..0000000 --- a/src/dimensional-logisets/dimensional-supports.jl +++ /dev/null @@ -1,420 +0,0 @@ -import Base: size, ndims, getindex, setindex! - -############################################################################################ -############################################################################################ -# Frame-specific FWD supports implementations -############################################################################################ -############################################################################################ - -abstract type AbstractUniformFullDimensionalRelationalSupport{T,W<:AbstractWorld,FR<:AbstractFrame{W}} <: AbstractScalarOneStepRelationalMemoset{T,W,FR} end - -# TODO switch from nothing to missing? -# usesmemo(fwd_rs::AbstractUniformFullDimensionalRelationalSupport) = Nothing <: Base.eltype(fwd_rs.d) -capacity(fwd_rs::AbstractUniformFullDimensionalRelationalSupport) = - error("Please, provide method capacity(...).") -nmemoizedvalues(support::AbstractUniformFullDimensionalRelationalSupport) = (capacity(support) - count(isnothing, support.d)) - -############################################################################################ -# FWD relational support for uniform full dimensional frames: -# a (ninstances × nfeatsnaggrs × nrelations) structure for each world. -# Each world is linearized, resulting in a (3+N*2)-D array -############################################################################################ - -struct UniformFullDimensionalRelationalSupport{ - T, - W<:AbstractWorld, - N, - D<:AbstractArray{TT} where TT<:Union{T,Nothing}, -} <: AbstractUniformFullDimensionalRelationalSupport{T,W,FullDimensionalFrame{N,W,Bool}} - - d :: D - - # Test operators associated with each feature, grouped by their respective aggregator - grouped_featsaggrsnops :: G1 - - grouped_featsnaggrs :: G2 - - function UniformFullDimensionalRelationalSupport{T,W,N,D}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{TT} where TT<:Union{T,Nothing}} - new{T,W,N,D}(d) - end - - function UniformFullDimensionalRelationalSupport{T,W,N}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{TT} where TT<:Union{T,Nothing}} - UniformFullDimensionalRelationalSupport{T,W,N,D}(d) - end - - function UniformFullDimensionalRelationalSupport( - fwd::UniformFullDimensionalFWD{T,W,0}, - nfeatsnaggrs::Integer, - nrelations::Integer, - perform_initialization::Bool = false, - ) where {T,W<:OneWorld} - # error("TODO actually, using a relational or a global support with a OneWorld frame makes no sense. Figure out what to do here!") - _fwd_rs = begin - if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 3}(undef, ninstances(fwd), nfeatsnaggrs, nrelations) - fill!(_fwd_rs, nothing) - else - Array{T,3}(undef, ninstances(fwd), nfeatsnaggrs, nrelations) - end - end - UniformFullDimensionalRelationalSupport{T,W,0,typeof(_fwd_rs)}(_fwd_rs) - end - function UniformFullDimensionalRelationalSupport( - fwd::UniformFullDimensionalFWD{T,W,1}, - nfeatsnaggrs::Integer, - nrelations::Integer, - perform_initialization::Bool = false, - ) where {T,W<:Interval} - _fwd_rs = begin - if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 5}(undef, size(fwd, 1), size(fwd, 2), ninstances(fwd), nfeatsnaggrs, nrelations) - fill!(_fwd_rs, nothing) - else - Array{T,5}(undef, size(fwd, 1), size(fwd, 2), ninstances(fwd), nfeatsnaggrs, nrelations) - end - end - UniformFullDimensionalRelationalSupport{T,W,1,typeof(_fwd_rs)}(_fwd_rs) - end - function UniformFullDimensionalRelationalSupport( - fwd::UniformFullDimensionalFWD{T,W,2}, - nfeatsnaggrs::Integer, - nrelations::Integer, - perform_initialization::Bool = false, - ) where {T,W<:Interval2D} - _fwd_rs = begin - if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), ninstances(fwd), nfeatsnaggrs, nrelations) - fill!(_fwd_rs, nothing) - else - Array{T,7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), ninstances(fwd), nfeatsnaggrs, nrelations) - end - end - UniformFullDimensionalRelationalSupport{T,W,2,typeof(_fwd_rs)}(_fwd_rs) - end - - function UniformFullDimensionalRelationalSupport( - fd::Logiset, - perform_initialization::Bool = false, - ) - UniformFullDimensionalRelationalSupport(fwd(fd), nfeatsnaggrs(fd), nrelations(fd), perform_initialization) - end - -end - -Base.size(support::UniformFullDimensionalRelationalSupport, args...) = size(support.d, args...) -Base.ndims(support::UniformFullDimensionalRelationalSupport, args...) = ndims(support.d, args...) - -ninstances(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)-2) -nfeatsnaggrs(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)-1) -nrelations(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)) - -############################################################################################ - -function capacity(support::UniformFullDimensionalRelationalSupport{T,OneWorld}) where {T} - prod(size(support)) -end -function capacity(support::UniformFullDimensionalRelationalSupport{T,<:Interval}) where {T} - prod([ - ninstances(support), - nfeatsnaggrs(support), - nrelations(support), - div(size(support, 1)*(size(support, 2)),2), - ]) -end -function capacity(support::UniformFullDimensionalRelationalSupport{T,<:Interval2D}) where {T} - prod([ - ninstances(support), - nfeatsnaggrs(support), - nrelations(support), - div(size(support, 1)*(size(support, 2)),2), - div(size(support, 3)*(size(support, 4)),2), - ]) -end - -############################################################################################ - -function hasnans(support::UniformFullDimensionalRelationalSupport{T,OneWorld}) where {T} - any(_isnan.(support.d)) -end -function hasnans(support::UniformFullDimensionalRelationalSupport{T,<:Interval}) where {T} - any([hasnans(support.d[x,y,:,:,:]) - for x in 1:size(support, 1) for y in (x+1):size(support, 2)]) -end -function hasnans(support::UniformFullDimensionalRelationalSupport{T,<:Interval2D}) where {T} - any([hasnans(support.d[xx,xy,yx,yy,:,:,:]) - for xx in 1:size(support, 1) for xy in (xx+1):size(support, 2) - for yx in 1:size(support, 3) for yy in (yx+1):size(support, 4)]) -end - -############################################################################################ - -function fwd_rs_init_world_slice( - support::UniformFullDimensionalRelationalSupport, - i_instance::Integer, - i_featsnaggr::Integer, - i_relation::Integer -) - nothing -end - -############################################################################################ -############################################################################################ -############################################################################################ - -@inline function Base.getindex( - support :: UniformFullDimensionalRelationalSupport{T,W}, - i_instance :: Integer, - w :: W, - i_featsnaggr :: Integer, - i_relation :: Integer -) where {T,W<:OneWorld} - support.d[i_instance, i_featsnaggr, i_relation] -end -@inline function Base.getindex( - support :: UniformFullDimensionalRelationalSupport{T,W}, - i_instance :: Integer, - w :: W, - i_featsnaggr :: Integer, - i_relation :: Integer -) where {T,W<:Interval} - support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] -end -@inline function Base.getindex( - support :: UniformFullDimensionalRelationalSupport{T,W}, - i_instance :: Integer, - w :: W, - i_featsnaggr :: Integer, - i_relation :: Integer -) where {T,W<:Interval2D} - support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] -end - -############################################################################################ - -Base.@propagate_inbounds @inline function Base.setindex!( - support::UniformFullDimensionalRelationalSupport{T,OneWorld}, - threshold::T, - i_instance::Integer, - w::OneWorld, - i_featsnaggr::Integer, - i_relation::Integer, -) where {T} - support.d[i_instance, i_featsnaggr, i_relation] = threshold -end - -Base.@propagate_inbounds @inline function Base.setindex!( - support::UniformFullDimensionalRelationalSupport{T,<:Interval}, - threshold::T, - i_instance::Integer, - w::Interval, - i_featsnaggr::Integer, - i_relation::Integer, -) where {T} - support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] = threshold -end - -Base.@propagate_inbounds @inline function Base.setindex!( - support::UniformFullDimensionalRelationalSupport{T,<:Interval2D}, - threshold::T, - i_instance::Integer, - w::Interval2D, - i_featsnaggr::Integer, - i_relation::Integer, -) where {T} - support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] = threshold -end - -############################################################################################ - -function instances( - support::UniformFullDimensionalRelationalSupport{T,W,N}, - inds::AbstractVector{<:Integer}, - return_view::Union{Val{true},Val{false}} = Val(false) -) where {T,W<:OneWorld,N} - UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) -end -function instances( - support::UniformFullDimensionalRelationalSupport{T,W,N}, - inds::AbstractVector{<:Integer}, - return_view::Union{Val{true},Val{false}} = Val(false) -) where {T,W<:Interval,N} - UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) -end -function instances( - support::UniformFullDimensionalRelationalSupport{T,W,N}, - inds::AbstractVector{<:Integer}, - return_view::Union{Val{true},Val{false}} = Val(false) -) where {T,W<:Interval2D,N} - UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[:,:,:,:,inds,:,:] else support.d[:,:,:,:,inds,:,:] end) -end - -############################################################################################ -# FWD support, OneWorld: 3D array (ninstances × nfeatsnaggrs × nrelations) -############################################################################################ - -# struct OneWorldFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,OneWorld} -# d :: Array{T,3} -# end - -# ninstances(support::OneWorldFWD_RS) = size(support, 1) -# nfeatsnaggrs(support::OneWorldFWD_RS) = size(support, 2) -# nrelations(support::OneWorldFWD_RS) = size(support, 3) -# capacity(support::OneWorldFWD_RS) = prod(size(support.d)) - -# @inline Base.getindex( -# support :: OneWorldFWD_RS{T}, -# i_instance :: Integer, -# w :: OneWorld, -# i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[i_instance, i_featsnaggr, i_relation] -# Base.size(support::OneWorldFWD_RS, args...) = size(support.d, args...) - -# hasnans(support::OneWorldFWD_RS) = any(_isnan.(support.d)) - -# function fwd_rs_init(fd::Logiset{T,OneWorld}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} -# if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 3}(undef, ninstances(fd), nfeatsnaggrs, nrelations), nothing) -# OneWorldFWD_RS{Union{T,Nothing}}(_fwd_rs) -# else -# _fwd_rs = Array{T,3}(undef, ninstances(fd), nfeatsnaggrs, nrelations) -# OneWorldFWD_RS{T}(_fwd_rs) -# end -# end -# fwd_rs_init_world_slice(support::OneWorldFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = -# nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::OneWorldFWD_RS{T}, threshold::T, i_instance::Integer, w::OneWorld, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[i_instance, i_featsnaggr, i_relation] = threshold -# function instances(support::OneWorldFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# OneWorldFWD_RS{T}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) -# end - -############################################################################################ -# FWD support, Interval: 5D array (x × y × ninstances × nfeatsnaggrs × nrelations) -############################################################################################ - - -# struct IntervalFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,<:Interval} -# d :: Array{T,5} -# end - -# ninstances(support::IntervalFWD_RS) = size(support, 3) -# nfeatsnaggrs(support::IntervalFWD_RS) = size(support, 4) -# nrelations(support::IntervalFWD_RS) = size(support, 5) -# capacity(support::IntervalFWD_RS) = -# prod([ninstances(support), nfeatsnaggrs(support), nrelations(support), div(size(support.d, 1)*(size(support.d, 1)+1),2)]) - -# @inline Base.getindex( -# support :: IntervalFWD_RS{T}, -# i_instance :: Integer, -# w :: Interval, -# i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] -# Base.size(support::IntervalFWD_RS, args...) = size(support.d, args...) - - -# function hasnans(support::IntervalFWD_RS) -# # @show [hasnans(support.d[x,y,:,:,:]) for x in 1:size(support.d, 1) for y in (x+1):size(support.d, 2)] -# any([hasnans(support.d[x,y,:,:,:]) for x in 1:size(support.d, 1) for y in (x+1):size(support.d, 2)]) -# end - -# function fwd_rs_init(fd::Logiset{T,<:Interval}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} -# _fwd = fd.fwd -# if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, size(_fwd, 1), size(_fwd, 2), ninstances(fd), nfeatsnaggrs, nrelations), nothing) -# IntervalFWD_RS{Union{T,Nothing}}(_fwd_rs) -# else -# _fwd_rs = Array{T,5}(undef, size(_fwd, 1), size(_fwd, 2), ninstances(fd), nfeatsnaggrs, nrelations) -# IntervalFWD_RS{T}(_fwd_rs) -# end -# end -# fwd_rs_init_world_slice(support::IntervalFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = -# nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::IntervalFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] = threshold -# function instances(support::IntervalFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# IntervalFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) -# end - -############################################################################################ -# FWD support, Interval2D: 7D array (x.x × x.y × y.x × y.y × ninstances × nfeatsnaggrs × nrelations) -############################################################################################ - -# struct Interval2DFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,<:Interval2D} -# d :: Array{T,7} -# end - -# ninstances(support::Interval2DFWD_RS) = size(support, 5) -# nfeatsnaggrs(support::Interval2DFWD_RS) = size(support, 6) -# nrelations(support::Interval2DFWD_RS) = size(support, 7) -# @inline Base.getindex( -# support :: Interval2DFWD_RS{T}, -# i_instance :: Integer, -# w :: Interval2D, -# i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] -# size(support::Interval2DFWD_RS) = size(support.d, args...) - -# TODO... hasnans(support::Interval2DFWD_RS) = any(_isnan.(support.d)) -# TODO...? hasnans(support::Interval2DFWD_RS) = any([hasnans(support.d[xx,xy,yx,yy,:,:,:]) for xx in 1:size(support.d, 1) for xy in (xx+1):size(support.d, 2) for yx in 1:size(support.d, 3) for yy in (yx+1):size(support.d, 4)]) - -# fwd_rs_init(fd::Logiset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} = begin -# _fwd = fd.fwd -# if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), ninstances(fd), nfeatsnaggrs, nrelations), nothing) -# Interval2DFWD_RS{Union{T,Nothing}}(_fwd_rs) -# else -# _fwd_rs = Array{T,7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), ninstances(fd), nfeatsnaggrs, nrelations) -# Interval2DFWD_RS{T}(_fwd_rs) -# end -# end -# fwd_rs_init_world_slice(support::Interval2DFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = -# nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] = threshold -# function instances(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# Interval2DFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,:,:,inds,:,:] else support.d[:,:,:,:,inds,:,:] end) -# end - - -############################################################################################ -# FWD support, Interval2D: 7D array (linearized(x) × linearized(y) × ninstances × nfeatsnaggrs × nrelations) -############################################################################################ - -# # TODO rewrite - -# struct Interval2DFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,<:Interval2D} -# d :: Array{T,5} -# end - -# ninstances(support::Interval2DFWD_RS) = size(support, 3) -# nfeatsnaggrs(support::Interval2DFWD_RS) = size(support, 4) -# nrelations(support::Interval2DFWD_RS) = size(support, 5) -# capacity(support::Interval2DFWD_RS) = prod(size(support.d)) - -# @inline Base.getindex( -# support :: Interval2DFWD_RS{T}, -# i_instance :: Integer, -# w :: Interval2D, -# i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[w.x.x+div((w.x.y-2)*(w.x.y-1),2), w.y.x+div((w.y.y-2)*(w.y.y-1),2), i_instance, i_featsnaggr, i_relation] -# Base.size(support::Interval2DFWD_RS, args...) = size(support.d, args...) - -# hasnans(support::Interval2DFWD_RS) = any(_isnan.(support.d)) - -# function fwd_rs_init(fd::Logiset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} -# _fwd = fd.fwd -# if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), ninstances(fd), nfeatsnaggrs, nrelations), nothing) -# Interval2DFWD_RS{Union{T,Nothing}}(_fwd_rs) -# else -# _fwd_rs = Array{T,5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), ninstances(fd), nfeatsnaggrs, nrelations) -# Interval2DFWD_RS{T}(_fwd_rs) -# end -# end -# fwd_rs_init_world_slice(support::Interval2DFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = -# nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[w.x.x+div((w.x.y-2)*(w.x.y-1),2), w.y.x+div((w.y.y-2)*(w.y.y-1),2), i_instance, i_featsnaggr, i_relation] = threshold -# function instances(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# Interval2DFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) -# end diff --git a/src/dimensional-logisets/main.jl b/src/dimensional-logisets/main.jl deleted file mode 100644 index cd89bb9..0000000 --- a/src/dimensional-logisets/main.jl +++ /dev/null @@ -1,68 +0,0 @@ -module DimensionalDatasets - - -import Base: size, show, getindex, iterate, length, push!, eltype - -using BenchmarkTools -using ComputedFieldTypes -using DataStructures -using ThreadSafeDicts -using ProgressMeter - -using SoleBase - -using SoleLogics -using SoleLogics: AbstractFormula, AbstractWorld, AbstractRelation -using SoleLogics: AbstractFrame, AbstractDimensionalFrame, FullDimensionalFrame -import SoleLogics: worldtype, accessibles, allworlds, alphabet, initialworld - -using SoleData -import SoleData: _isnan, hasnans, nvariables, max_channel_size, channel_size -import SoleData: instance, get_instance, slicedataset, instances -import SoleData: dimensionality - -############################################################################################ - -function check_initialworld(FD::Type{<:AbstractLogiset}, initialworld, W) - @assert isnothing(initialworld) || initialworld isa W "Cannot instantiate " * - "$(FD) with worldtype = $(W) but initialworld of type $(typeof(initialworld))." -end - -using SoleModels.utils - -# # Dataset structures -include("passive-dimensional-logiset.jl") - -include("dimensional-logiset.jl") - -# Frame-specific featured world datasets and supports -include("dimensional-fwds.jl") - -include("dimensional-supports.jl") - -# export parsecondition - -# # Conditions on features for dimensional datasets -# include("_parse-dimensional-condition.jl") - -# # Concrete type for ontologies -# include("ontology.jl") # TODO frame inside the ontology? - -# export DimensionalLogiset, Logiset, SupportedScalarLogiset - -# const GenericModalDataset = Union{AbstractDimensionalDataset,AbstractLogiset,MultiLogiset} - -# # Dimensional ontologies -# include("dimensional-ontologies.jl") - -using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame -using SoleLogics: X, Y, Z - -# Representatives for dimensional frames -include("representatives/Full0DFrame.jl") -include("representatives/Full1DFrame.jl") -include("representatives/Full1DFrame+IA.jl") -include("representatives/Full1DFrame+RCC.jl") -include("representatives/Full2DFrame.jl") - -end diff --git a/src/logisets/check.jl b/src/logisets/check.jl index 22e5880..404d7e9 100644 --- a/src/logisets/check.jl +++ b/src/logisets/check.jl @@ -27,6 +27,7 @@ function check( use_memo::Union{Nothing,AbstractMemoset{W},AbstractVector{<:AbstractDict{<:FT,<:WorldSet}}} = nothing, perform_normalization::Bool = true, memo_max_height::Union{Nothing,Int} = nothing, + onestep_memoset_is_complete = false, ) where {W<:AbstractWorld,U,FT<:SoleLogics.AbstractFormula} if isnothing(w) @@ -80,6 +81,10 @@ function check( fr = frame(X, i_instance) + # TODO try lazily + (_f, _c) = filter, collect + # (_f, _c) = Iterators.filter, identity + if !hasformula(memo_structure, φ) for ψ in unique(SoleLogics.subformulas(φ)) if !isnothing(memo_max_height) && height(ψ) > memo_max_height @@ -92,27 +97,31 @@ function check( worldset = begin if !isnothing(onestep_memoset) && SoleLogics.height(ψ) == 1 && tok isa SoleLogics.AbstractRelationalOperator && SoleLogics.ismodal(tok) && SoleLogics.isunary(tok) && SoleLogics.isdiamond(tok) && - token(first(children(ψ))) isa Proposition + token(first(children(ψ))) isa Proposition && + # Note: metacond with same aggregator also works. TODO maybe use Conditions with aggregators inside and look those up. + (onestep_memoset_is_complete || metacond(atom(token(first(children(ψ))))) in metaconditions(onestep_memoset)) && + true # println("ONESTEP!") # println(syntaxstring(ψ)) - _rel = SoleLogics.relation(tok) condition = atom(token(first(children(ψ)))) _metacond = metacond(condition) + _rel = SoleLogics.relation(tok) _feature = feature(condition) _featchannel = featchannel(X, i_instance, _feature) - filter(world->begin + _f(world->begin gamma = featchannel_onestep_aggregation(X, onestep_memoset, _featchannel, i_instance, world, _rel, _metacond) apply_test_operator(test_operator(_metacond), gamma, threshold(condition)) - end, allworlds(fr)) + end, _c(allworlds(fr))) elseif tok isa SoleLogics.AbstractOperator - collect(SoleLogics.collateworlds(fr, tok, map(f->readformula(memo_structure, f), children(ψ)))) + _c(SoleLogics.collateworlds(fr, tok, map(f->readformula(memo_structure, f), children(ψ)))) elseif tok isa Proposition - filter(w->check(tok, X, i_instance, w), collect(allworlds(fr))) + condition = atom(tok) + _f(_w->checkcondition(condition, X, i_instance, _w), _c(allworlds(fr))) else error("Unexpected token encountered in _check: $(typeof(tok))") end end - setformula(memo_structure, ψ, worldset) + setformula(memo_structure, ψ, Vector{W}(worldset)) end # @show syntaxstring(ψ), readformula(memo_structure, ψ) end diff --git a/src/logisets/dimensional-structures/dataset-bindings.jl b/src/logisets/dimensional-structures/dataset-bindings.jl new file mode 100644 index 0000000..4e54471 --- /dev/null +++ b/src/logisets/dimensional-structures/dataset-bindings.jl @@ -0,0 +1,77 @@ +using SoleData: AbstractDimensionalDataset, + AbstractDimensionalInstance, + AbstractDimensionalChannel, + UniformDimensionalDataset, + DimensionalInstance, + DimensionalChannel + +function initlogiset( + dataset::AbstractDimensionalDataset, + features::AbstractVector{<:VarFeature}, +) + _ninstances = ninstances(dataset) + _max_channel_size = max_channel_size(dataset) + W = worldtype(dataset) + N = dimensionality(dataset) + + features = UniqueVector(features) + nfeatures = length(features) + + U = Union{featvaltype.(features)...} + featstruct = Array{U,length(_max_channel_size)*2+2}( + undef, + vcat([[s,s+1] for s in _max_channel_size]...)..., + _ninstances, + length(features) + ) + return UniformFullDimensionalLogiset{U,W,N}(featstruct, features) +end + +function featchannel( + X::AbstractDimensionalDataset, + i_instance::Integer, + f::AbstractFeature, +) + get_instance(X, i_instance) +end + +function readfeature( + X::AbstractDimensionalDataset, + featchannel::Any, + w::W, + f::VarFeature{U}, +) where {U,W<:AbstractWorld} + w_values = interpret_world(w, featchannel) + computefeature(f, w_values)::U +end + +interpret_world(::OneWorld, instance::DimensionalInstance{T,1}) where {T} = instance +interpret_world(w::Interval2D, instance::DimensionalInstance{T,3}) where {T} = instance[w.x.x:w.x.y-1,w.y.x:w.y.y-1,:] +interpret_world(w::Interval, instance::DimensionalInstance{T,2}) where {T} = instance[w.x:w.y-1,:] + +function featvalue( + X::AbstractDimensionalDataset, + i_instance::Integer, + w::W, + feature::AbstractFeature, +) where {W<:AbstractWorld} + readfeature(X, featchannel(X, i_instance, feature), w, feature) +end + +function featvalue( + f::AbstractFeature, + X::AbstractDimensionalDataset, + i_instance::Integer, + w::W, +) where {W<:AbstractWorld} + featvalue(X, i_instance, w, f) +end + +worldtype(d::AbstractDimensionalDataset{T,2}) where {T} = OneWorld +worldtype(d::AbstractDimensionalDataset{T,3}) where {T} = Interval{Int} +worldtype(d::AbstractDimensionalDataset{T,4}) where {T} = Interval2D{Int} + +frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance::Integer) = frame(X) +frame(X::Union{UniformDimensionalDataset,AbstractArray}) = FullDimensionalFrame(channel_size(X)) + +allworlds(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance::Integer) = allworlds(frame(X, i_instance)) diff --git a/src/logisets/dimensional-structures/logiset.jl b/src/logisets/dimensional-structures/logiset.jl new file mode 100644 index 0000000..a39e180 --- /dev/null +++ b/src/logisets/dimensional-structures/logiset.jl @@ -0,0 +1,448 @@ +import Base: size, ndims, getindex, setindex! + +""" +Abstract type for optimized, uniform logisets with +full dimensional frames. + +See also +[`UniformFullDimensionalLogiset`](@ref), +[`FullDimensionalFrame`](@ref), +[`AbstractLogiset`](@ref). +""" +abstract type AbstractUniformFullDimensionalLogiset{U,N,W<:AbstractWorld,FT<:AbstractFeature,FR<:FullDimensionalFrame{N,W}} <: AbstractLogiset{W,U,FT,FR} end + +function channel_size(X::AbstractUniformFullDimensionalLogiset) + error("Please, provide method channel_size(::$(typeof(X))).") +end + +function dimensionality(X::AbstractUniformFullDimensionalLogiset{U,N}) where {U,N} + N +end + +frame(X::AbstractUniformFullDimensionalLogiset, i_instance::Integer) = FullDimensionalFrame(channel_size(X)) + +############################################################################################ + +""" +Uniform scalar logiset with +full dimensional frames of dimensionality `N`, storing values for each world in +a `ninstances` × `nfeatures` array. +Each world is a hyper-interval, and its `N*2` components are used to index different array +dimensions, ultimately resulting in a `(N*2+2)`-dimensional array. + +See also +[`AbstractUniformFullDimensionalLogiset`](@ref), +[`FullDimensionalFrame`](@ref), +[`AbstractLogiset`](@ref). +""" +struct UniformFullDimensionalLogiset{ + U, + W<:AbstractWorld, + N, + D<:AbstractArray{U}, + FT<:AbstractFeature, + FR<:FullDimensionalFrame{N,W}, +} <: AbstractUniformFullDimensionalLogiset{U,N,W,FT,FR} + + # Multi-dimensional structure + featstruct :: D + + # Features + features :: UniqueVector{FT} + + function UniformFullDimensionalLogiset{U,W,N,D,FT,FR}( + featstruct::D, + features::AbstractVector{FT}, + ) where {U,W<:AbstractWorld,N,D<:AbstractArray{U},FT<:AbstractFeature,FR<:FullDimensionalFrame{N,W}} + features = UniqueVector(features) + new{U,W,N,D,FT,FR}(featstruct, features) + end + + function UniformFullDimensionalLogiset{U,W,N}( + featstruct::D, + features::AbstractVector{FT}, + ) where {U,W<:AbstractWorld,N,D<:AbstractArray{U},FT<:AbstractFeature} + UniformFullDimensionalLogiset{U,W,N,D,FT,FullDimensionalFrame{N,W}}(featstruct, features) + end + + # function UniformFullDimensionalLogiset( + # dataset::Any, + # features::AbstractVector{<:VarFeature}, + # ) + # U = Union{featvaltype.(features)...} + # UniformFullDimensionalLogiset{U}(dataset, features) + # end + +end + +Base.size(X::UniformFullDimensionalLogiset, args...) = size(X.featstruct, args...) +Base.ndims(X::UniformFullDimensionalLogiset, args...) = ndims(X.featstruct, args...) + +ninstances(X::UniformFullDimensionalLogiset) = size(X, ndims(X)-1) +nfeatures(X::UniformFullDimensionalLogiset) = size(X, ndims(X)) + +features(X::UniformFullDimensionalLogiset) = X.features + +############################################################################################ + +channel_size(X::UniformFullDimensionalLogiset{U,OneWorld}) where {U} = () +channel_size(X::UniformFullDimensionalLogiset{U,<:Interval}) where {U} = (size(X, 1),) +channel_size(X::UniformFullDimensionalLogiset{U,<:Interval2D}) where {U} = (size(X, 1),size(X, 3)) + +############################################################################################ + + +Base.@propagate_inbounds @inline function featchannel( + X::UniformFullDimensionalLogiset{U,OneWorld}, + i_instance::Integer, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + X.featstruct[i_instance, i_feature] +end +Base.@propagate_inbounds @inline function featvalues!( + X::UniformFullDimensionalLogiset{U,OneWorld}, + featslice :: AbstractArray{U,1}, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing, +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + X.featstruct[:, i_feature] = featslice +end +function readfeature( + X::UniformFullDimensionalLogiset{U,OneWorld}, + featchannel::U, + w::OneWorld, + f::AbstractFeature +) where {U} + featchannel +end + + +Base.@propagate_inbounds @inline function featchannel( + X::UniformFullDimensionalLogiset{U,<:Interval}, + i_instance::Integer, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + @views X.featstruct[:,:,i_instance, i_feature] +end +Base.@propagate_inbounds @inline function featvalues!( + X::UniformFullDimensionalLogiset{U,<:Interval}, + featslice :: AbstractArray{U,3}, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing, +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + X.featstruct[:, :, :, i_feature] = featslice +end +function readfeature( + X::UniformFullDimensionalLogiset{U,<:Interval}, + featchannel::AbstractArray{U,2}, + w::Interval, + f::AbstractFeature +) where {U} + featchannel[w.x, w.y] +end + + +Base.@propagate_inbounds @inline function featchannel( + X::UniformFullDimensionalLogiset{U,<:Interval2D}, + i_instance::Integer, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + @views X.featstruct[:,:,:,:,i_instance, i_feature] +end +Base.@propagate_inbounds @inline function featvalues!( + X::UniformFullDimensionalLogiset{U,<:Interval2D}, + featslice :: AbstractArray{U,5}, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing, +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + X.featstruct[:, :, :, :, :, i_feature] = featslice +end +function readfeature( + X::UniformFullDimensionalLogiset{U,<:Interval2D}, + featchannel::AbstractArray{U,4}, + w::Interval2D, + f::AbstractFeature +) where {U} + featchannel[w.x.x, w.x.y, w.y.x, w.y.y] +end + +############################################################################################ + +@inline function featvalue( + X :: UniformFullDimensionalLogiset{U,OneWorld}, + i_instance :: Integer, + w :: OneWorld, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + X.featstruct[i_instance, i_feature] +end + +@inline function featvalue( + X :: UniformFullDimensionalLogiset{U,<:Interval}, + i_instance :: Integer, + w :: Interval, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + X.featstruct[w.x, w.y, i_instance, i_feature] +end + +@inline function featvalue( + X :: UniformFullDimensionalLogiset{U,<:Interval2D}, + i_instance :: Integer, + w :: Interval2D, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + X.featstruct[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] +end + +############################################################################################ + +@inline function featvalue!( + X::UniformFullDimensionalLogiset{U,OneWorld}, + featval::U, + i_instance::Integer, + w::OneWorld, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + X.featstruct[i_instance, i_feature] = featval +end + +@inline function featvalue!( + X::UniformFullDimensionalLogiset{U,<:Interval}, + featval::U, + i_instance::Integer, + w::Interval, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + X.featstruct[w.x, w.y, i_instance, i_feature] = featval +end + +@inline function featvalue!( + X::UniformFullDimensionalLogiset{U,<:Interval2D}, + featval::U, + i_instance::Integer, + w::Interval2D, + feature :: AbstractFeature, + i_feature :: Union{Nothing,Integer} = nothing +) where {U} + if isnothing(i_feature) + i_feature = findfirst(isequal(feature), features(X)) + if isnothing(i_feature) + error("Could not find feature $(feature) in memoset of type $(typeof(X)).") + end + end + + X.featstruct[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] = featval +end + +############################################################################################ + +function allfeatvalues( + X::UniformFullDimensionalLogiset, +) + unique(X.featstruct) +end + +function allfeatvalues( + X::UniformFullDimensionalLogiset, + i_instance, +) + error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), f::$(typeof(f))).") +end + +function allfeatvalues( + X::UniformFullDimensionalLogiset, + i_instance, + f, +) + error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), f::$(typeof(f))).") +end + +############################################################################################ + +function instances( + X::UniformFullDimensionalLogiset{U,W,N}, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false) +) where {U,W<:OneWorld,N} + UniformFullDimensionalLogiset{U,W,N}(if return_view == Val(true) @view X.featstruct[inds,:] else X.featstruct[inds,:] end, features(X)) +end + +function instances( + X::UniformFullDimensionalLogiset{U,W,N}, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false) +) where {U,W<:Interval,N} + UniformFullDimensionalLogiset{U,W,N}(if return_view == Val(true) @view X.featstruct[:,:,inds,:] else X.featstruct[:,:,inds,:] end, features(X)) +end + +function instances( + X::UniformFullDimensionalLogiset{U,W,N}, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false) +) where {U,W<:Interval2D,N} + UniformFullDimensionalLogiset{U,W,N}(if return_view == Val(true) @view X.featstruct[:,:,:,:,inds,:] else X.featstruct[:,:,:,:,inds,:] end, features(X)) +end + +############################################################################################ + +function concatdatasets(Xs::UniformFullDimensionalLogiset{U,W,N}...) where {U,W<:AbstractWorld,N} + @assert allequal(features.(Xs)) "Cannot concatenate " * + "UniformFullDimensionalLogiset's with different features: " * + "$(@show features.(Xs))" + UniformFullDimensionalLogiset{U,W,N}(concatdatasets([X.featstruct for X in Xs]...), features(first(Xs))) +end + +isminifiable(::UniformFullDimensionalLogiset) = true + +function minify(X::UniformFullDimensionalLogiset{U,W,N}) where {U,W<:AbstractWorld,N} + new_d, backmap = minify(X.featstruct) + X = UniformFullDimensionalLogiset{U,W,N}( + minify(new_d), + features(X), + ) + X, backmap +end + +############################################################################################ + +function displaystructure(X::UniformFullDimensionalLogiset{U,W,N}; indent_str = "", include_ninstances = true) where {U,W<:AbstractWorld,N} + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) + pieces = [] + push!(pieces, "UniformFullDimensionalLogiset ($(humansize(X)))") + push!(pieces, "$(padattribute("worldtype:", worldtype(X)))") + push!(pieces, "$(padattribute("featvaltype:", featvaltype(X)))") + push!(pieces, "$(padattribute("featuretype:", featuretype(X)))") + push!(pieces, "$(padattribute("frametype:", frametype(X)))") + if include_ninstances + push!(pieces, "$(padattribute("# instances:", ninstances(X)))") + end + push!(pieces, "$(padattribute("size × eltype:", "$(size(X.featstruct)) × $(eltype(X.featstruct))"))") + push!(pieces, "$(padattribute("dimensionality:", dimensionality(X)))") + push!(pieces, "$(padattribute("channel_size:", channel_size(X)))") + # push!(pieces, "$(padattribute("# features:", nfeatures(X)))") + push!(pieces, "$(padattribute("features:", "$(nfeatures(X)) -> $(displaysyntaxvector(features(X)))"))") + + return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" +end + +############################################################################################ + +function capacity(X::UniformFullDimensionalLogiset{U,OneWorld}) where {U} + prod(size(X)) +end +function capacity(X::UniformFullDimensionalLogiset{U,<:Interval}) where {U} + prod([ + ninstances(X), + nfeatures(X), + div(size(X, 1)*(size(X, 2)),2), + ]) +end +function capacity(X::UniformFullDimensionalLogiset{U,<:Interval2D}) where {U} + prod([ + ninstances(X), + nfeatures(X), + div(size(X, 1)*(size(X, 2)),2), + div(size(X, 3)*(size(X, 4)),2), + ]) +end + +############################################################################################ + +function hasnans(X::UniformFullDimensionalLogiset{U,OneWorld}) where {U} + any(_isnan.(X.featstruct)) +end +function hasnans(X::UniformFullDimensionalLogiset{U,<:Interval}) where {U} + any([hasnans(X.featstruct[x,y,:,:]) + for x in 1:size(X, 1) for y in (x+1):size(X, 2)]) +end +function hasnans(X::UniformFullDimensionalLogiset{U,<:Interval2D}) where {U} + any([hasnans(X.featstruct[xx,xy,yx,yy,:,:]) + for xx in 1:size(X, 1) for xy in (xx+1):size(X, 2) + for yx in 1:size(X, 3) for yy in (yx+1):size(X, 4)]) +end diff --git a/src/logisets/dimensional-structures/main.jl b/src/logisets/dimensional-structures/main.jl new file mode 100644 index 0000000..c4bf429 --- /dev/null +++ b/src/logisets/dimensional-structures/main.jl @@ -0,0 +1,74 @@ +module DimensionalDatasets + + +import Base: size, show, getindex, iterate, length, push!, eltype + +using BenchmarkTools +using ProgressMeter +using UniqueVectors + +using SoleBase + +using SoleLogics +using SoleLogics: AbstractFormula, AbstractWorld, AbstractRelation +using SoleLogics: AbstractFrame, AbstractDimensionalFrame, FullDimensionalFrame +import SoleLogics: worldtype, accessibles, allworlds, alphabet, initialworld + +using SoleData +import SoleData: _isnan, hasnans, nvariables, max_channel_size, channel_size +import SoleData: instance, get_instance, instances, concatdatasets +import SoleData: displaystructure +import SoleData: dimensionality + + +using SoleModels +using SoleModels.utils +using SoleModels: Aggregator, AbstractCondition +using SoleModels: BoundedScalarConditions +using SoleModels: CanonicalFeatureGeq, CanonicalFeatureGeqSoft, CanonicalFeatureLeq, CanonicalFeatureLeqSoft +using SoleModels: AbstractLogiset, AbstractMultiModalFrame +using SoleModels: MultiLogiset, AbstractLogiset +using SoleModels: apply_test_operator, existential_aggregator, aggregator_bottom, aggregator_to_binary + +using SoleModels: worldtype, featvaltype, featuretype, frametype + +import SoleModels: representatives, ScalarMetaCondition, ScalarCondition, featvaltype +import SoleModels: ninstances, nrelations, nfeatures, check, instances, minify +import SoleModels: displaystructure, frame +import SoleModels: alphabet, isminifiable + +import SoleModels: nmetaconditions +import SoleModels: capacity, nmemoizedvalues +using SoleModels: memoizationinfo + + +import SoleModels: initlogiset +import SoleModels: worldtype, allworlds, featvalue, featvalue! +import SoleModels: featchannel, readfeature, featvalues!, allfeatvalues +import SoleData: get_instance, ninstances, nvariables, channel_size, eltype + +export nvariables + +############################################################################################ + +# Frame-specific logisets +include("logiset.jl") + +include("onestep-memosets.jl") + +export initlogiset, ninstances, max_channel_size, worldtype, dimensionality, allworlds, featvalue + +# Bindings for interpreting dataset structures as logisets +include("dataset-bindings.jl") + +using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame +using SoleLogics: X, Y, Z + +# Representatives for dimensional frames +include("representatives/Full0DFrame.jl") +include("representatives/Full1DFrame.jl") +include("representatives/Full1DFrame+IA.jl") +include("representatives/Full1DFrame+RCC.jl") +include("representatives/Full2DFrame.jl") + +end diff --git a/src/logisets/dimensional-structures/onestep-memosets.jl b/src/logisets/dimensional-structures/onestep-memosets.jl new file mode 100644 index 0000000..40614a4 --- /dev/null +++ b/src/logisets/dimensional-structures/onestep-memosets.jl @@ -0,0 +1,291 @@ +using SoleModels: AbstractScalarOneStepRelationalMemoset + +import Base: size, ndims, getindex, setindex! +""" +Abstract type for relational memosets optimized for uniform logisets with +full dimensional frames. + +See also +[`UniformFullDimensionalLogiset`](@ref), +[`AbstractScalarOneStepRelationalMemoset`](@ref), +[`FullDimensionalFrame`](@ref), +[`AbstractLogiset`](@ref). +""" +abstract type AbstractUniformFullDimensionalOneStepRelationalMemoset{U,W<:AbstractWorld,FR<:AbstractFrame{W}} <: AbstractScalarOneStepRelationalMemoset{W,U,FR} end + +innerstruct(Xm::AbstractUniformFullDimensionalOneStepRelationalMemoset) = Xm.d + +function nmemoizedvalues(Xm::AbstractUniformFullDimensionalOneStepRelationalMemoset) + count(!isnothing, innerstruct(Xm)) +end + +""" +A relational memoset optimized for uniform scalar logisets with +full dimensional frames of dimensionality `N`, storing values for each world in +a `ninstances` × `nmetaconditions` × `nrelations` array. +Each world is a hyper-interval, and its `N*2` components are used to index different array +dimensions, ultimately resulting in a `(N*2+3)`-dimensional array. + +See also +[`UniformFullDimensionalLogiset`](@ref), +[`FullDimensionalFrame`](@ref), +[`AbstractLogiset`](@ref). +""" +struct UniformFullDimensionalOneStepRelationalMemoset{ + U, + W<:AbstractWorld, + N, + D<:AbstractArray{UU} where UU<:Union{U,Nothing}, +} <: AbstractUniformFullDimensionalOneStepRelationalMemoset{U,W,FullDimensionalFrame{N,W}} + + # Multi-dimensional structure + d :: D + + function UniformFullDimensionalOneStepRelationalMemoset{U,W,N,D}( + d::D + ) where {U,W<:AbstractWorld,N,D<:AbstractArray{UU} where UU<:Union{U,Nothing}} + new{U,W,N,D}(d) + end + + function UniformFullDimensionalOneStepRelationalMemoset{U,W,N}( + d::D + ) where {U,W<:AbstractWorld,N,D<:AbstractArray{UU} where UU<:Union{U,Nothing}} + UniformFullDimensionalOneStepRelationalMemoset{U,W,N,D}(d) + end + + function UniformFullDimensionalOneStepRelationalMemoset( + X::UniformFullDimensionalLogiset{U,W,0}, + metaconditions::AbstractVector{<:ScalarMetaCondition}, + relations::AbstractVector{<:AbstractRelation}, + perform_initialization::Bool = true, + ) where {U,W<:OneWorld} + nmetaconditions = length(metaconditions) + nrelations = length(relations) + # TODO + # if nrelations > 0 + # @warn "Note that using a relational memoset with W = $(OneWorld) is " * + # "overkill $(@show nrelations)." + # end + d = begin + if perform_initialization + d = Array{Union{U,Nothing}, 3}(undef, ninstances(X), nmetaconditions, nrelations) + fill!(d, nothing) + else + Array{U,3}(undef, ninstances(X), nmetaconditions, nrelations) + end + end + UniformFullDimensionalOneStepRelationalMemoset{U,W,0}(d) + end + + function UniformFullDimensionalOneStepRelationalMemoset( + X::UniformFullDimensionalLogiset{U,W,1}, + metaconditions::AbstractVector{<:ScalarMetaCondition}, + relations::AbstractVector{<:AbstractRelation}, + perform_initialization::Bool = true, + ) where {U,W<:Interval} + nmetaconditions = length(metaconditions) + nrelations = length(relations) + d = begin + if perform_initialization + d = Array{Union{U,Nothing}, 5}(undef, size(X, 1), size(X, 2), ninstances(X), nmetaconditions, nrelations) + fill!(d, nothing) + else + Array{U,5}(undef, size(X, 1), size(X, 2), ninstances(X), nmetaconditions, nrelations) + end + end + UniformFullDimensionalOneStepRelationalMemoset{U,W,1}(d) + end + + function UniformFullDimensionalOneStepRelationalMemoset( + X::UniformFullDimensionalLogiset{U,W,2}, + metaconditions::AbstractVector{<:ScalarMetaCondition}, + relations::AbstractVector{<:AbstractRelation}, + perform_initialization::Bool = true, + ) where {U,W<:Interval2D} + nmetaconditions = length(metaconditions) + nrelations = length(relations) + d = begin + if perform_initialization + d = Array{Union{U,Nothing}, 7}(undef, size(X, 1), size(X, 2), size(X, 3), size(X, 4), ninstances(X), nmetaconditions, nrelations) + fill!(d, nothing) + else + Array{U,7}(undef, size(X, 1), size(X, 2), size(X, 3), size(X, 4), ninstances(X), nmetaconditions, nrelations) + end + end + UniformFullDimensionalOneStepRelationalMemoset{U,W,2}(d) + end +end + +Base.size(Xm::UniformFullDimensionalOneStepRelationalMemoset, args...) = size(Xm.d, args...) +Base.ndims(Xm::UniformFullDimensionalOneStepRelationalMemoset, args...) = ndims(Xm.d, args...) + +ninstances(Xm::UniformFullDimensionalOneStepRelationalMemoset) = size(Xm, ndims(Xm)-2) +nmetaconditions(Xm::UniformFullDimensionalOneStepRelationalMemoset) = size(Xm, ndims(Xm)-1) +nrelations(Xm::UniformFullDimensionalOneStepRelationalMemoset) = size(Xm, ndims(Xm)) + +############################################################################################ + +function capacity(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,OneWorld}) where {U} + prod(size(Xm)) +end +function capacity(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interval}) where {U} + prod([ + ninstances(Xm), + nmetaconditions(Xm), + nrelations(Xm), + div(size(Xm, 1)*(size(Xm, 2)),2), + ]) +end +function capacity(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interval2D}) where {U} + prod([ + ninstances(Xm), + nmetaconditions(Xm), + nrelations(Xm), + div(size(Xm, 1)*(size(Xm, 2)),2), + div(size(Xm, 3)*(size(Xm, 4)),2), + ]) +end + +############################################################################################ + +@inline function Base.getindex( + Xm :: UniformFullDimensionalOneStepRelationalMemoset{U,W}, + i_instance :: Integer, + w :: W, + i_metacond :: Integer, + i_relation :: Integer +) where {U,W<:OneWorld} + Xm.d[i_instance, i_metacond, i_relation] +end +@inline function Base.getindex( + Xm :: UniformFullDimensionalOneStepRelationalMemoset{U,W}, + i_instance :: Integer, + w :: W, + i_metacond :: Integer, + i_relation :: Integer +) where {U,W<:Interval} + Xm.d[w.x, w.y, i_instance, i_metacond, i_relation] +end +@inline function Base.getindex( + Xm :: UniformFullDimensionalOneStepRelationalMemoset{U,W}, + i_instance :: Integer, + w :: W, + i_metacond :: Integer, + i_relation :: Integer +) where {U,W<:Interval2D} + Xm.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_metacond, i_relation] +end + +############################################################################################ + +Base.@propagate_inbounds @inline function Base.setindex!( + Xm::UniformFullDimensionalOneStepRelationalMemoset{U,OneWorld}, + gamma::U, + i_instance::Integer, + w::OneWorld, + i_metacond::Integer, + i_relation::Integer, +) where {U} + Xm.d[i_instance, i_metacond, i_relation] = gamma +end + +Base.@propagate_inbounds @inline function Base.setindex!( + Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interval}, + gamma::U, + i_instance::Integer, + w::Interval, + i_metacond::Integer, + i_relation::Integer, +) where {U} + Xm.d[w.x, w.y, i_instance, i_metacond, i_relation] = gamma +end + +Base.@propagate_inbounds @inline function Base.setindex!( + Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interval2D}, + gamma::U, + i_instance::Integer, + w::Interval2D, + i_metacond::Integer, + i_relation::Integer, +) where {U} + Xm.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_metacond, i_relation] = gamma +end + +############################################################################################ + +function hasnans(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,OneWorld}) where {U} + any(_isnan.(Xm.d)) +end +function hasnans(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interval}) where {U} + any([hasnans(Xm.d[x,y,:,:,:]) + for x in 1:size(Xm, 1) for y in (x+1):size(Xm, 2)]) +end +function hasnans(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interval2D}) where {U} + any([hasnans(Xm.d[xx,xy,yx,yy,:,:,:]) + for xx in 1:size(Xm, 1) for xy in (xx+1):size(Xm, 2) + for yx in 1:size(Xm, 3) for yy in (yx+1):size(Xm, 4)]) +end + +############################################################################################ + +function instances( + Xm::UniformFullDimensionalOneStepRelationalMemoset{U,W,N}, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false) +) where {U,W<:OneWorld,N} + UniformFullDimensionalOneStepRelationalMemoset{U,W,N}(if return_view == Val(true) @view Xm.d[inds,:,:] else Xm.d[inds,:,:] end) +end +function instances( + Xm::UniformFullDimensionalOneStepRelationalMemoset{U,W,N}, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false) +) where {U,W<:Interval,N} + UniformFullDimensionalOneStepRelationalMemoset{U,W,N}(if return_view == Val(true) @view Xm.d[:,:,inds,:,:] else Xm.d[:,:,inds,:,:] end) +end +function instances( + Xm::UniformFullDimensionalOneStepRelationalMemoset{U,W,N}, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false) +) where {U,W<:Interval2D,N} + UniformFullDimensionalOneStepRelationalMemoset{U,W,N}(if return_view == Val(true) @view Xm.d[:,:,:,:,inds,:,:] else Xm.d[:,:,:,:,inds,:,:] end) +end + +############################################################################################ + +function concatdatasets(Xms::UniformFullDimensionalOneStepRelationalMemoset{U,W,N}...) where {U,W<:AbstractWorld,N} + UniformFullDimensionalOneStepRelationalMemoset(cat([Xm.d for Xm in Xms]...; dims=1+2*N)) +end + +isminifiable(::UniformFullDimensionalOneStepRelationalMemoset) = true + +function minify(Xm::UniformFullDimensionalOneStepRelationalMemoset) + new_d, backmap = minify(Xm.d) + Xm = UniformFullDimensionalOneStepRelationalMemoset( + new_d, + ) + Xm, backmap +end + +############################################################################################ + +function displaystructure(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,W,N}; indent_str = "", include_ninstances = true, include_nmetaconditions = true, include_nrelations = true) where {U,W<:AbstractWorld,N} + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) + pieces = [] + push!(pieces, "$(nameof(typeof(Xm))) ($(memoizationinfo(Xm)), $(humansize(Xm)))") + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") + push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") + push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + if include_ninstances + push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") + end + if include_nmetaconditions + push!(pieces, "$(padattribute("# metaconditions:", nmetaconditions(Xm)))") + end + if include_nrelations + push!(pieces, "$(padattribute("# relations:", nrelations(Xm)))") + end + push!(pieces, "$(padattribute("size × eltype:", "$(size(innerstruct(Xm))) × $(eltype(innerstruct(Xm)))"))") + + return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" +end diff --git a/src/dimensional-logisets/representatives/Full0DFrame.jl b/src/logisets/dimensional-structures/representatives/Full0DFrame.jl similarity index 100% rename from src/dimensional-logisets/representatives/Full0DFrame.jl rename to src/logisets/dimensional-structures/representatives/Full0DFrame.jl diff --git a/src/dimensional-logisets/representatives/Full1DFrame+IA.jl b/src/logisets/dimensional-structures/representatives/Full1DFrame+IA.jl similarity index 100% rename from src/dimensional-logisets/representatives/Full1DFrame+IA.jl rename to src/logisets/dimensional-structures/representatives/Full1DFrame+IA.jl diff --git a/src/dimensional-logisets/representatives/Full1DFrame+RCC.jl b/src/logisets/dimensional-structures/representatives/Full1DFrame+RCC.jl similarity index 100% rename from src/dimensional-logisets/representatives/Full1DFrame+RCC.jl rename to src/logisets/dimensional-structures/representatives/Full1DFrame+RCC.jl diff --git a/src/dimensional-logisets/representatives/Full1DFrame.jl b/src/logisets/dimensional-structures/representatives/Full1DFrame.jl similarity index 100% rename from src/dimensional-logisets/representatives/Full1DFrame.jl rename to src/logisets/dimensional-structures/representatives/Full1DFrame.jl diff --git a/src/dimensional-logisets/representatives/Full2DFrame.jl b/src/logisets/dimensional-structures/representatives/Full2DFrame.jl similarity index 100% rename from src/dimensional-logisets/representatives/Full2DFrame.jl rename to src/logisets/dimensional-structures/representatives/Full2DFrame.jl diff --git a/src/logisets/logiset.jl b/src/logisets/logiset.jl index 9735be5..b007a32 100644 --- a/src/logisets/logiset.jl +++ b/src/logisets/logiset.jl @@ -35,18 +35,18 @@ abstract type AbstractLogiset{ function featchannel( X::AbstractLogiset{W}, i_instance::Integer, - f::AbstractFeature, + feature::AbstractFeature, ) where {W<:AbstractWorld} - error("Please, provide method featchannel(::$(typeof(X)), i_instance::$(typeof(i_instance)), f::$(typeof(f))).") + error("Please, provide method featchannel(::$(typeof(X)), i_instance::$(typeof(i_instance)), feature::$(typeof(feature))).") end function readfeature( X::AbstractLogiset{W}, featchannel::Any, w::W, - f::AbstractFeature, + feature::AbstractFeature, ) where {W<:AbstractWorld} - error("Please, provide method readfeature(::$(typeof(X)), featchannel::$(typeof(featchannel)), w::$(typeof(w)), f::$(typeof(f))).") + error("Please, provide method readfeature(::$(typeof(X)), featchannel::$(typeof(featchannel)), w::$(typeof(w)), feature::$(typeof(feature))).") end """ @@ -56,9 +56,27 @@ function featvalue( X::AbstractLogiset{W}, i_instance::Integer, w::W, - f::AbstractFeature, + feature::AbstractFeature, ) where {W<:AbstractWorld} - readfeature(X, featchannel(X, i_instance, f), w, f) + readfeature(X, featchannel(X, i_instance, feature), w, feature) +end + +function featvalue!( + X::AbstractLogiset{W}, + featval, + i_instance::Integer, + w::W, + feature::AbstractFeature, +) where {W<:AbstractWorld} + error("Please, provide method featvalue!(::$(typeof(X)), featval::$(typeof(featval)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), feature::$(typeof(feature))).") +end + +function featvalues!( + X::AbstractLogiset{W}, + featslice, + feature::AbstractFeature, +) where {W<:AbstractWorld} + error("Please, provide method featvalues!(::$(typeof(X)), featslice::$(typeof(featslice)), feature::$(typeof(feature))).") end function frame(X::AbstractLogiset, i_instance::Integer) @@ -79,9 +97,9 @@ end function allfeatvalues( X::AbstractLogiset, i_instance, - f, + feature, ) - error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), f::$(typeof(f))).") + error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), feature::$(typeof(feature))).") end function instances( @@ -114,22 +132,12 @@ hasnans(::AbstractLogiset) = any.(isnan, allfeatvalues(X)) ############################################################################################ function featvalue( - f::AbstractFeature, + feature::AbstractFeature, X::AbstractLogiset{W}, i_instance::Integer, w::W, ) where {W<:AbstractWorld} - featvalue(X, i_instance, w, f) -end - -function check( - p::Proposition{<:AbstractCondition}, - X::AbstractLogiset{W}, - i_instance::Integer, - w::W; - kwargs... -) where {W<:AbstractWorld} - checkcondition(atom(p), X, i_instance, w; kwargs...) + featvalue(X, i_instance, w, feature) end function Base.show(io::IO, X::AbstractLogiset; kwargs...) @@ -206,11 +214,10 @@ end ninstances(X::ExplicitBooleanLogiset) = length(X.d) - function featchannel( X::ExplicitBooleanLogiset{W}, i_instance::Integer, - f::AbstractFeature, + feature::AbstractFeature, ) where {W<:AbstractWorld} X.d[i_instance][1] end @@ -219,9 +226,24 @@ function readfeature( X::ExplicitBooleanLogiset{W}, featchannel::Any, w::W, - f::AbstractFeature, + feature::AbstractFeature, ) where {W<:AbstractWorld} - Base.in(f, featchannel[w]) + Base.in(feature, featchannel[w]) +end + +function featvalue!( + X::ExplicitBooleanLogiset{W}, + featval::Bool, + i_instance::Integer, + w::W, + feature::AbstractFeature, +) where {W<:AbstractWorld} + cur_featval = featvalue(X, featval, i_instance, w, feature) + if featval && !cur_featval + push!(X.d[i_instance][1][w], feature) + elseif !featval && cur_featval + filter!(_f->_f != feature, X.d[i_instance][1][w]) + end end function frame( @@ -317,7 +339,7 @@ ninstances(X::ExplicitLogiset) = length(X.d) function featchannel( X::ExplicitLogiset{W}, i_instance::Integer, - f::AbstractFeature, + feature::AbstractFeature, ) where {W<:AbstractWorld} X.d[i_instance][1] end @@ -326,9 +348,19 @@ function readfeature( X::ExplicitLogiset{W}, featchannel::Any, w::W, - f::AbstractFeature, + feature::AbstractFeature, +) where {W<:AbstractWorld} + featchannel[w][feature] +end + +function featvalue!( + X::ExplicitLogiset{W}, + featval, + i_instance::Integer, + w::W, + feature::AbstractFeature, ) where {W<:AbstractWorld} - featchannel[w][f] + X.d[i_instance][1][w][feature] = featval end function frame( diff --git a/src/logisets/main.jl b/src/logisets/main.jl index 83210ca..6e6582a 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -7,7 +7,8 @@ using SoleLogics: AbstractWorld, IdentityRel import SoleLogics: syntaxstring import SoleData: ninstances -import SoleData: hasnans, instances +import SoleData: hasnans, instances, concatdatasets +import SoleData: displaystructure # Features to be computed on worlds of dataset instances include("features.jl") @@ -40,16 +41,53 @@ include("multilogiset.jl") # Model checking algorithms for logisets and multilogisets include("check.jl") +# TODO remove? +function nfeatures end + include("scalar/main.jl") +include("dimensional-structures/main.jl") + +# export get_ontology, +# get_interval_ontology + +# export DimensionalLogiset, Logiset, SupportedScalarLogiset + +# using .DimensionalDatasets: nfeatures, nrelations, +# # +# relations, +# # +# GenericModalDataset, +# AbstractLogiset, +# AbstractActiveScalarLogiset, +# DimensionalLogiset, +# Logiset, +# SupportedScalarLogiset + +# using .DimensionalDatasets: AbstractWorld, AbstractRelation +# using .DimensionalDatasets: AbstractWorldSet, WorldSet +# using .DimensionalDatasets: FullDimensionalFrame + +# using .DimensionalDatasets: Ontology, worldtype + +# using .DimensionalDatasets: get_ontology, +# get_interval_ontology + +# using .DimensionalDatasets: OneWorld, OneWorldOntology + +# using .DimensionalDatasets: Interval, Interval2D + +# using .DimensionalDatasets: IARelations + function default_relmemoset_type(X::AbstractLogiset) + # TODO? # frames = [frame(X, i_instance) for i_instance in 1:ninstances(X)] # if allequal(frames) && first(unique(frames)) isa FullDimensionalFrame - # if X isa UniformFullDimensionalFWD TODO - # UniformFullDimensionalRelationalSupport - # else + if X isa DimensionalDatasets.UniformFullDimensionalLogiset + DimensionalDatasets.UniformFullDimensionalOneStepRelationalMemoset + else ScalarOneStepRelationalMemoset - # end + end end function default_onestep_memoset_type(X::AbstractLogiset) diff --git a/src/logisets/memosets.jl b/src/logisets/memosets.jl index c75ff95..cac5f76 100644 --- a/src/logisets/memosets.jl +++ b/src/logisets/memosets.jl @@ -20,7 +20,6 @@ abstract type AbstractMemoset{ FT<:AbstractFeature, FR<:AbstractFrame, } <: AbstractLogiset{W,U,FT,FR} end -# } <: AbstractInterpretationSet{AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:FT},T where T<:TruthValue,FR where FR<:AbstractFrame}} end function capacity(Xm::AbstractMemoset) error("Please, provide method capacity(::$(typeof(Xm))).") @@ -53,9 +52,9 @@ function displaystructure(Xm::AbstractMemoset; indent_str = "", include_ninstanc if include_ninstances push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") end - push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") + # push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") - return "FullMemoset ($(humansize(Xm)))" * + return "$(nameof(typeof(Xm))) ($(memoizationinfo(Xm)), $(humansize(Xm)))" * join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" end @@ -180,4 +179,18 @@ fullmemo(Xm::FullMemoset) = Xm hasnans(::FullMemoset) = false +function displaystructure(Xm::FullMemoset; indent_str = "", include_ninstances = true) + padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) + pieces = [] + push!(pieces, "") + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + if include_ninstances + push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") + end + # push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") + + return "$(nameof(typeof(Xm))) ($(memoizationinfo(Xm)), $(humansize(Xm)))" * + join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" +end + # Base.size(::FullMemoset) = () diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index d03f3f1..0e7c77e 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -114,17 +114,6 @@ function featvalue( featvalue(modality(X, i_modality), i_instance, w, f) end -function check( - p::Proposition{<:AbstractCondition}, - X::MultiLogiset, - i_modality::Integer, - i_instance::Integer, - w::W; - kwargs... -) where {W<:AbstractWorld} - check(p, modality(X, i_modality), i_instance, w; kwargs...) -end - hasnans(X::MultiLogiset) = any(hasnans.(modalities(X))) diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl new file mode 100644 index 0000000..92f4139 --- /dev/null +++ b/src/logisets/scalar/dataset-bindings.jl @@ -0,0 +1,60 @@ +using DataFrames + +function initlogiset(dataset, features) + error("Please, provide method initlogiset(dataset::$(typeof(dataset)), features::$(typeof(features))).") +end +function ninstances(dataset) + error("Please, provide method ninstances(dataset::$(typeof(dataset)).") +end +function worldtype(dataset) + error("Please, provide method worldtype(dataset::$(typeof(dataset)).") +end +function allworlds(dataset, i_instance) + error("Please, provide method allworlds(dataset::$(typeof(dataset)), i_instance::Integer).") +end +function featvalue(dataset, i_instance, w, feature) + error("Please, provide method featvalue(dataset::$(typeof(dataset)), i_instance::Integer, w::$(typeof(w)), feature::$(typeof(feature))).") +end + +""" + scalarlogiset(dataset, features::AbstractVector{<:VarFeature}) + +Converts a dataset structure to a logiset with scalar-valued features. +""" +function scalarlogiset( + dataset, + features::AbstractVector{<:VarFeature}, +) + features = UniqueVector(features) + + # Initialize the logiset structure + X = initlogiset(dataset, features) + + # Load external features (if any) + if any(isa.(features, ExternalFWDFeature)) + i_external_features = first.(filter(((i_feature,is_external_fwd),)->(is_external_fwd), collect(enumerate(isa.(features, ExternalFWDFeature))))) + for i_feature in i_external_features + feature = features[i_feature] + featvalues!(X, feature.X, i_feature) + end + end + + # Load internal features + i_features = first.(filter(((i_feature,is_external_fwd),)->!(is_external_fwd), collect(enumerate(isa.(features, ExternalFWDFeature))))) + enum_features = zip(i_features, features[i_features]) + + _ninstances = ninstances(dataset) + + # Compute features + # p = Progress(_ninstances, 1, "Computing EMD...") + @inbounds Threads.@threads for i_instance in 1:_ninstances + for w in allworlds(dataset, i_instance) + for (i_feature,feature) in enum_features + featval = featvalue(dataset, i_instance, w, feature) + featvalue!(X, featval, i_instance, w, feature, i_feature) + end + end + # next!(p) + end + X +end diff --git a/src/logisets/scalar/main.jl b/src/logisets/scalar/main.jl index dafe0d9..2f0ec83 100644 --- a/src/logisets/scalar/main.jl +++ b/src/logisets/scalar/main.jl @@ -1,4 +1,3 @@ - export inverse_test_operator, dual_test_operator, apply_test_operator, TestOperator @@ -29,9 +28,11 @@ include("random.jl") include("representatives.jl") +include("dataset-bindings.jl") + include("memosets.jl") -include("one-step-memoset.jl") +include("onestep-memoset.jl") export UnivariateMin, UnivariateMax, UnivariateSoftMin, UnivariateSoftMax, diff --git a/src/logisets/scalar/memosets.jl b/src/logisets/scalar/memosets.jl index 1a55e3c..66069b4 100644 --- a/src/logisets/scalar/memosets.jl +++ b/src/logisets/scalar/memosets.jl @@ -1,4 +1,12 @@ using UniqueVectors +import Base: in, findfirst + +# Fixes +# https://github.com/garrison/UniqueVectors.jl/blob/d63669d1f5c8f7ee4f4bf3f2920bc4afe33fe676/src/UniqueVectors.jl#L56 +Base.in(item, uv::UniqueVector) = haskey(uv.lookup, item) +Base.findfirst(p::UniqueVectors.EqualTo, uv::UniqueVector) = + get(uv.lookup, p.x, nothing) + """ A full memoization structure used for checking formulas of scalar conditions on diff --git a/src/logisets/scalar/one-step-memoset.jl b/src/logisets/scalar/onestep-memoset.jl similarity index 87% rename from src/logisets/scalar/one-step-memoset.jl rename to src/logisets/scalar/onestep-memoset.jl index c4094a0..8731409 100644 --- a/src/logisets/scalar/one-step-memoset.jl +++ b/src/logisets/scalar/onestep-memoset.jl @@ -84,18 +84,37 @@ end """ Abstract type for one-step memoization structures for checking formulas of type `⟨R⟩ (f ⋈ t)`, for a generic relation `R`. +We refer to these structures as *relational memosets*. """ abstract type AbstractScalarOneStepRelationalMemoset{W<:AbstractWorld,U,FR<:AbstractFrame{W}} <: AbstractMemoset{W,U,FT where FT<:AbstractFeature,FR} end @inline function Base.getindex( - memoset :: AbstractScalarOneStepRelationalMemoset{W}, + Xm :: AbstractScalarOneStepRelationalMemoset{W}, i_instance :: Integer, w :: W, i_metacond :: Integer, i_relation :: Integer ) where {W} error("Please, provide method Base.getindex(" * - "memoset::$(typeof(memoset)), " * + "Xm::$(typeof(Xm)), " * + "i_instance::$(typeof(i_instance)), " * + "w::$(typeof(w)), " * + "i_metacond::$(typeof(i_metacond)), " * + "i_relation::$(typeof(i_relation))" * + ").") +end + +@inline function Base.setindex!( + Xm :: AbstractScalarOneStepRelationalMemoset{W}, + gamma, + i_instance :: Integer, + w :: W, + i_metacond :: Integer, + i_relation :: Integer, +) where {W<:AbstractWorld} + error("Please, provide method Base.setindex!(" * + "Xm::$(typeof(Xm)), " * + "gamma, " * "i_instance::$(typeof(i_instance)), " * "w::$(typeof(w)), " * "i_metacond::$(typeof(i_metacond)), " * @@ -106,16 +125,31 @@ end """ Abstract type for one-step memoization structure for checking "global" formulas of type `⟨G⟩ (f ⋈ t)`. + We refer to these structures as *global memosets*. """ abstract type AbstractScalarOneStepGlobalMemoset{W<:AbstractWorld,U} <: AbstractMemoset{W,U,FT where FT<:AbstractFeature,FR where FR<:AbstractFrame{W}} end @inline function Base.getindex( - memoset :: AbstractScalarOneStepGlobalMemoset{W}, + Xm :: AbstractScalarOneStepGlobalMemoset{W}, i_instance :: Integer, i_metacond :: Integer, ) where {W} error("Please, provide method Base.getindex(" * - "memoset::$(typeof(memoset)), " * + "Xm::$(typeof(Xm)), " * + "i_instance::$(typeof(i_instance)), " * + "i_metacond::$(typeof(i_metacond))" * + ").") +end + +@inline function Base.setindex!( + Xm :: AbstractScalarOneStepGlobalMemoset{W}, + gamma, + i_instance :: Integer, + i_metacond :: Integer, +) where {W<:AbstractWorld} + error("Please, provide method Base.getindex(" * + "Xm::$(typeof(Xm)), " * + "gamma, " * "i_instance::$(typeof(i_instance)), " * "i_metacond::$(typeof(i_metacond))" * ").") @@ -132,6 +166,8 @@ function minify(Xm::Union{AbstractScalarOneStepRelationalMemoset,AbstractScalarO minify(innerstruct(Xm)) end +usesfullmemo(::Union{AbstractScalarOneStepRelationalMemoset,AbstractScalarOneStepGlobalMemoset}) = false + ############################################################################################ ############################################################################################ ############################################################################################ @@ -155,11 +191,11 @@ struct ScalarOneStepMemoset{ # Global memoset globmemoset :: GM - function ScalarOneStepMemoset( - metaconditions::AbstractVector{<:ScalarMetaCondition}, - relations::AbstractVector{<:AbstractRelation}, + function ScalarOneStepMemoset{U}( relmemoset::RM, globmemoset::GM, + metaconditions::AbstractVector{<:ScalarMetaCondition}, + relations::AbstractVector{<:AbstractRelation}, ) where { U<:Number, W<:AbstractWorld, @@ -195,7 +231,7 @@ struct ScalarOneStepMemoset{ "$(nmetaconditions(globmemoset)) and $(length(metaconditions))" @assert ninstances(globmemoset) == ninstances(relmemoset) "Can't " * "instantiate $(ty) with mismatching ninstances for " * - "globmemoset and relmemoset memoset: " * + "global and relational memosets: " * "$(ninstances(globmemoset)) and $(ninstances(relmemoset))" end @@ -206,7 +242,8 @@ struct ScalarOneStepMemoset{ X :: AbstractLogiset{W,U}, metaconditions :: AbstractVector{<:ScalarMetaCondition}, relations :: AbstractVector{<:AbstractRelation}, - relational_memoset_type :: Type{<:AbstractScalarOneStepRelationalMemoset} = default_relmemoset_type(X); + # relational_memoset_type :: Type{<:AbstractScalarOneStepRelationalMemoset} = default_relmemoset_type(X); + relational_memoset_type :: Type = default_relmemoset_type(X); precompute_globmemoset :: Bool = true, precompute_relmemoset :: Bool = false, ) where {W<:AbstractWorld,U} @@ -215,7 +252,11 @@ struct ScalarOneStepMemoset{ compute_globmemoset = begin if globalrel in relations relations = filter(l->l≠globalrel, relations) - true + if worldtype == OneWorld + false + else + true + end else false end @@ -291,7 +332,7 @@ struct ScalarOneStepMemoset{ # next!(p) end end - ScalarOneStepMemoset(metaconditions, relations, relmemoset, globmemoset) + ScalarOneStepMemoset{U}(relmemoset, globmemoset, metaconditions, relations) end end @@ -322,6 +363,7 @@ function featchannel_onestep_aggregation( end if isnothing(i_metacond) + # Find metacond with same aggregator i_metacond = findfirst((m)->feature(m) == feature(metacond) && existential_aggregator(test_operator(m)) == existential_aggregator(test_operator(metacond)), metaconditions(Xm)) if isnothing(i_metacond) i_neg_metacond = findfirst(isequal(negation(metacond)), metaconditions(Xm)) @@ -338,11 +380,15 @@ function featchannel_onestep_aggregation( gamma = begin if rel == globalrel _globmemoset = globmemoset(Xm) - if isnothing(_globmemoset[i_instance, i_metacond]) - gamma = featchannel_onestep_aggregation(X, featchannel, i_instance, rel, _feature, aggregator) - _globmemoset[i_instance, i_metacond] = gamma + if isnothing(_globmemoset) + error("Could not compute one-step aggregation with no global memoset.") + else + if isnothing(_globmemoset[i_instance, i_metacond]) + gamma = featchannel_onestep_aggregation(X, featchannel, i_instance, rel, _feature, aggregator) + _globmemoset[i_instance, i_metacond] = gamma + end + _globmemoset[i_instance, i_metacond] end - _globmemoset[i_instance, i_metacond] else i_relation = isnothing(i_relation) ? findfirst(isequal(rel), Xm.relations) : i_relation if isnothing(i_relation) @@ -400,25 +446,25 @@ end function instances(Xm::ScalarOneStepMemoset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) ScalarOneStepMemoset( - metaconditions(Xm), - relations(Xm), instances(relmemoset(Xm), inds, return_view), (isnothing(globmemoset(Xm)) ? nothing : instances(globmemoset(Xm), inds, return_view)), + metaconditions(Xm), + relations(Xm), ) end function concatdatasets(Xms::ScalarOneStepMemoset...) - @assert allequal(metaconditions.(Xms)) "Cannot concat " * + @assert allequal(metaconditions.(Xms)) "Cannot concatenate " * "ScalarOneStepMemoset's with different metaconditions: " * "$(@show metaconditions.(Xms))" - @assert allequal(relations.(Xms)) "Cannot concat " * + @assert allequal(relations.(Xms)) "Cannot concatenate " * "ScalarOneStepMemoset's with different relations: " * "$(@show relations.(Xms))" ScalarOneStepMemoset( - metaconditions(first(Xms)), - relations(first(Xms)), concatdatasets(relmemoset.(Xms)), concatdatasets(globmemoset.(Xms)), + metaconditions(first(Xms)), + relations(first(Xms)), ) end @@ -444,25 +490,25 @@ function minify(Xm::OSSD) where {OSSD<:ScalarOneStepMemoset} end function displaystructure(Xm::ScalarOneStepMemoset; indent_str = "", include_ninstances = true) - padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))-1) + padattribute(l,r,off=0) = string(l) * lpad(r,32+off+length(string(r))-(length(indent_str)+2+length(l))-1) pieces = [] push!(pieces, "ScalarOneStepMemoset ($(humansize(Xm)))\n") if include_ninstances - push!(pieces, " $(padattribute("# instances:", ninstances(Xm)))\n") + push!(pieces, " $(padattribute("# instances:", ninstances(Xm), 1))\n") end - push!(pieces, " $(padattribute("# metaconditions:", nmetaconditions(Xm)))\n") - push!(pieces, " $(padattribute("# relations:", nrelations(Xm)))\n") + # push!(pieces, " $(padattribute("# metaconditions:", nmetaconditions(Xm), 1))\n") + # push!(pieces, " $(padattribute("# relations:", nrelations(Xm), 1))\n") - push!(pieces, " $(padattribute("metaconditions:", "$(eltype(metaconditions(Xm)))[$(join([(syntaxstring.(metaconditions(Xm))[1:4])..., "...", syntaxstring.(metaconditions(Xm))[end-4:end]...], ","))]")))\n") - push!(pieces, " $(padattribute("relations:", relations(Xm)))\n") + push!(pieces, " $(padattribute("metaconditions:", "$(nmetaconditions(Xm)) -> $(displaysyntaxvector(metaconditions(Xm)))", 1))\n") + push!(pieces, " $(padattribute("relations:", "$(nrelations(Xm)) -> $(displaysyntaxvector(relations(Xm)))", -9))\n") push!(pieces, "[R] " * displaystructure(relmemoset(Xm); indent_str = "$(indent_str)│ ", include_ninstances = false, include_nmetaconditions = false, include_nrelations = false)) if !isnothing(globmemoset(Xm)) push!(pieces, "[G] " * displaystructure(globmemoset(Xm); indent_str = "$(indent_str) ", include_ninstances = false, include_nmetaconditions = false)) else - push!(pieces, " global memoset: −\n") + push!(pieces, "[G] −\n") end return join(pieces, "$(indent_str)├", "$(indent_str)└") @@ -501,7 +547,7 @@ struct ScalarOneStepRelationalMemoset{ X::AbstractLogiset{W,U,FT,FR}, metaconditions::AbstractVector{<:ScalarMetaCondition}, relations::AbstractVector{<:AbstractRelation}, - perform_initialization = true + perform_initialization::Bool = true ) where {W,U,FT<:AbstractFeature,FR<:AbstractFrame{W}} nmetaconditions = length(metaconditions) nrelations = length(relations) @@ -548,8 +594,6 @@ end Xm.d[i_instance, i_metacond, i_relation][w] = gamma end -usesfullmemo(::ScalarOneStepRelationalMemoset) = false - function hasnans(Xm::ScalarOneStepRelationalMemoset) any(map(d->(any(_isnan.(collect(values(d))))), Xm.d)) end @@ -610,9 +654,8 @@ struct ScalarOneStepGlobalMemoset{ function ScalarOneStepGlobalMemoset( X::AbstractLogiset{W,U}, metaconditions::AbstractVector{<:ScalarMetaCondition}, - perform_initialization = true + perform_initialization::Bool = true ) where {W<:AbstractWorld,U} - @assert worldtype(X) != OneWorld "TODO adjust this note: note that you should not use a global Xm when not using global decisions" nmetaconditions = length(metaconditions) d = Array{Union{U,Nothing},2}(undef, ninstances(X), length(metaconditions)) if perform_initialization @@ -628,7 +671,7 @@ ninstances(Xm::ScalarOneStepGlobalMemoset) = size(Xm.d, 1) nmetaconditions(Xm::ScalarOneStepGlobalMemoset) = size(Xm.d, 2) capacity(Xm::ScalarOneStepGlobalMemoset) = prod(size(Xm.d)) -nmemoizedvalues(Xm::ScalarOneStepGlobalMemoset) = sum((!).((isnothing).(Xm.d))) +nmemoizedvalues(Xm::ScalarOneStepGlobalMemoset) = count(!isnothing, Xm.d) @inline function Base.getindex( Xm :: ScalarOneStepGlobalMemoset{W}, @@ -648,8 +691,6 @@ end Xm.d[i_instance, i_metacond] = gamma end -usesfullmemo(::ScalarOneStepGlobalMemoset) = false - function hasnans(Xm::ScalarOneStepGlobalMemoset) # @show any(_isnan.(Xm.d)) any(_isnan.(Xm.d)) @@ -678,9 +719,7 @@ function displaystructure(Xm::ScalarOneStepGlobalMemoset; indent_str = "", inclu if include_nmetaconditions push!(pieces, "$(padattribute("# metaconditions:", nmetaconditions(Xm)))") end - push!(pieces, "$(padattribute("size × eltype:", "$(size(Xm.d)) × $(eltype(Xm.d))"))") - # push!(pieces, "$(padattribute("capacity:", capacity(Xm)))") - # push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") + push!(pieces, "$(padattribute("size × eltype:", "$(size(innerstruct(Xm))) × $(eltype(innerstruct(Xm)))"))") return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" end diff --git a/src/logisets/scalar/test-operators.jl b/src/logisets/scalar/test-operators.jl index 6e4c194..ff22dd8 100644 --- a/src/logisets/scalar/test-operators.jl +++ b/src/logisets/scalar/test-operators.jl @@ -1,8 +1,4 @@ -using SoleLogics: TruthValue - -############################################################################################ - """ const TestOperator = Function diff --git a/src/logisets/scalar/var-features.jl b/src/logisets/scalar/var-features.jl index 3f5c9e5..9b3d366 100644 --- a/src/logisets/scalar/var-features.jl +++ b/src/logisets/scalar/var-features.jl @@ -14,7 +14,7 @@ const UVF_VARPREFIX = "V" """ abstract type VarFeature{U} <: AbstractFeature end -Abstract type for feature functions that can be applied on (multi)variate data. +Abstract type for feature functions that can be computed on (multi)variate data. See also [`featvaltype`](@ref), [`computefeature`](@ref). """ diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index bbff96e..e715046 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -12,10 +12,14 @@ See also [`AbstractLogiset`](@ref). """ struct SupportedLogiset{ - L<:AbstractLogiset, + W<:AbstractWorld, + U, + FT<:AbstractFeature, + FR<:AbstractFrame{W}, + L<:AbstractLogiset{W,U,FT,FR}, N, MS<:NTuple{N,Union{AbstractOneStepMemoset,AbstractFullMemoset}}, -} <: AbstractLogiset{W where W<:AbstractWorld,U where U,FT where FT<:AbstractFeature,FR where FR<:AbstractFrame{W where W<:AbstractWorld}} +} <: AbstractLogiset{W,U,FT,FR} # Core dataset base :: L @@ -25,9 +29,9 @@ struct SupportedLogiset{ function SupportedLogiset( base::L, supports::_MS - ) where {L<:AbstractLogiset,_MS<:Tuple} + ) where {W,U,FT,FR,L<:AbstractLogiset{W,U,FT,FR},_MS<:Tuple} - wrong_supports = filter(supp->!(supp isa Union{<:AbstractVector{<:AbstractDict},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}), supports) + wrong_supports = filter(supp->!(supp isa Union{<:AbstractVector{<:AbstractDict{<:AbstractFormula,<:WorldSet}},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}), supports) @assert length(wrong_supports) == 0 "Cannot instantiate SupportedLogiset " * "with wrong support type(s): $(join(typeof.(wrong_supports), ", ")). " * @@ -50,6 +54,12 @@ struct SupportedLogiset{ end end, supports)...)) + wrong_supports = filter(supp->!(supp isa Union{AbstractOneStepMemoset,AbstractFullMemoset}), supports) + + @assert length(wrong_supports) == 0 "Cannot instantiate SupportedLogiset " * + "with wrong support type(s): $(join(typeof.(wrong_supports), ", ")). " * + "Only full and one-step memosets are allowed." + @assert !(any(isa.(supports, SupportedLogiset))) "Cannot instantiate " * "SupportedLogiset with a SupportedLogiset support." for (i_supp,supp) in enumerate(supports) @@ -69,18 +79,23 @@ struct SupportedLogiset{ _fullmemo = usesfullmemo.(supports) @assert sum(_fullmemo) <= 1 "Cannot instantiate SupportedLogiset with " * "more than one full memoization set. $(sum(_fullmemo)) were provided." * - "$(@show typeof.(supports))" - - @assert all((!).(_fullmemo)) || (last(_fullmemo) && all((!).(_fullmemo[1:end-1]))) "" * - "Please, provide cascading supports so that the full memoization set appears " * - "last. $(@show typeof.(supports))" + "support types: $(join(typeof.(supports), ", "))" + + if sum(_fullmemo) == 1 + # @assert all((!).(_fullmemo)) || (last(_fullmemo) && all((!).(_fullmemo[1:end-1]))) "" * + # "Please, provide cascading supports so that the full memoization set appears " * + # "last. $(@show typeof.(supports))" + nonfullsupports = filter(!usesfullmemo, supports) + fullsupport = first(filter(usesfullmemo, supports)) + supports = Tuple([nonfullsupports..., fullsupport]) + end @assert allequal([ninstances(base), ninstances.(supports)...]) "Consistency " * "check failed! Mismatching ninstances for base and memoset(s): $(ninstances(base)) and $(ninstances.(supports))" N = length(supports) MS = typeof(supports) - new{L,N,MS}(base, supports) + new{W,U,FT,FR,L,N,MS}(base, supports) end function SupportedLogiset( @@ -93,10 +108,10 @@ struct SupportedLogiset{ # Helper (avoids ambiguities) function SupportedLogiset( base::AbstractLogiset, - firstsupport::Union{<:AbstractVector{<:AbstractDict},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}, - supports::Union{<:AbstractVector{<:AbstractDict},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}... + firstsupport::Union{<:AbstractVector{<:AbstractDict{<:AbstractFormula,<:WorldSet}},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}, + supports::Union{<:AbstractVector{<:AbstractDict{<:AbstractFormula,<:WorldSet}},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}... ) - SupportedLogiset(base, Union{<:AbstractVector{<:AbstractDict},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}[firstsupport, supports...]) + SupportedLogiset(base, Union{<:AbstractVector{<:AbstractDict{<:AbstractFormula,<:WorldSet}},<:Union{AbstractOneStepMemoset,AbstractFullMemoset},<:SupportedLogiset}[firstsupport, supports...]) end function SupportedLogiset( @@ -107,7 +122,7 @@ struct SupportedLogiset{ relations :: Union{Nothing,AbstractVector{<:AbstractRelation}} = nothing, use_onestep_memoization :: Union{Bool,Type{<:AbstractOneStepMemoset}} = !isnothing(conditions) && !isnothing(relations), onestep_precompute_globmemoset :: Bool = (use_onestep_memoization != false), - onestep_precompute_relmemoset :: Bool = false, + onestep_precompute_relmemoset :: Bool = false, ) supports = Union{AbstractOneStepMemoset,AbstractFullMemoset}[] @@ -115,23 +130,33 @@ struct SupportedLogiset{ "both conditions and relations in order to use a one-step memoset." if use_onestep_memoization != false + @assert !isnothing(conditions) && !isnothing(relations) "Please, provide " * + "both conditions and relations in order to use a one-step memoset." onestep_memoset_type = (use_onestep_memoization isa Bool ? default_onestep_memoset_type(base) : use_onestep_memoization) push!(supports, onestep_memoset_type( base, conditions, - relations, + relations; precompute_globmemoset = onestep_precompute_globmemoset, precompute_relmemoset = onestep_precompute_relmemoset ) ) + else + @assert isnothing(conditions) && isnothing(relations) "Conditions and/or " * + "relations were passed; provide use_onestep_memoization = true " * + "to use a one-step memoset." end if use_full_memoization != false full_memoset_type = (use_full_memoization isa Bool ? default_full_memoset_type(base) : use_full_memoization) - push!(supports, full_memoset_type(base)) + push!(supports, full_memoset_type(base, conditions)) end + @assert length(supports) > 0 "Cannot instantiate SupportedLogiset with no supports " * + "Please, specify use_full_memoization = true and/or " * + "use_onestep_memoization = true." + SupportedLogiset(base, supports) end @@ -148,8 +173,8 @@ end base(X::SupportedLogiset) = X.base supports(X::SupportedLogiset) = X.supports -basetype(X::SupportedLogiset{L,N,MS}) where {L,N,MS} = L -supporttypes(X::SupportedLogiset{L,N,MS}) where {L,N,MS} = MS +basetype(X::SupportedLogiset{W,U,FT,FR,L,N,MS}) where {W,U,FT,FR,L,N,MS} = L +supporttypes(X::SupportedLogiset{W,U,FT,FR,L,N,MS}) where {W,U,FT,FR,L,N,MS} = MS nsupports(X::SupportedLogiset) = length(X.supports) @@ -197,14 +222,14 @@ fullmemo(X::SupportedLogiset) = usesfullmemo(X) ? last(supports(X)) : error("Thi isminifiable(X::SupportedLogiset) = isminifiable(base(X)) && all(isminifiable.(supports(X))) -function minify(X::SL) where {SL<:SupportedLogiset} +function minify(X::SupportedLogiset) (new_sl, new_supports...), backmap = minify([ base(X), supports(X)..., ]) - X = SL( + X = SupportedLogiset( new_sl, new_supports, ) @@ -227,27 +252,29 @@ function instances( end function concatdatasets(Xs::SupportedLogiset...) + @assert allequal(nsupports.(Xs)) "Cannot concatenate " * + "SupportedLogiset's with different nsupports: " * + "$(@show nsupports.(Xs))" SupportedLogiset( concatdatasets([base(X) for X in Xs]), - [concatdatasets([supports(X)[i_supp] for X in Xs]...) for i_supp in 1:nsupports(Xs)]..., + [concatdatasets([supports(X)[i_supp] for X in Xs]...) for i_supp in 1:nsupports(first(Xs))]..., ) end function displaystructure(X::SupportedLogiset; indent_str = "", include_ninstances = true) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))-1) pieces = [] - push!(pieces, " \n") + push!(pieces, "SupportedLogiset ($(humansize(X)))\n") if include_ninstances push!(pieces, " " * padattribute("# instances:", "$(ninstances(X))\n")) end + push!(pieces, " " * padattribute("# supports:", "$(nsupports(X))\n")) push!(pieces, " " * padattribute("usesfullmemo:", "$(usesfullmemo(X))\n")) push!(pieces, "[BASE] " * displaystructure(base(X); indent_str = "$(indent_str)│ ", include_ninstances = false)) - push!(pieces, " " * padattribute("# supports:", "$(nsupports(X))\n")) for (i_supp,supp) in enumerate(supports(X)) - push!(pieces, "[$(i_supp)] $(displaystructure(supp; indent_str = (i_supp == nsupports(X) ? "$(indent_str) " : "$(indent_str)│ "), include_ninstances = false))") + push!(pieces, "[SUPPORT $(i_supp)] $(displaystructure(supp; indent_str = (i_supp == nsupports(X) ? "$(indent_str) " : "$(indent_str)│ "), include_ninstances = false))") end - return "SupportedLogiset ($(humansize(X)))" * - join(pieces, "$(indent_str)├", "$(indent_str)└") * "\n" + return join(pieces, "$(indent_str)├", "$(indent_str)└") * "\n" end # ############################################################################################ diff --git a/src/dimensional-logisets/_parse-dimensional-condition.jl b/src/other/_parse-dimensional-condition.jl similarity index 100% rename from src/dimensional-logisets/_parse-dimensional-condition.jl rename to src/other/_parse-dimensional-condition.jl diff --git a/src/dimensional-logisets/active-logiset.jl b/src/other/active-logiset.jl similarity index 83% rename from src/dimensional-logisets/active-logiset.jl rename to src/other/active-logiset.jl index 7570ca3..47c8dad 100644 --- a/src/dimensional-logisets/active-logiset.jl +++ b/src/other/active-logiset.jl @@ -138,7 +138,7 @@ It stores the feature values in a lookup table. """ -struct Logiset{ +struct ActiveLogiset{ V, W<:AbstractWorld, FR<:AbstractFrame{W}, @@ -158,7 +158,7 @@ struct Logiset{ # Initial world(s) initialworld :: Union{Nothing,W,AbstractWorldSet{<:W}} - function Logiset{V,W,FR,FT,FWD}( + function ActiveLogiset{V,W,FR,FT,FWD}( featstruct :: FWD, features :: AbstractVector{FT}, relations :: AbstractVector{<:AbstractRelation}, @@ -167,10 +167,10 @@ struct Logiset{ initialworld = nothing, ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W},FWD<:AbstractFeatureLookupSet{V,FR},FT<:AbstractFeature} features = collect(features) - ty = "Logiset{$(V),$(W),$(FR),$(FT)}" + ty = "ActiveLogiset{$(V),$(W),$(FR),$(FT)}" @assert allow_no_instances || ninstances(featstruct) > 0 "Can't instantiate $(ty) with no instance. (featstruct's type $(typeof(featstruct)))" @assert nfeatures(featstruct) == length(features) "Can't instantiate $(ty) with different numbers of instances $(ninstances(featstruct)) and of features $(length(features))." - check_initialworld(Logiset, initialworld, W) + check_initialworld(ActiveLogiset, initialworld, W) new{ V, W, @@ -185,7 +185,7 @@ struct Logiset{ ) end - function Logiset{V,W,FR}( + function ActiveLogiset{V,W,FR}( featstruct :: FWD, features :: AbstractVector{<:AbstractFeature}, args...; @@ -194,54 +194,54 @@ struct Logiset{ features = collect(features) FT = Union{typeof.(features)...} features = Vector{FT}(features) - Logiset{V,W,FR,FT,FWD}(featstruct, features, args...; kwargs...) + ActiveLogiset{V,W,FR,FT,FWD}(featstruct, features, args...; kwargs...) end - function Logiset{V,W}( + function ActiveLogiset{V,W}( featstruct :: AbstractFeatureLookupSet{V,FR}, args...; kwargs... ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} - Logiset{V,W,FR}(featstruct, args...; kwargs...) + ActiveLogiset{V,W,FR}(featstruct, args...; kwargs...) end - function Logiset( + function ActiveLogiset( featstruct :: AbstractFeatureLookupSet{V,FR}, args...; kwargs..., ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} - Logiset{V,W}(featstruct, args...; kwargs...) + ActiveLogiset{V,W}(featstruct, args...; kwargs...) end end -@inline function Base.getindex(X::Logiset{V,W}, args...) where {V,W<:AbstractWorld} +@inline function Base.getindex(X::ActiveLogiset{V,W}, args...) where {V,W<:AbstractWorld} Base.getindex(featstruct(X), args...)::V end -@inline function featvalue(X::Logiset{V,W}, args...) where {V,W<:AbstractWorld} +@inline function featvalue(X::ActiveLogiset{V,W}, args...) where {V,W<:AbstractWorld} featvalue(featstruct(X), args...)::V end -Base.size(X::Logiset) = Base.size(featstruct(X)) +Base.size(X::ActiveLogiset) = Base.size(featstruct(X)) -featstruct(X::Logiset) = X.featstruct -relations(X::Logiset) = X.relations -features(X::Logiset) = X.features +featstruct(X::ActiveLogiset) = X.featstruct +relations(X::ActiveLogiset) = X.relations +features(X::ActiveLogiset) = X.features -nfeatures(X::Logiset) = length(features(X)) -nrelations(X::Logiset) = length(relations(X)) -ninstances(X::Logiset) = ninstances(featstruct(X)) -worldtype(X::Logiset{V,W}) where {V,W<:AbstractWorld} = W +nfeatures(X::ActiveLogiset) = length(features(X)) +nrelations(X::ActiveLogiset) = length(relations(X)) +ninstances(X::ActiveLogiset) = ninstances(featstruct(X)) +worldtype(X::ActiveLogiset{V,W}) where {V,W<:AbstractWorld} = W -frame(X::Logiset, i_instance::Integer) = frame(featstruct(X), i_instance) -initialworld(X::Logiset) = X.initialworld -function initialworld(X::Logiset, i_instance::Integer) +frame(X::ActiveLogiset, i_instance::Integer) = frame(featstruct(X), i_instance) +initialworld(X::ActiveLogiset) = X.initialworld +function initialworld(X::ActiveLogiset, i_instance::Integer) initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_instance] : initialworld(X) end -function instances(X::Logiset, inds::AbstractVector{<:Integer}, args...; kwargs...) - Logiset( +function instances(X::ActiveLogiset, inds::AbstractVector{<:Integer}, args...; kwargs...) + ActiveLogiset( instances(featstruct(X), inds, args...; kwargs...), features(X), relations(X), @@ -249,7 +249,7 @@ function instances(X::Logiset, inds::AbstractVector{<:Integer}, args...; kwargs. ) end -function displaystructure(X::Logiset; indent_str = "") +function displaystructure(X::ActiveLogiset; indent_str = "") out = "$(typeof(X))\t$(humansize(X))\n" out *= indent_str * "├ features:\t\t$((length(features(X))))\t$(features(X))\n" out *= indent_str * "├ relations:\t\t$((length(relations(X))))\t$(relations(X))\n" @@ -258,13 +258,13 @@ function displaystructure(X::Logiset; indent_str = "") out end -hasnans(X::Logiset) = hasnans(featstruct(X)) +hasnans(X::ActiveLogiset) = hasnans(featstruct(X)) -isminifiable(::Logiset) = true +isminifiable(::ActiveLogiset) = true -function minify(X::Logiset) +function minify(X::ActiveLogiset) new_fwd, backmap = minify(featstruct(X)) - X = Logiset( + X = ActiveLogiset( new_fwd, features(X), relations(X), diff --git a/src/dimensional-logisets/dimensional-logiset.jl b/src/other/dimensional-logiset.jl similarity index 100% rename from src/dimensional-logisets/dimensional-logiset.jl rename to src/other/dimensional-logiset.jl diff --git a/src/dimensional-logisets/dimensional-ontologies.jl b/src/other/dimensional-ontologies.jl similarity index 100% rename from src/dimensional-logisets/dimensional-ontologies.jl rename to src/other/dimensional-ontologies.jl diff --git a/src/other/main.jl b/src/other/main.jl new file mode 100644 index 0000000..6243c09 --- /dev/null +++ b/src/other/main.jl @@ -0,0 +1,17 @@ + +# include("dimensional-logiset.jl") + +# export parsecondition + +# # Conditions on features for dimensional datasets +# include("_parse-dimensional-condition.jl") + +# # Concrete type for ontologies +# include("ontology.jl") # TODO frame inside the ontology? + +# export DimensionalLogiset, Logiset, SupportedScalarLogiset + +# const GenericModalDataset = Union{AbstractDimensionalDataset,AbstractLogiset,MultiLogiset} + +# # Dimensional ontologies +# include("dimensional-ontologies.jl") diff --git a/src/dimensional-logisets/ontology.jl b/src/other/ontology.jl similarity index 100% rename from src/dimensional-logisets/ontology.jl rename to src/other/ontology.jl diff --git a/src/dimensional-logisets/passive-dimensional-logiset.jl b/src/other/passive-dimensional-logiset.jl similarity index 93% rename from src/dimensional-logisets/passive-dimensional-logiset.jl rename to src/other/passive-dimensional-logiset.jl index b20c263..c7c574e 100644 --- a/src/dimensional-logisets/passive-dimensional-logiset.jl +++ b/src/other/passive-dimensional-logiset.jl @@ -7,10 +7,12 @@ using SoleData: AbstractDimensionalDataset, DimensionalInstance, DimensionalChannel -using SoleLogics: TruthValue - """ -A logiset with a dimensional domain. TODO explain +Scalar logiset with of dimensionality `N`. + +See also +... +[`AbstractLogiset`](@ref). """ struct PassiveDimensionalLogiset{ N, @@ -25,8 +27,12 @@ struct PassiveDimensionalLogiset{ d::DOM, ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset,FR<:AbstractDimensionalFrame{N,W}} ty = "PassiveDimensionalLogiset{$(N),$(W),$(DOM),$(FR)}" - @assert N == dimensionality(d) "ERROR! Dimensionality mismatch: can't instantiate $(ty) with underlying structure $(DOM). $(N) == $(dimensionality(d)) should hold." - @assert SoleLogics.goeswithdim(W, N) "ERROR! Dimensionality mismatch: can't interpret worldtype $(W) on PassiveDimensionalLogiset of dimensionality = $(N)" + @assert N == dimensionality(d) "ERROR! Dimensionality mismatch: " * + "can't instantiate $(ty) with underlying structure" * + "$(DOM). $(N) == $(dimensionality(d)) should hold." + @assert SoleLogics.goeswithdim(W, N) "ERROR! Dimensionality mismatch: " * + "can't interpret worldtype $(W) on PassiveDimensionalLogiset" * + "of dimensionality = $(N)" new{N,W,DOM,FR}(d) end diff --git a/src/dimensional-logisets/scalar-logiset.jl b/src/other/scalar-logiset.jl similarity index 100% rename from src/dimensional-logisets/scalar-logiset.jl rename to src/other/scalar-logiset.jl diff --git a/src/utils.jl b/src/utils.jl index df945b8..5302931 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,5 +1,9 @@ module utils +using SoleLogics: syntaxstring + +export displaysyntaxvector + using SoleBase @inline function softminimum(vals, alpha) @@ -54,4 +58,15 @@ end subscriptnumber(i::AbstractFloat) = subscriptnumber(string(i)) subscriptnumber(i::Any) = i +function displaysyntaxvector(a, maxnum = 8) + els = begin + if length(a) > maxnum + [(syntaxstring.(a)[1:div(maxnum, 2)])..., "...", syntaxstring.(a)[end-div(maxnum, 2):end]...] + else + syntaxstring.(a) + end + end + "$(eltype(a))[$(join(map(e->"\"$(e)\"", els), ", "))]" +end + end diff --git a/test/dimensional-logisets.jl b/test/dimensional-logisets.jl new file mode 100644 index 0000000..7365265 --- /dev/null +++ b/test/dimensional-logisets.jl @@ -0,0 +1,64 @@ +using Test +using StatsBase +using Random +using SoleLogics +using SoleModels +using SoleModels.DimensionalDatasets + +for (X,relations) in [ + # (Array(reshape(1.0:20.0, 2,10)),[identityrel,globalrel]), + # (Array(reshape(1.0:20.0, 2,10)),[identityrel]), + # (Array(reshape(1.0:60.0, 3,2,10)),[IARelations..., identityrel]), + # (Array(reshape(1.0:180.0, 3,3,2,10)),[IA2DRelations..., identityrel]), + (Array(reshape(1.0:60.0, 3,2,10)),[IARelations..., globalrel]), + (Array(reshape(1.0:180.0, 3,3,2,10)),[IA2DRelations..., globalrel]), +] + +nvars = nvariables(X) +features = collect(Iterators.flatten([[UnivariateMax{Float64}(i_var), UnivariateMin{Float64}(i_var)] for i_var in 1:nvars])) +logiset = scalarlogiset(X, features) + +metaconditions = [ScalarMetaCondition(feature, >) for feature in features] +@test_nowarn SupportedLogiset(logiset, ()) +@test_throws AssertionError SupportedLogiset(logiset, [Dict()]) +@test_throws AssertionError SupportedLogiset(logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}()]) +@test_nowarn SupportedLogiset(logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10]) +@test_throws AssertionError SupportedLogiset(logiset, ([Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10], [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10])) +@test_throws AssertionError SupportedLogiset(logiset; use_full_memoization = false) +@test_throws AssertionError SupportedLogiset(logiset; use_onestep_memoization = true) +supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = relations) +@test_throws AssertionError SupportedLogiset(logiset, (supported_logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10])) +supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = false, use_onestep_memoization = true, conditions = metaconditions, relations = relations) + +@test_nowarn SupportedLogiset(logiset, (supported_logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10])) +@test_nowarn SupportedLogiset(logiset, ([Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10], supported_logiset)) + +supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = true, conditions = metaconditions, relations = relations) + +metaconditions = vcat([[ScalarMetaCondition(feature, >), ScalarMetaCondition(feature, <)] for feature in features]...) +complete_supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = true, conditions = metaconditions, relations = relations) + +rng = Random.MersenneTwister(1) +alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]); +syntaxstring.(alph) +_formulas = [randformulatree(rng, 3, alph, [SoleLogics.BASE_MULTIMODAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:16:end]...]) for i in 1:20]; +syntaxstring.(_formulas) .|> println; + +i_instance = 1 +@test_nowarn checkcondition(atom(alph.propositions[1]), logiset, i_instance, first(allworlds(X, i_instance))) + +c1 = @test_nowarn [ + [check(φ, logiset, i_instance, w) for φ in _formulas] + for w in allworlds(X, i_instance)] +@test_throws ErrorException [ + [check(φ, supported_logiset, i_instance, w) for φ in _formulas] + for w in allworlds(X, i_instance)] +c3 = @test_nowarn [ + [check(φ, complete_supported_logiset, i_instance, w) for φ in _formulas] + for w in allworlds(X, i_instance)] + +@test c1 == c3 + +@test_nowarn concatdatasets(logiset, logiset, logiset) +@test_broken concatdatasets(complete_supported_logiset, complete_supported_logiset) +end diff --git a/test/datasets.jl b/test/logisets.jl similarity index 97% rename from test/datasets.jl rename to test/logisets.jl index 5693024..b1539ae 100644 --- a/test/datasets.jl +++ b/test/logisets.jl @@ -196,9 +196,9 @@ using SoleModels: ScalarOneStepMemoset relations = [identityrel, globalrel] -bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(metaconditions, relations, bool_relationalmemoset, bool_globalmemoset) +bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(bool_relationalmemoset, bool_globalmemoset, metaconditions, relations) -bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(metaconditions, relations, bool_relationalmemoset, bool_globalmemoset) +bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(bool_relationalmemoset, bool_globalmemoset, metaconditions, relations) bool_onestepmemoset_empty = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations) bool_onestepmemoset_full = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations; precompute_globmemoset = true, precompute_relmemoset = true) diff --git a/test/runtests.jl b/test/runtests.jl index c7c602f..b979669 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -16,9 +16,9 @@ end println("Julia version: ", VERSION) test_suites = [ - ("Datasets", [ - "datasets.jl", - # "dimensional-datasets.jl", + ("Logisets", [ + "logisets.jl", + "dimensional-logisets.jl", ]), ("Models", ["base.jl", ]), ("Miscellaneous", ["misc.jl", "minify.jl"]), From 5499debe7338283262e3c80e6f1a55291e173a04 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 16 Jun 2023 02:06:01 +0200 Subject: [PATCH 14/77] Minor fix --- test/dimensional-logisets.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/dimensional-logisets.jl b/test/dimensional-logisets.jl index 7365265..2eae6d6 100644 --- a/test/dimensional-logisets.jl +++ b/test/dimensional-logisets.jl @@ -41,7 +41,7 @@ complete_supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_mem rng = Random.MersenneTwister(1) alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]); syntaxstring.(alph) -_formulas = [randformulatree(rng, 3, alph, [SoleLogics.BASE_MULTIMODAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:16:end]...]) for i in 1:20]; +_formulas = [randformulatree(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:16:end]...]) for i in 1:20]; syntaxstring.(_formulas) .|> println; i_instance = 1 @@ -50,7 +50,7 @@ i_instance = 1 c1 = @test_nowarn [ [check(φ, logiset, i_instance, w) for φ in _formulas] for w in allworlds(X, i_instance)] -@test_throws ErrorException [ +c2 = @test_nowarn [ [check(φ, supported_logiset, i_instance, w) for φ in _formulas] for w in allworlds(X, i_instance)] c3 = @test_nowarn [ From 93b9151917bbb3bb8c9019b6b1c8152d3e8303c8 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 20 Jun 2023 01:07:42 +0200 Subject: [PATCH 15/77] dataset-bindings, (multimodal) scalarlogiset, preserveseltype, clean displaystructure, MultiFormula, AnchoredFormula, CheckMode's --- Project.toml | 1 + src/SoleModels.jl | 4 +- src/logisets/check-modes.jl | 110 ++++++++++++ src/logisets/check.jl | 22 +-- src/logisets/conditions.jl | 11 ++ .../dimensional-structures/computefeature.jl | 52 ++++++ .../dataset-bindings.jl | 152 +++++++++++++--- .../dimensional-structures/logiset.jl | 55 ++++-- src/logisets/dimensional-structures/main.jl | 13 +- .../onestep-memosets.jl | 32 +++- src/logisets/features.jl | 2 +- src/logisets/logiset.jl | 69 ++++++-- src/logisets/main.jl | 5 +- src/logisets/memosets.jl | 44 ++++- src/logisets/multilogiset.jl | 91 ++++++++-- src/logisets/scalar/conditions.jl | 2 +- src/logisets/scalar/dataset-bindings.jl | 162 +++++++++++++++++- src/logisets/scalar/memosets.jl | 3 +- src/logisets/scalar/onestep-memoset.jl | 124 +++++++++----- src/logisets/scalar/var-features.jl | 124 +++++++++----- src/logisets/supported-logiset.jl | 53 ++++-- src/machine-learning.jl | 2 +- src/models/base.jl | 6 +- src/other/active-logiset.jl | 4 +- src/other/dimensional-logiset.jl | 22 +-- src/other/ontology.jl | 2 +- src/other/passive-dimensional-logiset.jl | 14 +- .../other/test-dimensional-datasets.jl | 0 src/utils.jl | 41 +++++ .../cube2logiset.jl} | 58 ++++--- test/logisets/dataframe2logiset.jl | 65 +++++++ test/{ => logisets}/logisets.jl | 8 +- test/logisets/multilogisets.jl | 65 +++++++ test/runtests.jl | 6 +- 34 files changed, 1154 insertions(+), 270 deletions(-) create mode 100644 src/logisets/check-modes.jl create mode 100644 src/logisets/dimensional-structures/computefeature.jl rename test/dimensional-datasets.jl => src/other/test-dimensional-datasets.jl (100%) rename test/{dimensional-logisets.jl => logisets/cube2logiset.jl} (56%) create mode 100644 test/logisets/dataframe2logiset.jl rename test/{ => logisets}/logisets.jl (95%) create mode 100644 test/logisets/multilogisets.jl diff --git a/Project.toml b/Project.toml index caece32..bcb6933 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.1.0" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" diff --git a/src/SoleModels.jl b/src/SoleModels.jl index 0147a2c..cb6b133 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -76,6 +76,8 @@ export ExplicitLogiset, ScalarCondition export ninstances, nfeatures export MultiLogiset, nmodalities, modalities +export MultiFormula + export UnivariateMin, UnivariateMax, UnivariateSoftMin, UnivariateSoftMax, MultivariateFeature @@ -87,7 +89,7 @@ export VarFeature, export computefeature export scalarlogiset -export initlogiset, ninstances, max_channel_size, worldtype, dimensionality, allworlds, featvalue +export initlogiset, ninstances, maxchannelsize, worldtype, dimensionality, allworlds, featvalue export ScalarMetaCondition diff --git a/src/logisets/check-modes.jl b/src/logisets/check-modes.jl new file mode 100644 index 0000000..2cbc2b0 --- /dev/null +++ b/src/logisets/check-modes.jl @@ -0,0 +1,110 @@ + +############################################################################################ +# Check modes +############################################################################################ + +using SoleLogics: AbstractMultiModalFrame + +""" +Abstract type for model checking modes. + +See also +[`GlobalCheck`](@ref), +[`CenteredCheck`](@ref), +[`WorldCheck`](@ref). +""" +abstract type CheckMode end + +""" +A model checking mode where the formula to check is global and no specific +world is required. + +See also +[`CheckMode`](@ref). +""" +struct GlobalCheck <: CheckMode end; + +abstract type GroundedCheck <: CheckMode end + +""" +A model checking mode where the formula is checked on the central world; +note that the central world must be defined via + +See also +[`CheckMode`](@ref). +""" +struct CenteredCheck <: GroundedCheck end; + +function getworld(fr::AbstractMultiModalFrame{W}, checkmode::CenteredCheck) where {W<:AbstractWorld} + SoleLogics.centeredworld(fr) +end + +""" +A model checking mode where the formula is checked on the central world + +See also +[`CheckMode`](@ref), +[`CenteredCheck`](@ref). +""" +struct WorldCheck{W<:AbstractWorld} <: GroundedCheck + w::W +end + +function getworld(::AbstractMultiModalFrame{W}, checkmode::WorldCheck{W}) where {W<:AbstractWorld} + checkmode.w +end + +function check( + φ::SoleLogics.AbstractFormula, + X::AbstractLogiset, + i_instance::Integer, + checkmode::GlobalCheck, + args...; + kwargs... +) + check(φ, X, i_instance, nothing, args...; kwargs...) +end + +function check( + φ::SoleLogics.AbstractFormula, + X::AbstractLogiset, + i_instance::Integer, + checkmode::GroundedCheck, + args...; + kwargs... +) + check(φ, X, i_instance, getworld(frame(X, i_instance), checkmode), args...; kwargs...) +end + +############################################################################################ + +import SoleLogics: tree + +struct AnchoredFormula{ + F<:AbstractFormula, + C<:CheckMode, +} <: AbstractSyntaxStructure + + formula::F + + checkmode::C +end + +# tree(f::AnchoredFormula) = tree(f.formula) +function SoleLogics.tree(f::AnchoredFormula) + error("Cannot convert object of type AnchoredFormula to a SyntaxTree.") +end + +function syntaxstring(f::AnchoredFormula; kwargs...) + "@$(f.checkmode)($(syntaxstring(f.formula)))" +end + +function check( + φ::AnchoredFormula, + X::AbstractLogiset, + i_instance::Integer, + args...; + kwargs... +) + check(φ.formula, X, i_instance, φ.checkmode, args...; kwargs...) +end diff --git a/src/logisets/check.jl b/src/logisets/check.jl index 404d7e9..8da16a2 100644 --- a/src/logisets/check.jl +++ b/src/logisets/check.jl @@ -1,22 +1,16 @@ -function check( - φ::SoleLogics.AbstractFormula, - X::MultiLogiset, - i_modality::Integer, - i_instance::Integer, - args...; - kwargs..., -) - check(φ, modality(X, i_modality), i_instance, args...; kwargs...) -end +############################################################################################ +# Check algorithms +############################################################################################ function check( φ::SoleLogics.AbstractFormula, X::AbstractLogiset, - i_instance::Integer; + i_instance::Integer, + args...; kwargs... ) - check(tree(φ), X, i_instance; kwargs...) + check(tree(φ), X, i_instance, args...; kwargs...) end function check( @@ -31,12 +25,12 @@ function check( ) where {W<:AbstractWorld,U,FT<:SoleLogics.AbstractFormula} if isnothing(w) - # w = SoleLogics.initialworld(X, i_instance) w = nothing elseif w isa AbstractVector w = w[i_instance] end - @assert SoleLogics.isglobal(φ) || !isnothing(w) "Cannot check non-global formula with no initialworld(s): $(syntaxstring(φ))." + @assert SoleLogics.isglobal(φ) || !isnothing(w) "Please, specify a world in order " * + "to check non-global formula: $(syntaxstring(φ))." setformula(memo_structure::AbstractDict{<:AbstractFormula}, φ::AbstractFormula, val) = memo_structure[SoleLogics.tree(φ)] = val readformula(memo_structure::AbstractDict{<:AbstractFormula}, φ::AbstractFormula) = memo_structure[SoleLogics.tree(φ)] diff --git a/src/logisets/conditions.jl b/src/logisets/conditions.jl index 9d808bf..e658071 100644 --- a/src/logisets/conditions.jl +++ b/src/logisets/conditions.jl @@ -88,6 +88,17 @@ end ############################################################################################ +function featvalue( + feature::AbstractFeature, + X, + i_instance::Integer, + w::W, +) where {W<:AbstractWorld} + featvalue(X, i_instance, w, feature) +end + +############################################################################################ + """ struct FunctionalCondition{FT<:AbstractFeature} <: AbstractCondition{FT} feature::FT diff --git a/src/logisets/dimensional-structures/computefeature.jl b/src/logisets/dimensional-structures/computefeature.jl new file mode 100644 index 0000000..8b73f00 --- /dev/null +++ b/src/logisets/dimensional-structures/computefeature.jl @@ -0,0 +1,52 @@ +using SoleModels: MultivariateFeature, + UnivariateFeature, + UnivariateNamedFeature, + UnivariateValue, + UnivariateMin, + UnivariateMax, + UnivariateSoftMin, + UnivariateSoftMax, + i_variable, + alpha + +import SoleModels: computefeature, computeunivariatefeature + +function computefeature(f::MultivariateFeature{U}, featchannel::Any)::U where {U} + (f.f(featchannel)) +end + +function computeunivariatefeature(f::UnivariateFeature{U}, varchannel::Union{T,AbstractArray{T}}) where {U,T} + (f.f(SoleBase.vectorize(varchannel);))::U +end +function computeunivariatefeature(f::UnivariateNamedFeature, varchannel::Union{T,AbstractArray{T}}) where {T} + @error "Cannot intepret UnivariateNamedFeature on any structure at all." +end +function computeunivariatefeature(f::UnivariateValue{U}, varchannel::Union{T,AbstractArray{T}}) where {U<:Real,T} + varchannel::U +end +function computeunivariatefeature(f::UnivariateMin{U}, varchannel::AbstractArray{T}) where {U<:Real,T} + (minimum(varchannel))::U +end +function computeunivariatefeature(f::UnivariateMax{U}, varchannel::AbstractArray{T}) where {U<:Real,T} + (maximum(varchannel))::U +end +function computeunivariatefeature(f::UnivariateSoftMin{U}, varchannel::AbstractArray{T}) where {U<:Real,T} + utils.softminimum(varchannel, alpha(f))::U +end +function computeunivariatefeature(f::UnivariateSoftMax{U}, varchannel::AbstractArray{T}) where {U<:Real,T} + utils.softmaximum(varchannel, alpha(f))::U +end + +# simplified propositional cases: +function computeunivariatefeature(f::UnivariateMin{U}, varchannel::T) where {U<:Real,T} + (minimum(varchannel))::U +end +function computeunivariatefeature(f::UnivariateMax{U}, varchannel::T) where {U<:Real,T} + (maximum(varchannel))::U +end +function computeunivariatefeature(f::UnivariateSoftMin{U}, varchannel::T) where {U<:Real,T} + varchannel::U +end +function computeunivariatefeature(f::UnivariateSoftMax{U}, varchannel::T) where {U<:Real,T} + varchannel::U +end diff --git a/src/logisets/dimensional-structures/dataset-bindings.jl b/src/logisets/dimensional-structures/dataset-bindings.jl index 4e54471..6f75d23 100644 --- a/src/logisets/dimensional-structures/dataset-bindings.jl +++ b/src/logisets/dimensional-structures/dataset-bindings.jl @@ -1,77 +1,173 @@ +using SoleModels: AbstractUnivariateFeature + using SoleData: AbstractDimensionalDataset, - AbstractDimensionalInstance, - AbstractDimensionalChannel, UniformDimensionalDataset, DimensionalInstance, DimensionalChannel +import SoleData: ninstances, nvariables + +import SoleModels: scalarlogiset, + initlogiset, initlogiset, allworlds, + featchannel, readfeature, featvalue, vareltype + function initlogiset( dataset::AbstractDimensionalDataset, features::AbstractVector{<:VarFeature}, ) _ninstances = ninstances(dataset) - _max_channel_size = max_channel_size(dataset) - W = worldtype(dataset) + _maxchannelsize = maxchannelsize(dataset) + + _worldtype(dataset::AbstractDimensionalDataset{T,2}) where {T} = OneWorld + _worldtype(dataset::AbstractDimensionalDataset{T,3}) where {T} = Interval{Int} + _worldtype(dataset::AbstractDimensionalDataset{T,4}) where {T} = Interval2D{Int} + + function _worldtype(dataset::AbstractDimensionalDataset) + error("Cannot initialize logiset with dimensional dataset " * + "with ndims = $(ndims(dataset)). Please, provide a " * + "dataset structure of size X × Y × ... × nvariables × ninstances." * + "Note that, currently, only ndims ≤ 4 (dimensionality = 2) is supported." + ) + end + + W = _worldtype(dataset) N = dimensionality(dataset) features = UniqueVector(features) nfeatures = length(features) U = Union{featvaltype.(features)...} - featstruct = Array{U,length(_max_channel_size)*2+2}( + featstruct = Array{U,length(_maxchannelsize)*2+2}( undef, - vcat([[s,s+1] for s in _max_channel_size]...)..., + vcat([[s,s+1] for s in _maxchannelsize]...)..., _ninstances, length(features) ) return UniformFullDimensionalLogiset{U,W,N}(featstruct, features) end +function allworlds( + dataset::Union{UniformDimensionalDataset,AbstractArray}, + i_instance::Integer +) + allworlds(FullDimensionalFrame(channelsize(dataset))) +end + function featchannel( - X::AbstractDimensionalDataset, + dataset::AbstractDimensionalDataset, i_instance::Integer, f::AbstractFeature, ) - get_instance(X, i_instance) + get_instance(dataset, i_instance) end function readfeature( - X::AbstractDimensionalDataset, + dataset::AbstractDimensionalDataset, featchannel::Any, w::W, f::VarFeature{U}, ) where {U,W<:AbstractWorld} - w_values = interpret_world(w, featchannel) - computefeature(f, w_values)::U + _interpret_world(::OneWorld, instance::DimensionalInstance{T,1}) where {T} = instance + _interpret_world(w::Interval, instance::DimensionalInstance{T,2}) where {T} = instance[w.x:w.y-1,:] + _interpret_world(w::Interval2D, instance::DimensionalInstance{T,3}) where {T} = instance[w.x.x:w.x.y-1,w.y.x:w.y.y-1,:] + wchannel = _interpret_world(w, featchannel) + computefeature(f, wchannel)::U end -interpret_world(::OneWorld, instance::DimensionalInstance{T,1}) where {T} = instance -interpret_world(w::Interval2D, instance::DimensionalInstance{T,3}) where {T} = instance[w.x.x:w.x.y-1,w.y.x:w.y.y-1,:] -interpret_world(w::Interval, instance::DimensionalInstance{T,2}) where {T} = instance[w.x:w.y-1,:] - function featvalue( - X::AbstractDimensionalDataset, + dataset::AbstractDimensionalDataset, i_instance::Integer, w::W, feature::AbstractFeature, ) where {W<:AbstractWorld} - readfeature(X, featchannel(X, i_instance, feature), w, feature) + readfeature(dataset, featchannel(dataset, i_instance, feature), w, feature) end -function featvalue( - f::AbstractFeature, - X::AbstractDimensionalDataset, +function vareltype( + dataset::AbstractDimensionalDataset{T}, + i_variable::Integer +) where {T} + T +end + +############################################################################################ + +using DataFrames + +function initlogiset( + dataset::AbstractDataFrame, + features::AbstractVector{<:VarFeature}, +) + _ninstances = nrow(dataset) + + cube, varnames = SoleData.dataframe2cube(dataset; dry_run = true) + + initlogiset(cube, features) +end + +function allworlds( + dataset::AbstractDataFrame, + i_instance::Integer +) + # dataset_cube, varnames = SoleData.dataframe2cube(dataset; dry_run = true) + # allworlds(FullDimensionalFrame(channelsize(dataset_cube))) + allworlds(FullDimensionalFrame(size(dataset[i_instance,1]))) +end + +function featchannel( + dataset::AbstractDataFrame, i_instance::Integer, + f::AbstractFeature, +) + @views dataset[i_instance, :] +end + +function readfeature( + dataset::AbstractDataFrame, + featchannel::Any, w::W, -) where {W<:AbstractWorld} - featvalue(X, i_instance, w, f) + f::VarFeature{U}, +) where {U,W<:AbstractWorld} + _interpret_world(::OneWorld, instance::DataFrameRow) = instance + _interpret_world(w::Interval, instance::DataFrameRow) = map(varchannel->varchannel[w.x:w.y-1], instance) + _interpret_world(w::Interval2D, instance::DataFrameRow) = map(varchannel->varchannel[w.x.x:w.x.y-1,w.y.x:w.y.y-1], instance) + wchannel = _interpret_world(w, featchannel) + computefeature(f, wchannel)::U end -worldtype(d::AbstractDimensionalDataset{T,2}) where {T} = OneWorld -worldtype(d::AbstractDimensionalDataset{T,3}) where {T} = Interval{Int} -worldtype(d::AbstractDimensionalDataset{T,4}) where {T} = Interval2D{Int} +function featchannel( + dataset::AbstractDataFrame, + i_instance::Integer, + f::AbstractUnivariateFeature, +) + @views dataset[i_instance, SoleModels.i_variable(f)] +end -frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance::Integer) = frame(X) -frame(X::Union{UniformDimensionalDataset,AbstractArray}) = FullDimensionalFrame(channel_size(X)) +function readfeature( + dataset::AbstractDataFrame, + featchannel::Any, + w::W, + f::AbstractUnivariateFeature{U}, +) where {U,W<:AbstractWorld} + _interpret_world(::OneWorld, varchannel::T) where {T} = varchannel + _interpret_world(w::Interval, varchannel::AbstractArray{T,1}) where {T} = varchannel[w.x:w.y-1] + _interpret_world(w::Interval2D, varchannel::AbstractArray{T,2}) where {T} = varchannel[w.x.x:w.x.y-1,w.y.x:w.y.y-1] + wchannel = _interpret_world(w, featchannel) + computeunivariatefeature(f, wchannel)::U +end + +function featvalue( + dataset::AbstractDataFrame, + i_instance::Integer, + w::W, + feature::AbstractFeature, +) where {W<:AbstractWorld} + readfeature(dataset, featchannel(dataset, i_instance, feature), w, feature) +end -allworlds(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance::Integer) = allworlds(frame(X, i_instance)) +function vareltype( + dataset::AbstractDataFrame, + i_variable::Integer +) + eltype(eltype(dataset[:,i_variable])) +end diff --git a/src/logisets/dimensional-structures/logiset.jl b/src/logisets/dimensional-structures/logiset.jl index a39e180..330e0b9 100644 --- a/src/logisets/dimensional-structures/logiset.jl +++ b/src/logisets/dimensional-structures/logiset.jl @@ -11,15 +11,19 @@ See also """ abstract type AbstractUniformFullDimensionalLogiset{U,N,W<:AbstractWorld,FT<:AbstractFeature,FR<:FullDimensionalFrame{N,W}} <: AbstractLogiset{W,U,FT,FR} end -function channel_size(X::AbstractUniformFullDimensionalLogiset) - error("Please, provide method channel_size(::$(typeof(X))).") +function maxchannelsize(X::AbstractUniformFullDimensionalLogiset) + error("Please, provide method maxchannelsize(::$(typeof(X))).") +end + +function channelsize(X::AbstractUniformFullDimensionalLogiset) + error("Please, provide method channelsize(::$(typeof(X))).") end function dimensionality(X::AbstractUniformFullDimensionalLogiset{U,N}) where {U,N} N end -frame(X::AbstractUniformFullDimensionalLogiset, i_instance::Integer) = FullDimensionalFrame(channel_size(X)) +frame(X::AbstractUniformFullDimensionalLogiset, i_instance::Integer) = FullDimensionalFrame(channelsize(X, i_instance)) ############################################################################################ @@ -85,9 +89,10 @@ features(X::UniformFullDimensionalLogiset) = X.features ############################################################################################ -channel_size(X::UniformFullDimensionalLogiset{U,OneWorld}) where {U} = () -channel_size(X::UniformFullDimensionalLogiset{U,<:Interval}) where {U} = (size(X, 1),) -channel_size(X::UniformFullDimensionalLogiset{U,<:Interval2D}) where {U} = (size(X, 1),size(X, 3)) +maxchannelsize(X::UniformFullDimensionalLogiset) = channelsize(X, 0) +channelsize(X::UniformFullDimensionalLogiset{U,OneWorld}, i_instance::Integer) where {U} = () +channelsize(X::UniformFullDimensionalLogiset{U,<:Interval}, i_instance::Integer) where {U} = (size(X, 1),) +channelsize(X::UniformFullDimensionalLogiset{U,<:Interval2D}, i_instance::Integer) where {U} = (size(X, 1),size(X, 3)) ############################################################################################ @@ -391,24 +396,44 @@ end ############################################################################################ -function displaystructure(X::UniformFullDimensionalLogiset{U,W,N}; indent_str = "", include_ninstances = true) where {U,W<:AbstractWorld,N} +function displaystructure( + X::UniformFullDimensionalLogiset{U,W,N}; + indent_str = "", + include_ninstances = true, + include_worldtype = missing, + include_featvaltype = missing, + include_featuretype = missing, + include_frametype = missing, +) where {U,W<:AbstractWorld,N} padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) pieces = [] - push!(pieces, "UniformFullDimensionalLogiset ($(humansize(X)))") - push!(pieces, "$(padattribute("worldtype:", worldtype(X)))") - push!(pieces, "$(padattribute("featvaltype:", featvaltype(X)))") - push!(pieces, "$(padattribute("featuretype:", featuretype(X)))") - push!(pieces, "$(padattribute("frametype:", frametype(X)))") + push!(pieces, "UniformFullDimensionalLogiset " * + (dimensionality(X) == 0 ? "of dimensionality 0" : + dimensionality(X) == 1 ? "of channel size $(maxchannelsize(X))" : + "of channel size $(join(maxchannelsize(X), " × "))")* + " ($(humansize(X)))") + if ismissing(include_worldtype) || include_worldtype != worldtype(X) + push!(pieces, "$(padattribute("worldtype:", worldtype(X)))") + end + if ismissing(include_featvaltype) || include_featvaltype != featvaltype(X) + push!(pieces, "$(padattribute("featvaltype:", featvaltype(X)))") + end + if ismissing(include_featuretype) || include_featuretype != featuretype(X) + push!(pieces, "$(padattribute("featuretype:", featuretype(X)))") + end + if ismissing(include_frametype) || include_frametype != frametype(X) + push!(pieces, "$(padattribute("frametype:", frametype(X)))") + end if include_ninstances push!(pieces, "$(padattribute("# instances:", ninstances(X)))") end push!(pieces, "$(padattribute("size × eltype:", "$(size(X.featstruct)) × $(eltype(X.featstruct))"))") - push!(pieces, "$(padattribute("dimensionality:", dimensionality(X)))") - push!(pieces, "$(padattribute("channel_size:", channel_size(X)))") + # push!(pieces, "$(padattribute("dimensionality:", dimensionality(X)))") + # push!(pieces, "$(padattribute("maxchannelsize:", maxchannelsize(X)))") # push!(pieces, "$(padattribute("# features:", nfeatures(X)))") push!(pieces, "$(padattribute("features:", "$(nfeatures(X)) -> $(displaysyntaxvector(features(X)))"))") - return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" + return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") end ############################################################################################ diff --git a/src/logisets/dimensional-structures/main.jl b/src/logisets/dimensional-structures/main.jl index c4bf429..ab5468b 100644 --- a/src/logisets/dimensional-structures/main.jl +++ b/src/logisets/dimensional-structures/main.jl @@ -12,10 +12,10 @@ using SoleBase using SoleLogics using SoleLogics: AbstractFormula, AbstractWorld, AbstractRelation using SoleLogics: AbstractFrame, AbstractDimensionalFrame, FullDimensionalFrame -import SoleLogics: worldtype, accessibles, allworlds, alphabet, initialworld +import SoleLogics: worldtype, accessibles, allworlds, alphabet using SoleData -import SoleData: _isnan, hasnans, nvariables, max_channel_size, channel_size +import SoleData: _isnan, hasnans, nvariables, maxchannelsize, channelsize import SoleData: instance, get_instance, instances, concatdatasets import SoleData: displaystructure import SoleData: dimensionality @@ -23,6 +23,7 @@ import SoleData: dimensionality using SoleModels using SoleModels.utils + using SoleModels: Aggregator, AbstractCondition using SoleModels: BoundedScalarConditions using SoleModels: CanonicalFeatureGeq, CanonicalFeatureGeqSoft, CanonicalFeatureLeq, CanonicalFeatureLeqSoft @@ -41,11 +42,9 @@ import SoleModels: nmetaconditions import SoleModels: capacity, nmemoizedvalues using SoleModels: memoizationinfo - -import SoleModels: initlogiset import SoleModels: worldtype, allworlds, featvalue, featvalue! import SoleModels: featchannel, readfeature, featvalues!, allfeatvalues -import SoleData: get_instance, ninstances, nvariables, channel_size, eltype +import SoleData: get_instance, ninstances, nvariables, channelsize, eltype export nvariables @@ -56,7 +55,9 @@ include("logiset.jl") include("onestep-memosets.jl") -export initlogiset, ninstances, max_channel_size, worldtype, dimensionality, allworlds, featvalue +export initlogiset, ninstances, maxchannelsize, worldtype, dimensionality, allworlds, featvalue + +include("computefeature.jl") # Bindings for interpreting dataset structures as logisets include("dataset-bindings.jl") diff --git a/src/logisets/dimensional-structures/onestep-memosets.jl b/src/logisets/dimensional-structures/onestep-memosets.jl index 40614a4..5f29bd5 100644 --- a/src/logisets/dimensional-structures/onestep-memosets.jl +++ b/src/logisets/dimensional-structures/onestep-memosets.jl @@ -253,7 +253,7 @@ end ############################################################################################ function concatdatasets(Xms::UniformFullDimensionalOneStepRelationalMemoset{U,W,N}...) where {U,W<:AbstractWorld,N} - UniformFullDimensionalOneStepRelationalMemoset(cat([Xm.d for Xm in Xms]...; dims=1+2*N)) + UniformFullDimensionalOneStepRelationalMemoset{U,W,N}(cat([Xm.d for Xm in Xms]...; dims=1+2*N)) end isminifiable(::UniformFullDimensionalOneStepRelationalMemoset) = true @@ -268,14 +268,32 @@ end ############################################################################################ -function displaystructure(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,W,N}; indent_str = "", include_ninstances = true, include_nmetaconditions = true, include_nrelations = true) where {U,W<:AbstractWorld,N} +function displaystructure( + Xm::UniformFullDimensionalOneStepRelationalMemoset{U,W,N}; + indent_str = "", + include_ninstances = true, + include_nmetaconditions = true, + include_nrelations = true, + include_worldtype = missing, + include_featvaltype = missing, + include_featuretype = missing, + include_frametype = missing, +) where {U,W<:AbstractWorld,N} padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) pieces = [] push!(pieces, "$(nameof(typeof(Xm))) ($(memoizationinfo(Xm)), $(humansize(Xm)))") - push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") - push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") - push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") - push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + if ismissing(include_worldtype) || include_worldtype != worldtype(Xm) + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + end + if ismissing(include_featvaltype) || include_featvaltype != featvaltype(Xm) + push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") + end + # if ismissing(include_featuretype) || include_featuretype != featuretype(Xm) + # push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") + # end + # if ismissing(include_frametype) || include_frametype != frametype(Xm) + # push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + # end if include_ninstances push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") end @@ -287,5 +305,5 @@ function displaystructure(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,W end push!(pieces, "$(padattribute("size × eltype:", "$(size(innerstruct(Xm))) × $(eltype(innerstruct(Xm)))"))") - return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" + return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") end diff --git a/src/logisets/features.jl b/src/logisets/features.jl index a2d468f..9f57716 100644 --- a/src/logisets/features.jl +++ b/src/logisets/features.jl @@ -7,7 +7,7 @@ import SoleLogics: syntaxstring Abstract type for features of worlds of [Kripke structures](https://en.wikipedia.org/wiki/Kripke_structure_(model_checking). -See also [`featvaltype`](@ref), [`AbstractWorld`](@ref). +See also [`VarFeature`](@ref), [`featvaltype`](@ref), [`AbstractWorld`](@ref). """ abstract type AbstractFeature end diff --git a/src/logisets/logiset.jl b/src/logisets/logiset.jl index b007a32..40b3c67 100644 --- a/src/logisets/logiset.jl +++ b/src/logisets/logiset.jl @@ -1,7 +1,7 @@ using SoleLogics: AbstractKripkeStructure, AbstractInterpretationSet, AbstractFrame using SoleLogics: TruthValue import SoleLogics: alphabet, frame, check -import SoleLogics: accessibles, allworlds, nworlds, initialworld +import SoleLogics: accessibles, allworlds, nworlds import SoleLogics: worldtype, frametype """ @@ -129,17 +129,16 @@ end hasnans(::AbstractLogiset) = any.(isnan, allfeatvalues(X)) -############################################################################################ - -function featvalue( - feature::AbstractFeature, - X::AbstractLogiset{W}, +function initialworldset( + X::AbstractLogiset, i_instance::Integer, - w::W, -) where {W<:AbstractWorld} - featvalue(X, i_instance, w, feature) + args... +) + initialworldset(frame(X, i_instance), args...) end +############################################################################################ + function Base.show(io::IO, X::AbstractLogiset; kwargs...) println(io, displaystructure(X; kwargs...)) end @@ -266,13 +265,29 @@ function concatdatasets(Xs::ExplicitBooleanLogiset...) ExplicitBooleanLogiset(vcat([X.d for X in Xs]...)) end -function displaystructure(X::ExplicitBooleanLogiset; indent_str = "", include_ninstances = true) +function displaystructure( + X::ExplicitBooleanLogiset; + indent_str = "", + include_ninstances = true, + include_worldtype = missing, + include_featvaltype = missing, + include_featuretype = missing, + include_frametype = missing, +) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) out = "ExplicitBooleanLogiset ($(humansize(X)))\n" - out *= indent_str * "├ " * padattribute("worldtype:", worldtype(X)) * "\n" - out *= indent_str * "├ " * padattribute("featvaltype:", featvaltype(X)) * "\n" - out *= indent_str * "├ " * padattribute("featuretype:", featuretype(X)) * "\n" - out *= indent_str * "├ " * padattribute("frametype:", frametype(X)) * "\n" + if ismissing(include_worldtype) || include_worldtype != worldtype(X) + out *= indent_str * "├ " * padattribute("worldtype:", worldtype(X)) * "\n" + end + if ismissing(include_featvaltype) || include_featvaltype != featvaltype(X) + out *= indent_str * "├ " * padattribute("featvaltype:", featvaltype(X)) * "\n" + end + if ismissing(include_featuretype) || include_featuretype != featuretype(X) + out *= indent_str * "├ " * padattribute("featuretype:", featuretype(X)) * "\n" + end + if ismissing(include_frametype) || include_frametype != frametype(X) + out *= indent_str * "├ " * padattribute("frametype:", frametype(X)) * "\n" + end if include_ninstances out *= indent_str * "├ " * padattribute("# instances:", ninstances(X)) * "\n" end @@ -383,13 +398,29 @@ function concatdatasets(Xs::ExplicitLogiset...) ExplicitBooleanLogiset(vcat([X.d for X in Xs]...)) end -function displaystructure(X::ExplicitLogiset; indent_str = "", include_ninstances = true) +function displaystructure( + X::ExplicitLogiset; + indent_str = "", + include_ninstances = true, + include_worldtype = missing, + include_featvaltype = missing, + include_featuretype = missing, + include_frametype = missing, +) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) out = "ExplicitBooleanLogiset ($(humansize(X)))\n" - out *= indent_str * "├ " * padattribute("worldtype:", "$(worldtype(X))") * "\n" - out *= indent_str * "├ " * padattribute("featvaltype:", "$(featvaltype(X))") * "\n" - out *= indent_str * "├ " * padattribute("featuretype:", "$(featuretype(X))") * "\n" - out *= indent_str * "├ " * padattribute("frametype:", "$(frametype(X))") * "\n" + if ismissing(include_worldtype) || include_worldtype != worldtype(X) + out *= indent_str * "├ " * padattribute("worldtype:", worldtype(X)) * "\n" + end + if ismissing(include_featvaltype) || include_featvaltype != featvaltype(X) + out *= indent_str * "├ " * padattribute("featvaltype:", featvaltype(X)) * "\n" + end + if ismissing(include_featuretype) || include_featuretype != featuretype(X) + out *= indent_str * "├ " * padattribute("featuretype:", featuretype(X)) * "\n" + end + if ismissing(include_frametype) || include_frametype != frametype(X) + out *= indent_str * "├ " * padattribute("frametype:", frametype(X)) * "\n" + end if include_ninstances out *= indent_str * "├ " * padattribute("# instances:", "$(ninstances(X))") * "\n" end diff --git a/src/logisets/main.jl b/src/logisets/main.jl index 6e6582a..ae833f5 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -19,7 +19,7 @@ include("conditions.jl") # Templates for formulas of conditions (e.g., templates for ⊤, p, ⟨R⟩p, etc.) include("templated-formulas.jl") -export check, accessibles, allworlds, representatives, initialworld +export accessibles, allworlds, representatives # Interface for representative accessibles, for optimized model checking on specific frames include("representatives.jl") @@ -38,7 +38,10 @@ export MultiLogiset, modalities, worldtypes, nmodalities # Multiframe version of logisets, for representing multimodal datasets include("multilogiset.jl") +export check, AnchoredFormula + # Model checking algorithms for logisets and multilogisets +include("check-modes.jl") include("check.jl") # TODO remove? diff --git a/src/logisets/memosets.jl b/src/logisets/memosets.jl index cac5f76..6375ad9 100644 --- a/src/logisets/memosets.jl +++ b/src/logisets/memosets.jl @@ -41,21 +41,37 @@ function memoizationinfo(Xm::AbstractMemoset) end end -function displaystructure(Xm::AbstractMemoset; indent_str = "", include_ninstances = true) +function displaystructure( + Xm::AbstractMemoset; + indent_str = "", + include_ninstances = true, + include_worldtype = missing, + include_featvaltype = missing, + include_featuretype = missing, + include_frametype = missing, +) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) pieces = [] push!(pieces, "") - push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") - push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") - push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") - push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + if ismissing(include_worldtype) || include_worldtype != worldtype(Xm) + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + end + if ismissing(include_featvaltype) || include_featvaltype != featvaltype(Xm) + push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") + end + if ismissing(include_featuretype) || include_featuretype != featuretype(Xm) + push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") + end + if ismissing(include_frametype) || include_frametype != frametype(Xm) + push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + end if include_ninstances push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") end # push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") return "$(nameof(typeof(Xm))) ($(memoizationinfo(Xm)), $(humansize(Xm)))" * - join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" + join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") end ############################################################################################ @@ -179,18 +195,28 @@ fullmemo(Xm::FullMemoset) = Xm hasnans(::FullMemoset) = false -function displaystructure(Xm::FullMemoset; indent_str = "", include_ninstances = true) +function displaystructure( + Xm::FullMemoset; + indent_str = "", + include_ninstances = true, + include_worldtype = missing, + include_featvaltype = missing, + include_featuretype = missing, + include_frametype = missing, +) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) pieces = [] push!(pieces, "") - push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + if ismissing(include_worldtype) || include_worldtype != worldtype(Xm) + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + end if include_ninstances push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") end # push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") return "$(nameof(typeof(Xm))) ($(memoizationinfo(Xm)), $(humansize(Xm)))" * - join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" + join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") end # Base.size(::FullMemoset) = () diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index 0e7c77e..d97fae4 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -1,3 +1,5 @@ +import SoleData: modality, nmodalities + """ struct MultiLogiset{L<:AbstractLogiset} modalities :: Vector{L} @@ -20,7 +22,7 @@ struct MultiLogiset{L<:AbstractLogiset} end function MultiLogiset{L}(X::AbstractVector) where {L<:AbstractLogiset} X = collect(X) - @assert length(X) > 0 && length(unique(ninstances.(X))) == 1 "Can't create an empty MultiLogiset or with mismatching number of instances (nmodalities: $(length(X)), modality_sizes: $(ninstances.(X)))." + @assert length(X) > 0 && length(unique(ninstances.(X))) == 1 "Cannot create an empty MultiLogiset or with mismatching number of instances (nmodalities: $(length(X)), modality_sizes: $(ninstances.(X)))." new{L}(X) end function MultiLogiset{L}() where {L<:AbstractLogiset} @@ -83,23 +85,26 @@ function Base.show(io::IO, X::MultiLogiset; kwargs...) end function displaystructure(X::MultiLogiset; indent_str = "", include_ninstances = true) - out = "MultiLogiset($(humansize(X)))\n" + pieces = [] + push!(pieces, "MultiLogiset with $(nmodalities(X)) modalities ($(humansize(X)))") + # push!(pieces, indent_str * "├ # modalities:\t$(nmodalities(X))") if include_ninstances - out *= indent_str * "├ # instances:\t$(ninstances(X))\n" + push!(pieces, indent_str * "├ # instances:\t$(ninstances(X))") end - out *= indent_str * "├ modalitytype:\t$(modalitytype(X))\n" - out *= indent_str * "├ # modalities:\t$(nmodalities(X))\n" + # push!(pieces, indent_str * "├ modalitytype:\t$(modalitytype(X))") for (i_modality, mod) in enumerate(modalities(X)) + out = "" if i_modality == nmodalities(X) - out *= "$(indent_str)└ " + out *= "$(indent_str)└" else - out *= "$(indent_str)├ " + out *= "$(indent_str)├" end out *= "[$(i_modality)] " # \t\t\t$(humansize(mod))\t(worldtype: $(worldtype(mod)))" - out *= displaystructure(mod; indent_str = indent_str * (i_modality == nmodalities(X) ? " " : "│ "), include_ninstances = false) * "" + out *= displaystructure(mod; indent_str = indent_str * (i_modality == nmodalities(X) ? " " : "│ "), include_ninstances = false) + push!(pieces, out) end - out + return join(pieces, "\n") end @@ -148,8 +153,74 @@ end # Base.size(X::MultiLogiset) = map(size, modalities(X)) -# # max_channel_size(X::MultiLogiset) = map(max_channel_size, modalities(X)) # TODO: figure if this is useless or not. Note: channel_size doesn't make sense at this point. +# # maxchannelsize(X::MultiLogiset) = map(maxchannelsize, modalities(X)) # TODO: figure if this is useless or not. Note: channelsize doesn't make sense at this point. # nfeatures(X::MultiLogiset) = map(nfeatures, modalities(X)) # Note: used for safety checks in fit_tree.jl # # nrelations(X::MultiLogiset) = map(nrelations, modalities(X)) # TODO: figure if this is useless or not # nfeatures(X::MultiLogiset, i_modality::Integer) = nfeatures(modality(X, i_modality)) # nrelations(X::MultiLogiset, i_modality::Integer) = nrelations(modality(X, i_modality)) + +############################################################################################ + +using SoleLogics: AbstractFormula, AbstractSyntaxStructure, AbstractOperator +import SoleLogics: syntaxstring, joinformulas + +import SoleLogics: tree + +""" +A logical formula that can be checked on a `MultiLogiset`, associating +a set of subformulas to each modality +""" +struct MultiFormula{ + F<:AbstractFormula, +} <: AbstractSyntaxStructure + formulas::Dict{Int,F} +end + +function SoleLogics.tree(f::MultiFormula) + error("Cannot convert object of type MultiFormula to a SyntaxTree.") +end + +function syntaxstring(f::MultiFormula; kwargs...) + join(["{$(i_modality)}($(syntaxstring(f.formulas[i_modality])))" for i_modality in sort(collect(keys(f.formulas)))], " ∧ ") +end + +function joinformulas(op::typeof(CONJUNCTION), children::NTuple{N,MultiFormula{F}}) where {N,F} + formulas = Dict{Int,F}() + i_modalities = unique(vcat(collect.(keys.([ch.formulas for ch in children]))...)) + for i_modality in i_modalities + chs = filter(ch->haskey(ch.formulas, i_modality), children) + fs = filter(ch->ch.formulas[i_modality], chs) + formulas[i_modality] = joinformulas(op, fs) + end + return MultiFormula(formulas) +end + +function check( + φ::MultiFormula, + X::MultiLogiset, + i_instance::Integer, + args...; + kwargs..., +) + # TODO in the fuzzy case: use collatetruth(fuzzy algebra, ∧, ...) + all([check(f, X, i_modality, i_instance, args...; kwargs...) + for (i_modality, f) in φ.formulas]) +end + +function check( + φ::SoleLogics.AbstractFormula, + X::MultiLogiset, + i_modality::Integer, + i_instance::Integer, + args...; + kwargs..., +) + check(φ, modality(X, i_modality), i_instance, args...; kwargs...) +end + + +# # TODO join MultiFormula leads to a SyntaxTree with MultiFormula children +# function joinformulas(op::AbstractOperator, children::NTuple{N,MultiFormula{F}}) where {N,F} +# end + +# TODO MultiFormula parser diff --git a/src/logisets/scalar/conditions.jl b/src/logisets/scalar/conditions.jl index f100af5..9ee3cc6 100644 --- a/src/logisets/scalar/conditions.jl +++ b/src/logisets/scalar/conditions.jl @@ -301,7 +301,7 @@ struct BoundedScalarConditions{C<:ScalarCondition} <: AbstractConditionalAlphabe thresholds::Vector{<:Vector}, ) where {C<:ScalarCondition} length(metaconditions) != length(thresholds) && - error("Can't instantiate BoundedScalarConditions with mismatching " * + error("Cannot instantiate BoundedScalarConditions with mismatching " * "number of `metaconditions` and `thresholds` " * "($(metaconditions) != $(thresholds)).") grouped_featconditions = collect(zip(metaconditions, thresholds)) diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 92f4139..6670a63 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -1,13 +1,14 @@ -using DataFrames +using SoleData: AbstractMultiModalDataset +import SoleData: ninstances, nvariables, nmodalities, eachmodality function initlogiset(dataset, features) error("Please, provide method initlogiset(dataset::$(typeof(dataset)), features::$(typeof(features))).") end function ninstances(dataset) - error("Please, provide method ninstances(dataset::$(typeof(dataset)).") + error("Please, provide method ninstances(dataset::$(typeof(dataset))).") end -function worldtype(dataset) - error("Please, provide method worldtype(dataset::$(typeof(dataset)).") +function nvariables(dataset) + error("Please, provide method nvariables(dataset::$(typeof(dataset))).") end function allworlds(dataset, i_instance) error("Please, provide method allworlds(dataset::$(typeof(dataset)), i_instance::Integer).") @@ -15,16 +16,151 @@ end function featvalue(dataset, i_instance, w, feature) error("Please, provide method featvalue(dataset::$(typeof(dataset)), i_instance::Integer, w::$(typeof(w)), feature::$(typeof(feature))).") end +function vareltype(dataset, i_variable) + error("Please, provide method vareltype(dataset::$(typeof(dataset)), i_variable::Integer).") +end + +# Multimodal dataset interface +function ismultimodal(dataset) + false +end +function nmodalities(dataset) + error("Please, provide method nmodalities(dataset::$(typeof(dataset))).") +end +function eachmodality(dataset) + error("Please, provide method eachmodality(dataset::$(typeof(dataset))).") +end + +function ismultimodal(dataset::AbstractMultiModalDataset) + true +end + +function ismultimodal(dataset::Union{AbstractVector,Tuple}) + true +end +function nmodalities(dataset::Union{AbstractVector,Tuple}) + length(dataset) +end +function eachmodality(dataset::Union{AbstractVector,Tuple}) + dataset +end + """ scalarlogiset(dataset, features::AbstractVector{<:VarFeature}) -Converts a dataset structure to a logiset with scalar-valued features. +Converts a dataset structure (with variables) to a logiset with scalar-valued features. +If `dataset` is not a multimodal dataset, the following methods should be defined: + +```julia + initlogiset(dataset, features::AbstractVector{<:VarFeature}) + ninstances(dataset) + nvariables(dataset) + allworlds(dataset, i_instance::Integer) + featvalue(dataset, i_instance::Integer, w::AbstractWorld, feature::VarFeature) + vareltype(dataset, i_variable::Integer) +``` + +If `dataset` represents a multimodal dataset, the following methods should be defined, +while its modalities (iterated via `eachmodality`) should provide the methods above: + +```julia + ismultimodal(dataset) + nmodalities(dataset) + eachmodality(dataset) +``` + +See also +[`AbstractLogiset`](@ref), +[`VarFeature`](@ref), +[`ScalarCondition`](@ref). """ function scalarlogiset( dataset, - features::AbstractVector{<:VarFeature}, + features::Union{Nothing,AbstractVector{<:VarFeature},AbstractVector{<:Union{Nothing,<:AbstractVector}}} = nothing; + # + use_full_memoization :: Union{Bool,Type{<:Union{AbstractOneStepMemoset,AbstractFullMemoset}}} = true, + # + conditions :: Union{Nothing,AbstractVector{<:AbstractCondition},AbstractVector{<:Union{Nothing,AbstractVector}}} = nothing, + relations :: Union{Nothing,AbstractVector{<:AbstractRelation},AbstractVector{<:Union{Nothing,AbstractVector}}} = nothing, + use_onestep_memoization :: Union{Bool,Type{<:AbstractOneStepMemoset}} = !isnothing(conditions) && !isnothing(relations), + onestep_precompute_globmemoset :: Bool = (use_onestep_memoization != false), + onestep_precompute_relmemoset :: Bool = false, ) + some_features_were_specified = !isnothing(features) + + if ismultimodal(dataset) + + kwargs = (; + use_full_memoization = use_full_memoization, + use_onestep_memoization = use_onestep_memoization, + onestep_precompute_globmemoset = onestep_precompute_globmemoset, + onestep_precompute_relmemoset = onestep_precompute_relmemoset, + ) + + features = begin + if features isa Union{Nothing,AbstractVector{<:VarFeature}} + fill(features, nmodalities(dataset)) + elseif features isa AbstractVector{<:Union{Nothing,AbstractVector}} + features + else + error("Cannot build multimodal scalar logiset with features " * + "$(displaysyntaxvector(features)).") + end + end + + conditions = begin + if conditions isa Union{Nothing,AbstractVector{<:AbstractCondition}} + fill(conditions, nmodalities(dataset)) + elseif conditions isa AbstractVector{<:Union{Nothing,AbstractVector}} + conditions + else + error("Cannot build multimodal scalar logiset with conditions " * + "$(displaysyntaxvector(conditions)).") + end + end + + relations = begin + if relations isa Union{Nothing,AbstractVector{<:AbstractRelation}} + fill(relations, nmodalities(dataset)) + elseif relations isa AbstractVector{<:Union{Nothing,AbstractVector}} + relations + else + error("Cannot build multimodal scalar logiset with relations " * + "$(displaysyntaxvector(relations)).") + end + end + + return MultiLogiset([ + scalarlogiset(_dataset, _features; conditions = _conditions, relations = _relations, kwargs...) + for (_dataset, _features, _conditions, _relations) in + zip(eachmodality(dataset), features, conditions, relations) + ]) + end + + if isnothing(features) + features = collect(Iterators.flatten([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:nvariables(dataset)])) + end + + features_ok = filter(f->isconcretetype(featvaltype(f)), features) + features_notok = filter(f->!isconcretetype(featvaltype(f)), features) + + if length(features_notok) > 0 + if all(preserveseltype, features_notok) && all(f->f isa AbstractUnivariateFeature, features_notok) + features_notok_fixed = [begin + U = vareltype(dataset, i_variable(f)) + eval(nameof(typeof(f))){U}(f) + end for f in features_notok] + if some_features_were_specified + @warn "Patching $(length(features_notok)) features using vareltype." + end + features = [features_ok..., features_notok_fixed...] + else + @warn "Could not infer feature value type for some of the specified features. " * + "Please specify the feature value type upon construction. Untyped " * + "features: $(displaysyntaxvector(features_notok_fixed))" + end + end features = UniqueVector(features) # Initialize the logiset structure @@ -56,5 +192,17 @@ function scalarlogiset( end # next!(p) end - X + + if !use_full_memoization && !use_onestep_memoization + X + else + SupportedLogiset(X; + use_full_memoization = use_full_memoization, + use_onestep_memoization = use_onestep_memoization, + conditions = conditions, + relations = relations, + onestep_precompute_globmemoset = onestep_precompute_globmemoset, + onestep_precompute_relmemoset = onestep_precompute_relmemoset, + ) + end end diff --git a/src/logisets/scalar/memosets.jl b/src/logisets/scalar/memosets.jl index 66069b4..a1bb80e 100644 --- a/src/logisets/scalar/memosets.jl +++ b/src/logisets/scalar/memosets.jl @@ -4,8 +4,7 @@ import Base: in, findfirst # Fixes # https://github.com/garrison/UniqueVectors.jl/blob/d63669d1f5c8f7ee4f4bf3f2920bc4afe33fe676/src/UniqueVectors.jl#L56 Base.in(item, uv::UniqueVector) = haskey(uv.lookup, item) -Base.findfirst(p::UniqueVectors.EqualTo, uv::UniqueVector) = - get(uv.lookup, p.x, nothing) +Base.findfirst(p::UniqueVectors.EqualTo, uv::UniqueVector) = get(uv.lookup, p.x, nothing) """ diff --git a/src/logisets/scalar/onestep-memoset.jl b/src/logisets/scalar/onestep-memoset.jl index 8731409..3942616 100644 --- a/src/logisets/scalar/onestep-memoset.jl +++ b/src/logisets/scalar/onestep-memoset.jl @@ -182,15 +182,15 @@ struct ScalarOneStepMemoset{ GM<:Union{AbstractScalarOneStepGlobalMemoset{W,U},Nothing}, } <: AbstractOneStepMemoset{W,U,FT where FT<:AbstractFeature,FR} - metaconditions :: UniqueVector{<:ScalarMetaCondition} - relations :: UniqueVector{<:AbstractRelation} - # Relational memoset relmemoset :: RM # Global memoset globmemoset :: GM + metaconditions :: UniqueVector{<:ScalarMetaCondition} + relations :: UniqueVector{<:AbstractRelation} + function ScalarOneStepMemoset{U}( relmemoset::RM, globmemoset::GM, @@ -216,26 +216,26 @@ struct ScalarOneStepMemoset{ @warn "Using global relation in a relational memoset. This is not optimal." end - @assert nmetaconditions(relmemoset) == length(metaconditions) "Can't instantiate " * + @assert nmetaconditions(relmemoset) == length(metaconditions) "Cannot instantiate " * "$(ty) with mismatching nmetaconditions for relmemoset and " * "provided metaconditions: $(nmetaconditions(relmemoset)) and $(length(metaconditions))" # Global relation breaks this: - # @assert nrelations(relmemoset) == length(relations) "Can't instantiate " * + # @assert nrelations(relmemoset) == length(relations) "Cannot instantiate " * # "$(ty) with mismatching nrelations for relmemoset and " * # "provided relations: $(nrelations(relmemoset)) and $(length(relations))" if !isnothing(globmemoset) - @assert nmetaconditions(globmemoset) == length(metaconditions) "Can't " * + @assert nmetaconditions(globmemoset) == length(metaconditions) "Cannot " * "instantiate $(ty) with mismatching nmetaconditions for " * "globmemoset and provided metaconditions: " * "$(nmetaconditions(globmemoset)) and $(length(metaconditions))" - @assert ninstances(globmemoset) == ninstances(relmemoset) "Can't " * + @assert ninstances(globmemoset) == ninstances(relmemoset) "Cannot " * "instantiate $(ty) with mismatching ninstances for " * "global and relational memosets: " * "$(ninstances(globmemoset)) and $(ninstances(relmemoset))" end - new{U,W,FR,UU,RM,GM}(metaconditions, relations, relmemoset, globmemoset) + new{U,W,FR,UU,RM,GM}(relmemoset, globmemoset, metaconditions, relations) end Base.@propagate_inbounds function ScalarOneStepMemoset( @@ -444,8 +444,8 @@ function check( end end -function instances(Xm::ScalarOneStepMemoset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) - ScalarOneStepMemoset( +function instances(Xm::ScalarOneStepMemoset{U}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {U} + ScalarOneStepMemoset{U}( instances(relmemoset(Xm), inds, return_view), (isnothing(globmemoset(Xm)) ? nothing : instances(globmemoset(Xm), inds, return_view)), metaconditions(Xm), @@ -453,16 +453,16 @@ function instances(Xm::ScalarOneStepMemoset, inds::AbstractVector{<:Integer}, re ) end -function concatdatasets(Xms::ScalarOneStepMemoset...) +function concatdatasets(Xms::ScalarOneStepMemoset{U}...) where {U} @assert allequal(metaconditions.(Xms)) "Cannot concatenate " * "ScalarOneStepMemoset's with different metaconditions: " * "$(@show metaconditions.(Xms))" @assert allequal(relations.(Xms)) "Cannot concatenate " * "ScalarOneStepMemoset's with different relations: " * "$(@show relations.(Xms))" - ScalarOneStepMemoset( - concatdatasets(relmemoset.(Xms)), - concatdatasets(globmemoset.(Xms)), + ScalarOneStepMemoset{U}( + concatdatasets(relmemoset.(Xms)...), + concatdatasets(globmemoset.(Xms)...), metaconditions(first(Xms)), relations(first(Xms)), ) @@ -489,29 +489,38 @@ function minify(Xm::OSSD) where {OSSD<:ScalarOneStepMemoset} Xm, backmap end -function displaystructure(Xm::ScalarOneStepMemoset; indent_str = "", include_ninstances = true) - padattribute(l,r,off=0) = string(l) * lpad(r,32+off+length(string(r))-(length(indent_str)+2+length(l))-1) +function displaystructure( + Xm::ScalarOneStepMemoset; + indent_str = "", + include_ninstances = true, + kwargs... + # include_worldtype = missing, + # include_featvaltype = missing, + # include_featuretype = missing, + # include_frametype = missing, +) + padattribute(l,r,off=0) = string(l) * lpad(string(r),32+off+length(string(r))-(length(indent_str)+2+length(string(l)))-1) pieces = [] - push!(pieces, "ScalarOneStepMemoset ($(humansize(Xm)))\n") + push!(pieces, "ScalarOneStepMemoset ($(humansize(Xm)))") if include_ninstances - push!(pieces, " $(padattribute("# instances:", ninstances(Xm), 1))\n") + push!(pieces, " $(padattribute("# instances:", ninstances(Xm), 1))") end - # push!(pieces, " $(padattribute("# metaconditions:", nmetaconditions(Xm), 1))\n") - # push!(pieces, " $(padattribute("# relations:", nrelations(Xm), 1))\n") + # push!(pieces, " $(padattribute("# metaconditions:", nmetaconditions(Xm), 1))") + # push!(pieces, " $(padattribute("# relations:", nrelations(Xm), 1))") - push!(pieces, " $(padattribute("metaconditions:", "$(nmetaconditions(Xm)) -> $(displaysyntaxvector(metaconditions(Xm)))", 1))\n") - push!(pieces, " $(padattribute("relations:", "$(nrelations(Xm)) -> $(displaysyntaxvector(relations(Xm)))", -9))\n") + push!(pieces, " $(padattribute("metaconditions:", "$(nmetaconditions(Xm)) -> $(displaysyntaxvector(metaconditions(Xm)))", 1))") + push!(pieces, " $(padattribute("relations:", "$(nrelations(Xm)) -> $(displaysyntaxvector(relations(Xm)))"))") - push!(pieces, "[R] " * displaystructure(relmemoset(Xm); indent_str = "$(indent_str)│ ", include_ninstances = false, include_nmetaconditions = false, include_nrelations = false)) + push!(pieces, "[R] " * displaystructure(relmemoset(Xm); indent_str = "$(indent_str)│ ", include_ninstances = false, include_nmetaconditions = false, include_nrelations = false, kwargs...)) if !isnothing(globmemoset(Xm)) - push!(pieces, "[G] " * displaystructure(globmemoset(Xm); indent_str = "$(indent_str) ", include_ninstances = false, include_nmetaconditions = false)) + push!(pieces, "[G] " * displaystructure(globmemoset(Xm); indent_str = "$(indent_str) ", include_ninstances = false, include_nmetaconditions = false, kwargs...)) else - push!(pieces, "[G] −\n") + push!(pieces, "[G] −") end - return join(pieces, "$(indent_str)├", "$(indent_str)└") + return join(pieces, "\n$(indent_str)├", "\n$(indent_str)└") end ############################################################################################ @@ -607,14 +616,32 @@ function concatdatasets(Xms::ScalarOneStepRelationalMemoset...) end -function displaystructure(Xm::ScalarOneStepRelationalMemoset; indent_str = "", include_ninstances = true, include_nmetaconditions = true, include_nrelations = true) +function displaystructure( + Xm::ScalarOneStepRelationalMemoset; + indent_str = "", + include_ninstances = true, + include_nmetaconditions = true, + include_nrelations = true, + include_worldtype = missing, + include_featvaltype = missing, + include_featuretype = missing, + include_frametype = missing, +) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) pieces = [] push!(pieces, "ScalarOneStepRelationalMemoset ($(memoizationinfo(Xm)), $(humansize(Xm)))") - push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") - push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") - push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") - push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + if ismissing(include_worldtype) || include_worldtype != worldtype(Xm) + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + end + if ismissing(include_featvaltype) || include_featvaltype != featvaltype(Xm) + push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") + end + # if ismissing(include_featuretype) || include_featuretype != featuretype(Xm) + # push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") + # end + if ismissing(include_frametype) || include_frametype != frametype(Xm) + push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + end if include_ninstances push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") end @@ -627,7 +654,7 @@ function displaystructure(Xm::ScalarOneStepRelationalMemoset; indent_str = "", i push!(pieces, "$(padattribute("size × eltype:", "$(size(Xm.d)) × $(eltype(Xm.d))"))") # push!(pieces, "$(padattribute("# memoized values:", nmemoizedvalues(Xm)))") - return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" + return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") end # @inline function Base.setindex!(Xm::ScalarOneStepRelationalMemoset{W,U}, threshold::U, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {W,U} @@ -700,19 +727,36 @@ function instances(Xm::ScalarOneStepGlobalMemoset{W,U}, inds::AbstractVector{<:I ScalarOneStepGlobalMemoset{W,U}(if return_view == Val(true) @view Xm.d[inds,:] else Xm.d[inds,:] end) end -function concatdatasets(Xms::ScalarOneStepGlobalMemoset...) - ScalarOneStepGlobalMemoset(cat([Xm.d for Xm in Xms]...; dims=1)) +function concatdatasets(Xms::ScalarOneStepGlobalMemoset{W,U}...) where {W,U} + ScalarOneStepGlobalMemoset{W,U}(cat([Xm.d for Xm in Xms]...; dims=1)) end -function displaystructure(Xm::ScalarOneStepGlobalMemoset; indent_str = "", include_ninstances = true, include_nmetaconditions = true) +function displaystructure( + Xm::ScalarOneStepGlobalMemoset; + indent_str = "", + include_ninstances = true, + include_nmetaconditions = true, + include_worldtype = missing, + include_featvaltype = missing, + include_featuretype = missing, + include_frametype = missing, +) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) pieces = [] push!(pieces, "ScalarOneStepGlobalMemoset ($(memoizationinfo(Xm)), $(humansize(Xm)))") - push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") - push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") - push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") - push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + if ismissing(include_worldtype) || include_worldtype != worldtype(Xm) + push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") + end + if ismissing(include_featvaltype) || include_featvaltype != featvaltype(Xm) + push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") + end + # if ismissing(include_featuretype) || include_featuretype != featuretype(Xm) + # push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") + # end + # if ismissing(include_frametype) || include_frametype != frametype(Xm) + # push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") + # end if include_ninstances push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") end @@ -721,6 +765,6 @@ function displaystructure(Xm::ScalarOneStepGlobalMemoset; indent_str = "", inclu end push!(pieces, "$(padattribute("size × eltype:", "$(size(innerstruct(Xm))) × $(eltype(innerstruct(Xm)))"))") - return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" + return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") end diff --git a/src/logisets/scalar/var-features.jl b/src/logisets/scalar/var-features.jl index 9b3d366..dab7a55 100644 --- a/src/logisets/scalar/var-features.jl +++ b/src/logisets/scalar/var-features.jl @@ -1,6 +1,6 @@ import SoleModels: AbstractFeature -using SoleData: AbstractDimensionalChannel, channelvariable +using SoleData: channelvariable import Base: isequal, hash, show import SoleLogics: syntaxstring @@ -35,16 +35,18 @@ featvaltype(::Type{<:VarFeature{U}}) where {U} = U featvaltype(::VarFeature{U}) where {U} = U """ - computefeature(f::VarFeature{U}, channel; kwargs...)::U where {U} + computefeature(f::VarFeature{U}, featchannel; kwargs...)::U where {U} -Compute a feature on a channel (i.e., world reading) of an instance. +Compute a feature on a featchannel (i.e., world reading) of an instance. See also [`VarFeature`](@ref). """ -function computefeature(f::VarFeature{U}, channel; kwargs...) where {U} - error("Please, provide method computefeature(::$(typeof(f)), channel::$(typeof(channel)); kwargs...)::U.") +function computefeature(f::VarFeature{U}, featchannel; kwargs...) where {U} + error("Please, provide method computefeature(::$(typeof(f)), featchannel::$(typeof(featchannel)); kwargs...)::U.") end +preserveseltype(::VarFeature) = false + @inline (f::AbstractFeature)(args...) = computefeature(f, args...) ############################################################################################ @@ -68,9 +70,6 @@ See also [`Interval`](@ref), struct MultivariateFeature{U} <: VarFeature{U} f::Function end -function computefeature(f::MultivariateFeature{U}, channel::AbstractDimensionalChannel{T})::U where {U,T} - (f.f(channel)) -end syntaxstring(f::MultivariateFeature, args...; kwargs...) = "$(f.f)" ############################################################################################ @@ -90,8 +89,23 @@ See also [`Interval`](@ref), """ abstract type AbstractUnivariateFeature{U} <: VarFeature{U} end +""" + computeunivariatefeature(f::AbstractUnivariateFeature{U}, varchannel; kwargs...)::U where {U} + +Compute a feature on a variable channel (i.e., world reading) of an instance. + +See also [`AbstractUnivariateFeature`](@ref). +""" +function computeunivariatefeature(f::AbstractUnivariateFeature{U}, varchannel::Any; kwargs...) where {U} + error("Please, provide method computeunivariatefeature(::$(typeof(f)), varchannel::$(typeof(varchannel)); kwargs...)::U.") +end + i_variable(f::AbstractUnivariateFeature) = f.i_variable +function computefeature(f::AbstractUnivariateFeature{U}, featchannel::Any)::U where {U} + computeunivariatefeature(f, channelvariable(featchannel, i_variable(f)))::U +end + """ function variable_name( f::AbstractUnivariateFeature; @@ -165,9 +179,6 @@ struct UnivariateFeature{U} <: AbstractUnivariateFeature{U} i_variable::Integer f::Function end -function computefeature(f::UnivariateFeature{U}, channel::AbstractDimensionalChannel{T}) where {U,T} - (f.f(SoleBase.vectorize(channelvariable(channel, f.i_variable));))::U -end featurename(f::UnivariateFeature) = string(f.f) """ @@ -187,13 +198,46 @@ struct UnivariateNamedFeature{U} <: AbstractUnivariateFeature{U} i_variable::Integer name::String end -function computefeature(f::UnivariateNamedFeature, channel::AbstractDimensionalChannel{T}) where {T} - @error "Can't intepret UnivariateNamedFeature on any structure at all." -end featurename(f::UnivariateNamedFeature) = f.name ############################################################################################ +""" + struct UnivariateValue{U} <: AbstractUnivariateFeature{U} + i_variable::Integer + end + +Notable univariate feature computing the minimum value for a given variable. + +See also [`Interval`](@ref), +[`Interval2D`](@ref), +[`AbstractUnivariateFeature`](@ref), +[`UnivariateMax`](@ref), +[`VarFeature`](@ref), [`AbstractFeature`](@ref). +""" +struct UnivariateValue{U} <: AbstractUnivariateFeature{U} + i_variable::Integer + function UnivariateValue{U}(f::UnivariateValue) where {U<:Real} + return new{U}(i_variable(f)) + end + function UnivariateValue{U}(i_variable::Integer) where {U<:Real} + return new{U}(i_variable) + end + function UnivariateValue(i_variable::Integer) + return UnivariateValue{DEFAULT_VARFEATVALTYPE}(i_variable) + end +end +featurename(f::UnivariateValue) = "" + +function syntaxstring( + f::UnivariateValue; + kwargs... +) + variable_name(f; kwargs...) +end + +############################################################################################ + """ struct UnivariateMin{U} <: AbstractUnivariateFeature{U} i_variable::Integer @@ -209,20 +253,20 @@ See also [`Interval`](@ref), """ struct UnivariateMin{U} <: AbstractUnivariateFeature{U} i_variable::Integer + function UnivariateMin{U}(f::UnivariateMin) where {U<:Real} + return new{U}(i_variable(f)) + end function UnivariateMin{U}(i_variable::Integer) where {U<:Real} return new{U}(i_variable) end function UnivariateMin(i_variable::Integer) - @warn "Please specify the feature value type for UnivariateMin. " * - "For example: UnivariateMin{Float64}($(i_variable))." return UnivariateMin{DEFAULT_VARFEATVALTYPE}(i_variable) end end -function computefeature(f::UnivariateMin{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} - (minimum(channelvariable(channel, f.i_variable)))::U -end featurename(f::UnivariateMin) = "min" +preserveseltype(::UnivariateMin) = true + """ struct UnivariateMax{U} <: AbstractUnivariateFeature{U} i_variable::Integer @@ -238,20 +282,19 @@ See also [`Interval`](@ref), """ struct UnivariateMax{U} <: AbstractUnivariateFeature{U} i_variable::Integer + function UnivariateMax{U}(f::UnivariateMax) where {U<:Real} + return new{U}(i_variable(f)) + end function UnivariateMax{U}(i_variable::Integer) where {U<:Real} return new{U}(i_variable) end function UnivariateMax(i_variable::Integer) - @warn "Please specify the feature value type for UnivariateMax. " * - "For example: UnivariateMax{Float64}($(i_variable))." return UnivariateMax{DEFAULT_VARFEATVALTYPE}(i_variable) end end -function computefeature(f::UnivariateMax{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} - (maximum(channelvariable(channel, f.i_variable)))::U -end featurename(f::UnivariateMax) = "max" +preserveseltype(::UnivariateMax) = true ############################################################################################ @@ -272,18 +315,19 @@ See also [`Interval`](@ref), struct UnivariateSoftMin{U,T<:AbstractFloat} <: AbstractUnivariateFeature{U} i_variable::Integer alpha::T + function UnivariateSoftMin{U}(f::UnivariateSoftMin) where {U<:Real} + return new{U,typeof(alpha(f))}(i_variable(f), alpha(f)) + end function UnivariateSoftMin{U}(i_variable::Integer, alpha::T) where {U<:Real,T} - @assert !(alpha > 1.0 || alpha < 0.0) "Can't instantiate UnivariateSoftMin with alpha = $(alpha)" - @assert !isone(alpha) "Can't instantiate UnivariateSoftMin with alpha = $(alpha). Use UnivariateMin instead!" + @assert !(alpha > 1.0 || alpha < 0.0) "Cannot instantiate UnivariateSoftMin with alpha = $(alpha)" + @assert !isone(alpha) "Cannot instantiate UnivariateSoftMin with alpha = $(alpha). Use UnivariateMin instead!" new{U,T}(i_variable, alpha) end end alpha(f::UnivariateSoftMin) = f.alpha -featurename(f::UnivariateSoftMin) = "min" * utils.subscriptnumber(rstrip(rstrip(string(f.alpha*100), '0'), '.')) -function computefeature(f::UnivariateSoftMin{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} - utils.softminimum(channelvariable(channel, f.i_variable), f.alpha)::U -end +featurename(f::UnivariateSoftMin) = "min" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')) +preserveseltype(::UnivariateSoftMin) = true """ struct UnivariateSoftMax{U,T<:AbstractFloat} <: AbstractUnivariateFeature{U} @@ -302,25 +346,19 @@ See also [`Interval`](@ref), struct UnivariateSoftMax{U,T<:AbstractFloat} <: AbstractUnivariateFeature{U} i_variable::Integer alpha::T + function UnivariateSoftMax{U}(f::UnivariateSoftMax) where {U<:Real} + return new{U,typeof(alpha(f))}(i_variable(f), alpha(f)) + end function UnivariateSoftMax{U}(i_variable::Integer, alpha::T) where {U<:Real,T} - @assert !(alpha > 1.0 || alpha < 0.0) "Can't instantiate UnivariateSoftMax with alpha = $(alpha)" - @assert !isone(alpha) "Can't instantiate UnivariateSoftMax with alpha = $(alpha). Use UnivariateMax instead!" + @assert !(alpha > 1.0 || alpha < 0.0) "Cannot instantiate UnivariateSoftMax with alpha = $(alpha)" + @assert !isone(alpha) "Cannot instantiate UnivariateSoftMax with alpha = $(alpha). Use UnivariateMax instead!" new{U,T}(i_variable, alpha) end end alpha(f::UnivariateSoftMax) = f.alpha -featurename(f::UnivariateSoftMax) = "max" * utils.subscriptnumber(rstrip(rstrip(string(f.alpha*100), '0'), '.')) -function computefeature(f::UnivariateSoftMax{U}, channel::AbstractDimensionalChannel{T}) where {U<:Real,T} - utils.softmaximum(channelvariable(channel, f.i_variable), f.alpha)::U -end +featurename(f::UnivariateSoftMax) = "max" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')) -# simplified propositional cases: -function computefeature(f::UnivariateSoftMin{U}, channel::AbstractDimensionalChannel{T,1}) where {U<:Real,T} - channelvariable(channel, f.i_variable)::U -end -function computefeature(f::UnivariateSoftMax{U}, channel::AbstractDimensionalChannel{T,1}) where {U<:Real,T} - channelvariable(channel, f.i_variable)::U -end +preserveseltype(::UnivariateSoftMax) = true ############################################################################################ diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index e715046..5f344da 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -261,20 +261,55 @@ function concatdatasets(Xs::SupportedLogiset...) ) end -function displaystructure(X::SupportedLogiset; indent_str = "", include_ninstances = true) +function displaystructure( + X::SupportedLogiset; + indent_str = "", + include_ninstances = true, + include_worldtype = missing, + include_featvaltype = missing, + include_featuretype = missing, + include_frametype = missing, +) padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))-1) pieces = [] - push!(pieces, "SupportedLogiset ($(humansize(X)))\n") + push!(pieces, "SupportedLogiset with $(nsupports(X)) supports ($(humansize(X)))") + if ismissing(include_worldtype) || include_worldtype != worldtype(X) + push!(pieces, " " * padattribute("worldtype:", worldtype(X))) + end + if ismissing(include_featvaltype) || include_featvaltype != featvaltype(X) + push!(pieces, " " * padattribute("featvaltype:", featvaltype(X))) + end + if ismissing(include_featuretype) || include_featuretype != featuretype(X) + push!(pieces, " " * padattribute("featuretype:", featuretype(X))) + end + if ismissing(include_frametype) || include_frametype != frametype(X) + push!(pieces, " " * padattribute("frametype:", frametype(X))) + end if include_ninstances - push!(pieces, " " * padattribute("# instances:", "$(ninstances(X))\n")) + push!(pieces, " " * padattribute("# instances:", "$(ninstances(X))")) end - push!(pieces, " " * padattribute("# supports:", "$(nsupports(X))\n")) - push!(pieces, " " * padattribute("usesfullmemo:", "$(usesfullmemo(X))\n")) - push!(pieces, "[BASE] " * displaystructure(base(X); indent_str = "$(indent_str)│ ", include_ninstances = false)) + # push!(pieces, " " * padattribute("# supports:", "$(nsupports(X))")) + push!(pieces, " " * padattribute("usesfullmemo:", "$(usesfullmemo(X))")) + push!(pieces, "[BASE] " * displaystructure(base(X); + indent_str = "$(indent_str)│ ", + include_ninstances = false, + include_worldtype = worldtype(X), + include_featvaltype = featvaltype(X), + include_featuretype = featuretype(X), + include_frametype = frametype(X), + )) + for (i_supp,supp) in enumerate(supports(X)) - push!(pieces, "[SUPPORT $(i_supp)] $(displaystructure(supp; indent_str = (i_supp == nsupports(X) ? "$(indent_str) " : "$(indent_str)│ "), include_ninstances = false))") + push!(pieces, "[SUPPORT $(i_supp)] " * displaystructure(supp; + indent_str = (i_supp == nsupports(X) ? "$(indent_str) " : "$(indent_str)│ "), + include_ninstances = false, + include_worldtype = worldtype(X), + include_featvaltype = featvaltype(X), + include_featuretype = featuretype(X), + include_frametype = frametype(X), + ) * ")") end - return join(pieces, "$(indent_str)├", "$(indent_str)└") * "\n" + return join(pieces, "\n$(indent_str)├", "\n$(indent_str)└") end # ############################################################################################ @@ -290,7 +325,5 @@ end # fwd(X::SupportedLogiset) = fwd(base(X)) # worldtype(X::SupportedLogiset{V,W}) where {V,W} = W -# initialworld(X::SupportedScalarLogiset, args...) = initialworld(base(X), args...) - # TODO remove: support(X::SupportedLogiset) = first(supports(X)) diff --git a/src/machine-learning.jl b/src/machine-learning.jl index a32233e..f1d2e3e 100644 --- a/src/machine-learning.jl +++ b/src/machine-learning.jl @@ -82,7 +82,7 @@ function bestguess( if isnothing(weights) countmap(labels) else - @assert length(labels) === length(weights) "Can't compute " * + @assert length(labels) === length(weights) "Cannot compute " * "best guess with uneven number of votes " * "$(length(labels)) and weights $(length(weights))." countmap(labels, weights) diff --git a/src/models/base.jl b/src/models/base.jl index 6da4f74..f6899c7 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -570,10 +570,10 @@ function check_model_constraints( ) I_O = outcometype(I_M) # FM_O = outcometype(FM) - @assert I_O <: FM_O "Can't instantiate $(M) with inner model outcometype " * + @assert I_O <: FM_O "Cannot instantiate $(M) with inner model outcometype " * "$(I_O)! $(I_O) <: $(FM_O) should hold." - # @assert I_M <: FM || typename(I_M) <: typename(FM) "Can't instantiate $(M) with inner model $(I_M))! $(I_M) <: $(FM) || $(typename(I_M)) <: $(typename(FM)) should hold." - @assert I_M <: FM "Can't instantiate $(M) with inner model $(I_M))! " * + # @assert I_M <: FM || typename(I_M) <: typename(FM) "Cannot instantiate $(M) with inner model $(I_M))! $(I_M) <: $(FM) || $(typename(I_M)) <: $(typename(FM)) should hold." + @assert I_M <: FM "Cannot instantiate $(M) with inner model $(I_M))! " * "$(I_M) <: $(FM) should hold." if ! (I_M<:FinalModel{<:FM_O}) # @assert I_M<:ConstrainedModel{FM_O,<:FM} "ConstrainedModels require I_M<:ConstrainedModel{O,<:FM}, but $(I_M) does not subtype $(ConstrainedModel{FM_O,<:FM})." diff --git a/src/other/active-logiset.jl b/src/other/active-logiset.jl index 47c8dad..13a02b0 100644 --- a/src/other/active-logiset.jl +++ b/src/other/active-logiset.jl @@ -168,8 +168,8 @@ struct ActiveLogiset{ ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W},FWD<:AbstractFeatureLookupSet{V,FR},FT<:AbstractFeature} features = collect(features) ty = "ActiveLogiset{$(V),$(W),$(FR),$(FT)}" - @assert allow_no_instances || ninstances(featstruct) > 0 "Can't instantiate $(ty) with no instance. (featstruct's type $(typeof(featstruct)))" - @assert nfeatures(featstruct) == length(features) "Can't instantiate $(ty) with different numbers of instances $(ninstances(featstruct)) and of features $(length(features))." + @assert allow_no_instances || ninstances(featstruct) > 0 "Cannot instantiate $(ty) with no instance. (featstruct's type $(typeof(featstruct)))" + @assert nfeatures(featstruct) == length(features) "Cannot instantiate $(ty) with different numbers of instances $(ninstances(featstruct)) and of features $(length(features))." check_initialworld(ActiveLogiset, initialworld, W) new{ V, diff --git a/src/other/dimensional-logiset.jl b/src/other/dimensional-logiset.jl index 1410aa3..d3c0070 100644 --- a/src/other/dimensional-logiset.jl +++ b/src/other/dimensional-logiset.jl @@ -50,15 +50,15 @@ struct DimensionalLogiset{ FT = Union{typeof.(features)...} features = Vector{FT}(features) @assert allow_no_instances || ninstances(domain) > 0 "" * - "Can't instantiate $(ty) with no instance. (domain's type $(typeof(domain)))" + "Cannot instantiate $(ty) with no instance. (domain's type $(typeof(domain)))" @assert length(features) == length(grouped_featsaggrsnops) "" * - "Can't instantiate $(ty) with mismatching length(features) and " * + "Cannot instantiate $(ty) with mismatching length(features) and " * "length(grouped_featsaggrsnops): " * "$(length(features)) != $(length(grouped_featsaggrsnops))" @assert length(grouped_featsaggrsnops) > 0 && sum(length.(grouped_featsaggrsnops)) > 0 && sum(vcat([[length(test_ops) for test_ops in aggrs] for aggrs in grouped_featsaggrsnops]...)) > 0 "" * - "Can't instantiate $(ty) with no test operator: $(grouped_featsaggrsnops)" + "Cannot instantiate $(ty) with no test operator: $(grouped_featsaggrsnops)" grouped_featsnaggrs = features_grouped_featsaggrsnops2grouped_featsnaggrs(features, grouped_featsaggrsnops) check_initialworld(DimensionalLogiset, initialworld, W) new{ @@ -200,10 +200,10 @@ struct DimensionalLogiset{ DimensionalLogiset{V}(domain, ontology, features, args...; kwargs...) end - preserves_type(::Any) = false - preserves_type(::CanonicalFeature) = true - preserves_type(::typeof(minimum)) = true # TODO fix - preserves_type(::typeof(maximum)) = true # TODO fix + preserveseltype(::Any) = false + preserveseltype(::CanonicalFeature) = true + preserveseltype(::typeof(minimum)) = true # TODO fix + preserveseltype(::typeof(maximum)) = true # TODO fix function DimensionalLogiset( domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, @@ -212,7 +212,7 @@ struct DimensionalLogiset{ kwargs..., ) where {N,W<:AbstractWorld} domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalLogiset{dimensionality(domain),W}(domain) : domain) - @assert all((f)->(preserves_type(f)), mixed_features) "Please, specify the feature output type V upon construction, as in: DimensionalLogiset{V}(...)." # TODO highlight and improve + @assert all((f)->(preserveseltype(f)), mixed_features) "Please, specify the feature output type V upon construction, as in: DimensionalLogiset{V}(...)." # TODO highlight and improve V = eltype(domain) DimensionalLogiset{V}(domain, ontology, mixed_features; kwargs...) end @@ -241,8 +241,8 @@ relations(X::DimensionalLogiset) = relations(ontology(X)) nrelations(X::DimensionalLogiset) = length(relations(X)) nfeatures(X::DimensionalLogiset) = length(features(X)) -channel_size(X::DimensionalLogiset, args...) = channel_size(domain(X), args...) -max_channel_size(X::DimensionalLogiset) = max_channel_size(domain(X)) +channelsize(X::DimensionalLogiset, args...) = channelsize(domain(X), args...) +maxchannelsize(X::DimensionalLogiset) = maxchannelsize(domain(X)) get_instance(X::DimensionalLogiset, args...) = get_instance(domain(X), args...) @@ -262,7 +262,7 @@ function displaystructure(X::DimensionalLogiset; indent_str = "") out *= indent_str * "├ domain shape:\t\t$(Base.size(domain(X)))\n" out *= indent_str * "├ ninstances:\t\t$(ninstances(X))\n" out *= indent_str * "├ nvariables:\t\t$(nvariables(X))\n" - out *= indent_str * "├ max_channel_size:\t$(max_channel_size(X))\n" + out *= indent_str * "├ maxchannelsize:\t$(maxchannelsize(X))\n" out *= indent_str * "└ initialworld(s):\t$(initialworld(X))" out end diff --git a/src/other/ontology.jl b/src/other/ontology.jl index 797ce0f..bcbb676 100644 --- a/src/other/ontology.jl +++ b/src/other/ontology.jl @@ -17,7 +17,7 @@ struct Ontology{W<:AbstractWorld} function Ontology{W}(_relations::AbstractVector) where {W<:AbstractWorld} _relations = collect(unique(_relations)) # for relation in _relations - # @assert goeswith(world2frametype[W], relation) "Can't instantiate Ontology{$(W)} with relation $(relation)!" + # @assert goeswith(world2frametype[W], relation) "Cannot instantiate Ontology{$(W)} with relation $(relation)!" # end if W == OneWorld && length(_relations) > 0 _relations = similar(_relations, 0) diff --git a/src/other/passive-dimensional-logiset.jl b/src/other/passive-dimensional-logiset.jl index c7c574e..4849740 100644 --- a/src/other/passive-dimensional-logiset.jl +++ b/src/other/passive-dimensional-logiset.jl @@ -1,8 +1,6 @@ using SoleData: slicedataset -import SoleData: get_instance, ninstances, nvariables, channel_size, max_channel_size, dimensionality, eltype +import SoleData: get_instance, ninstances, nvariables, channelsize, maxchannelsize, dimensionality, eltype using SoleData: AbstractDimensionalDataset, - AbstractDimensionalInstance, - AbstractDimensionalChannel, UniformDimensionalDataset, DimensionalInstance, DimensionalChannel @@ -92,8 +90,8 @@ end Base.size(X::PassiveDimensionalLogiset) = Base.size(X.d) nvariables(X::PassiveDimensionalLogiset) = nvariables(X.d) -channel_size(X::PassiveDimensionalLogiset) = channel_size(X.d) -max_channel_size(X::PassiveDimensionalLogiset) = max_channel_size(X.d) +channelsize(X::PassiveDimensionalLogiset) = channelsize(X.d) +maxchannelsize(X::PassiveDimensionalLogiset) = maxchannelsize(X.d) dimensionality(X::PassiveDimensionalLogiset) = dimensionality(X.d) eltype(X::PassiveDimensionalLogiset) = eltype(X.d) @@ -126,10 +124,10 @@ function displaystructure(Xm::PassiveDimensionalLogiset; indent_str = "", includ push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") end push!(pieces, "$(padattribute("# variables:", nvariables(Xm)))") - push!(pieces, "$(padattribute("channel_size:", channel_size(Xm)))") + push!(pieces, "$(padattribute("channelsize:", channelsize(Xm)))") push!(pieces, "$(padattribute("size × eltype:", "$(size(Xm.d)) × $(eltype(Xm.d))"))") - return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") * "\n" + return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") end hasnans(X::PassiveDimensionalLogiset) = hasnans(X.d) @@ -140,4 +138,4 @@ worldtype(X::PassiveDimensionalLogiset{N,W}) where {N,W} = W ############################################################################################ _frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance::Integer) = _frame(X) -_frame(X::Union{UniformDimensionalDataset,AbstractArray}) = FullDimensionalFrame(channel_size(X)) +_frame(X::Union{UniformDimensionalDataset,AbstractArray}) = FullDimensionalFrame(channelsize(X)) diff --git a/test/dimensional-datasets.jl b/src/other/test-dimensional-datasets.jl similarity index 100% rename from test/dimensional-datasets.jl rename to src/other/test-dimensional-datasets.jl diff --git a/src/utils.jl b/src/utils.jl index 5302931..8de146e 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -70,3 +70,44 @@ function displaysyntaxvector(a, maxnum = 8) end end + +# https://discourse.julialang.org/t/groupby-function/9896 + +""" + group items of list l according to the corresponding values in list v + + julia> _groupby([31,28,31,30,31,30,31,31,30,31,30,31], + [:Jan,:Feb,:Mar,:Apr,:May,:Jun,:Jul,:Aug,:Sep,:Oct,:Nov,:Dec]) + Dict{Int64,Array{Symbol,1}} with 3 entries: + 31 => Symbol[:Jan, :Mar, :May, :Jul, :Aug, :Oct, :Dec] + 28 => Symbol[:Feb] + 30 => Symbol[:Apr, :Jun, :Sep, :Nov] + +""" +function _groupby(v::AbstractVector, l::AbstractVector) + @assert length(v) == length(l) "$(@show v, l)" + res = Dict{eltype(v),Vector{eltype(l)}}() + for (k, val) in zip(v, l) + push!(get!(res, k, similar(l, 0)), val) + end + res +end + +""" + group items of list l according to the values taken by function f on them + + julia> _groupby(iseven,1:10) + Dict{Bool,Array{Int64,1}} with 2 entries: + false => [1, 3, 5, 7, 9] + true => [2, 4, 6, 8, 10] + +Note:in this version l is required to be non-empty since I do not know how to +access the return type of a function +""" +function _groupby(f,l::AbstractVector) + res = Dict(f(l[1]) => [l[1]]) # l should be nonempty + for val in l[2:end] + push!(get!(res, f(val), similar(l, 0)), val) + end + res +end diff --git a/test/dimensional-logisets.jl b/test/logisets/cube2logiset.jl similarity index 56% rename from test/dimensional-logisets.jl rename to test/logisets/cube2logiset.jl index 2eae6d6..7f13972 100644 --- a/test/dimensional-logisets.jl +++ b/test/logisets/cube2logiset.jl @@ -5,60 +5,68 @@ using SoleLogics using SoleModels using SoleModels.DimensionalDatasets -for (X,relations) in [ - # (Array(reshape(1.0:20.0, 2,10)),[identityrel,globalrel]), - # (Array(reshape(1.0:20.0, 2,10)),[identityrel]), - # (Array(reshape(1.0:60.0, 3,2,10)),[IARelations..., identityrel]), - # (Array(reshape(1.0:180.0, 3,3,2,10)),[IA2DRelations..., identityrel]), - (Array(reshape(1.0:60.0, 3,2,10)),[IARelations..., globalrel]), - (Array(reshape(1.0:180.0, 3,3,2,10)),[IA2DRelations..., globalrel]), +n_instances = 2 +_nvars = 2 + +for (dataset, relations) in [ + # (Array(reshape(1.0:4.0, _nvars,n_instances)), []), + (Array(reshape(1.0:4.0, _nvars,n_instances)), [globalrel]), + (Array(reshape(1.0:12.0, 3,_nvars,n_instances)), [IARelations..., globalrel]), + (Array(reshape(1.0:36.0, 3,3,_nvars,n_instances)), [IA2DRelations..., globalrel]), ] -nvars = nvariables(X) -features = collect(Iterators.flatten([[UnivariateMax{Float64}(i_var), UnivariateMin{Float64}(i_var)] for i_var in 1:nvars])) -logiset = scalarlogiset(X, features) +nvars = nvariables(dataset) + +generic_features = collect(Iterators.flatten([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:nvars])) +logiset = @test_logs (:warn,) scalarlogiset(dataset, generic_features; use_full_memoization = false, use_onestep_memoization = false) + +logiset = @test_nowarn scalarlogiset(dataset; use_full_memoization = false, use_onestep_memoization = false) -metaconditions = [ScalarMetaCondition(feature, >) for feature in features] +metaconditions = [ScalarMetaCondition(feature, >) for feature in generic_features] @test_nowarn SupportedLogiset(logiset, ()) @test_throws AssertionError SupportedLogiset(logiset, [Dict()]) @test_throws AssertionError SupportedLogiset(logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}()]) -@test_nowarn SupportedLogiset(logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10]) -@test_throws AssertionError SupportedLogiset(logiset, ([Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10], [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10])) +@test_nowarn SupportedLogiset(logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:n_instances]) +@test_throws AssertionError SupportedLogiset(logiset, ([Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:n_instances], [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:n_instances])) @test_throws AssertionError SupportedLogiset(logiset; use_full_memoization = false) @test_throws AssertionError SupportedLogiset(logiset; use_onestep_memoization = true) + +@test_logs (:warn,) SupportedLogiset(logiset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = [relations..., identityrel]) + supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = relations) -@test_throws AssertionError SupportedLogiset(logiset, (supported_logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10])) +@test_throws AssertionError SupportedLogiset(logiset, (supported_logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:n_instances])) supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = false, use_onestep_memoization = true, conditions = metaconditions, relations = relations) -@test_nowarn SupportedLogiset(logiset, (supported_logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10])) -@test_nowarn SupportedLogiset(logiset, ([Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:10], supported_logiset)) +@test_nowarn SupportedLogiset(logiset, (supported_logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:n_instances])) +@test_nowarn SupportedLogiset(logiset, ([Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:n_instances], supported_logiset)) supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = true, conditions = metaconditions, relations = relations) -metaconditions = vcat([[ScalarMetaCondition(feature, >), ScalarMetaCondition(feature, <)] for feature in features]...) +metaconditions = vcat([[ScalarMetaCondition(feature, >), ScalarMetaCondition(feature, <)] for feature in generic_features]...) complete_supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = true, conditions = metaconditions, relations = relations) rng = Random.MersenneTwister(1) -alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]); -syntaxstring.(alph) +alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, generic_features), rand(rng, [>, <]), rand(rng)) for i in 1:n_instances]); +# syntaxstring.(alph) _formulas = [randformulatree(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:16:end]...]) for i in 1:20]; -syntaxstring.(_formulas) .|> println; +# syntaxstring.(_formulas) .|> println; i_instance = 1 -@test_nowarn checkcondition(atom(alph.propositions[1]), logiset, i_instance, first(allworlds(X, i_instance))) +@test_nowarn checkcondition(atom(alph.propositions[1]), logiset, i_instance, first(allworlds(logiset, i_instance))) c1 = @test_nowarn [ [check(φ, logiset, i_instance, w) for φ in _formulas] - for w in allworlds(X, i_instance)] + for w in allworlds(logiset, i_instance)] c2 = @test_nowarn [ [check(φ, supported_logiset, i_instance, w) for φ in _formulas] - for w in allworlds(X, i_instance)] + for w in allworlds(logiset, i_instance)] c3 = @test_nowarn [ [check(φ, complete_supported_logiset, i_instance, w) for φ in _formulas] - for w in allworlds(X, i_instance)] + for w in allworlds(logiset, i_instance)] @test c1 == c3 @test_nowarn concatdatasets(logiset, logiset, logiset) -@test_broken concatdatasets(complete_supported_logiset, complete_supported_logiset) +@test_nowarn concatdatasets(complete_supported_logiset, complete_supported_logiset) + end diff --git a/test/logisets/dataframe2logiset.jl b/test/logisets/dataframe2logiset.jl new file mode 100644 index 0000000..de7eda4 --- /dev/null +++ b/test/logisets/dataframe2logiset.jl @@ -0,0 +1,65 @@ +using Test +using StatsBase +using Random +using SoleLogics +using SoleModels +using SoleModels.DimensionalDatasets + +n_instances = 2 +_nvars = 2 + +dataset, relations = (DataFrame(; NamedTuple([Symbol(i_var) => [rand(3,3) for i_instance in 1:n_instances] for i_var in 1:_nvars])...), [IA2DRelations..., globalrel]) + +nvars = nvariables(dataset) + +generic_features = collect(Iterators.flatten([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:nvars])) + +float_features = collect(Iterators.flatten([[UnivariateMax{Float64}(i_var), UnivariateMin{Float64}(i_var)] for i_var in 1:nvars])) +logiset = @test_nowarn scalarlogiset(dataset, float_features; use_full_memoization = false, use_onestep_memoization = false) + +int_features = collect(Iterators.flatten([[UnivariateMax{Int64}(i_var), UnivariateMin{Int64}(i_var)] for i_var in 1:nvars])) +logiset = @test_throws CompositeException scalarlogiset(dataset, int_features; use_full_memoization = false, use_onestep_memoization = false) + +@test isequal(generic_features, int_features) +@test isequal(generic_features, float_features) + +@test hash.(generic_features) == hash.(int_features) + +logiset = @test_nowarn scalarlogiset(dataset; use_full_memoization = false, use_onestep_memoization = false) +logiset = @test_nowarn scalarlogiset(dataset; use_full_memoization = true, use_onestep_memoization = false) + +int_metaconditions = [ScalarMetaCondition(feature, >) for feature in int_features] + +generic_metaconditions = [ScalarMetaCondition(feature, >) for feature in generic_features] + +@test isequal(generic_metaconditions, int_metaconditions) +@test hash.(generic_metaconditions) == hash.(int_metaconditions) + +@test_nowarn scalarlogiset(dataset; use_full_memoization = true, use_onestep_memoization = true, relations = relations, conditions = generic_metaconditions) +@test_throws AssertionError scalarlogiset(dataset; use_full_memoization = true, use_onestep_memoization = false, relations = relations, conditions = generic_metaconditions) +@test_nowarn scalarlogiset(dataset; use_full_memoization = false, relations = relations, conditions = generic_metaconditions, onestep_precompute_globmemoset = false, onestep_precompute_relmemoset = false) +@test_nowarn scalarlogiset(dataset; use_full_memoization = false, relations = relations, conditions = generic_metaconditions, onestep_precompute_globmemoset = true, onestep_precompute_relmemoset = true) + +logiset = @test_nowarn scalarlogiset(dataset; use_full_memoization = false, relations = relations, conditions = generic_metaconditions) + +generic_complete_metaconditions = vcat([[ScalarMetaCondition(feature, >), ScalarMetaCondition(feature, <)] for feature in generic_features]...) + +complete_logiset = @test_nowarn scalarlogiset(dataset; use_full_memoization = false, relations = relations, conditions = generic_complete_metaconditions) + +rng = Random.MersenneTwister(1) +alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, generic_features), rand(rng, [>, <]), rand(rng)) for i in 1:n_instances]); +syntaxstring.(alph) +_formulas = [randformulatree(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:16:end]...]) for i in 1:20]; +syntaxstring.(_formulas) .|> println; + +i_instance = 1 +@test_nowarn checkcondition(atom(alph.propositions[1]), complete_logiset, i_instance, first(allworlds(complete_logiset, i_instance))) + +c1 = @test_nowarn [ + [check(φ, logiset, i_instance, w) for φ in _formulas] + for w in allworlds(logiset, i_instance)] +c2 = @test_nowarn [ + [check(φ, complete_logiset, i_instance, w) for φ in _formulas] + for w in allworlds(complete_logiset, i_instance)] + +@test c1 == c2 diff --git a/test/logisets.jl b/test/logisets/logisets.jl similarity index 95% rename from test/logisets.jl rename to test/logisets/logisets.jl index b1539ae..f1eef15 100644 --- a/test/logisets.jl +++ b/test/logisets/logisets.jl @@ -155,7 +155,8 @@ rng = Random.MersenneTwister(1) alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]) syntaxstring.(alph) _formulas = [randformulatree(rng, 4, alph, [NEGATION, CONJUNCTION, IMPLICATION, DIAMOND, BOX]) for i in 1:10] -# syntaxstring.(_formulas) +@test_nowarn syntaxstring.(_formulas) +@test_nowarn syntaxstring.(_formulas; threshold_decimals = 2) c1 = @test_nowarn [check(φ, bool_logiset, 1, w) for φ in _formulas] c2 = @test_nowarn [check(φ, bool_logiset, 1, w; use_memo = nothing) for φ in _formulas] @@ -196,10 +197,11 @@ using SoleModels: ScalarOneStepMemoset relations = [identityrel, globalrel] -bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(bool_relationalmemoset, bool_globalmemoset, metaconditions, relations) +# bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(bool_relationalmemoset, bool_globalmemoset, metaconditions, relations) +bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset{Bool}(bool_relationalmemoset, bool_globalmemoset, metaconditions, relations) -bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(bool_relationalmemoset, bool_globalmemoset, metaconditions, relations) bool_onestepmemoset_empty = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations) +bool_onestepmemoset_full = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations; precompute_globmemoset = false, precompute_relmemoset = false) bool_onestepmemoset_full = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations; precompute_globmemoset = true, precompute_relmemoset = true) @test_nowarn SupportedLogiset(bool_logiset, bool_onestepmemoset) diff --git a/test/logisets/multilogisets.jl b/test/logisets/multilogisets.jl new file mode 100644 index 0000000..9457296 --- /dev/null +++ b/test/logisets/multilogisets.jl @@ -0,0 +1,65 @@ +using Test +using StatsBase +using Random +using SoleLogics +using SoleModels +using SoleModels.DimensionalDatasets + + +n_instances = 2 +_nvars = 2 + +multidataset, multirelations = collect.(zip([ + (Array(reshape(1.0:4.0, _nvars,n_instances)), [globalrel]), + (Array(reshape(1.0:12.0, 3,_nvars,n_instances)), [IARelations..., globalrel]), + (Array(reshape(1.0:36.0, 3,3,_nvars,n_instances)), [IA2DRelations..., globalrel]), +]...)) + +multilogiset = @test_nowarn scalarlogiset(multidataset) + +generic_features = collect(Iterators.flatten([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:_nvars])) +metaconditions = [ScalarMetaCondition(feature, >) for feature in generic_features] +multilogiset = @test_nowarn scalarlogiset(multidataset; use_full_memoization = false, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) +multilogiset = @test_nowarn scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) + +metaconditions = vcat([[ScalarMetaCondition(feature, >), ScalarMetaCondition(feature, <)] for feature in generic_features]...) +complete_supported_multilogiset = @test_nowarn scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) + +rng = Random.MersenneTwister(1) +alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, generic_features), rand(rng, [>, <]), rand(rng)) for i in 1:n_instances]); +# syntaxstring.(alph) + +i_instance = 1 + +multiformulas = [begin + _formulas_dict = Dict{Int,SoleLogics.AbstractFormula}() + for (i_modality, relations) in enumerate(multirelations) + f = randformulatree(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:32:end]...]) + if rand(Bool) + # coin = rand(1:3) + coin = rand(2:3) + f = begin + if coin == 1 + AnchoredFormula(f, SoleModels.GlobalCheck()) + elseif coin == 2 + AnchoredFormula(f, SoleModels.CenteredCheck()) + else + AnchoredFormula(f, SoleModels.WorldCheck(rand(collect(allworlds(modality(multilogiset, i_modality), i_instance))))) + end + end + _formulas_dict[i_modality] = f + end + end + MultiFormula(_formulas_dict) +end for i in 1:200]; +# syntaxstring.(multiformulas) .|> println; + +@test_throws MethodError checkcondition(atom(alph.propositions[1]), multilogiset, i_instance) + +c1 = @test_nowarn [check(φ, multilogiset, i_instance) for φ in multiformulas] +c3 = @test_nowarn [check(φ, complete_supported_multilogiset, i_instance) for φ in multiformulas] + +@test c1 == c3 + +@test_nowarn concatdatasets(multilogiset, multilogiset, multilogiset) +@test_nowarn concatdatasets(complete_supported_multilogiset, complete_supported_multilogiset) diff --git a/test/runtests.jl b/test/runtests.jl index b979669..26ae8ab 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,8 +17,10 @@ println("Julia version: ", VERSION) test_suites = [ ("Logisets", [ - "logisets.jl", - "dimensional-logisets.jl", + "logisets/logisets.jl", + "logisets/cube2logiset.jl", + "logisets/dataframe2logiset.jl", + "logisets/multilogisets.jl", ]), ("Models", ["base.jl", ]), ("Miscellaneous", ["misc.jl", "minify.jl"]), From 5ebedd0de225ef18d726c3c8910eed445a0aea8f Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 20 Jun 2023 14:42:55 +0200 Subject: [PATCH 16/77] Minor clean --- src/logisets/main.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/logisets/main.jl b/src/logisets/main.jl index ae833f5..bb8e8ba 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -107,7 +107,3 @@ function default_full_memoset_type(X::AbstractLogiset) FullMemoset # end end - -# # TODO figure out which convert function works best: -# convert(::Type{<:MultiLogiset{T}}, X::MD) where {T,MD<:AbstractLogiset{T}} = MultiLogiset{MD}([X]) -# convert(::Type{<:MultiLogiset}, X::AbstractLogiset) = MultiLogiset([X]) From 6b056a4451f3d7360d23a0246086a0b7b3eb87e9 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 20 Jun 2023 16:53:15 +0200 Subject: [PATCH 17/77] Fix exports, modalities->eachmodality, add tests. --- src/SoleModels.jl | 40 ++++++++++----- .../dimensional-structures/logiset.jl | 20 ++++---- src/logisets/dimensional-structures/main.jl | 6 +-- src/logisets/logiset.jl | 9 +++- src/logisets/main.jl | 37 ++------------ src/logisets/multilogiset.jl | 50 +++++++++---------- src/logisets/scalar/canonical-conditions.jl | 18 +++++-- src/logisets/scalar/main.jl | 12 ----- src/logisets/supported-logiset.jl | 9 ++-- src/other/main.jl | 2 +- test/logisets/cube2logiset.jl | 4 ++ test/logisets/dataframe2logiset.jl | 8 +++ test/logisets/logisets.jl | 12 +++++ test/logisets/multilogisets.jl | 10 ++-- test/runtests.jl | 2 +- 15 files changed, 131 insertions(+), 108 deletions(-) diff --git a/src/SoleModels.jl b/src/SoleModels.jl index cb6b133..a609466 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -61,9 +61,6 @@ export AbstractFeature, export propositions -export parsecondition -export modalities - export slicedataset, concatdatasets export World, Feature, featvalue @@ -73,28 +70,47 @@ export SupportedLogiset, nmemoizedvalues export ExplicitBooleanLogiset, checkcondition export ExplicitLogiset, ScalarCondition -export ninstances, nfeatures -export MultiLogiset, nmodalities, modalities +export ninstances +export MultiLogiset, modality, nmodalities, modalities export MultiFormula -export UnivariateMin, UnivariateMax, - UnivariateSoftMin, UnivariateSoftMax, - MultivariateFeature - -export VarFeature, - UnivariateNamedFeature, +export UnivariateNamedFeature, UnivariateFeature export computefeature export scalarlogiset -export initlogiset, ninstances, maxchannelsize, worldtype, dimensionality, allworlds, featvalue + +export initlogiset, maxchannelsize, worldtype, dimensionality, allworlds, featvalue, nvariables export ScalarMetaCondition +export inverse_test_operator, dual_test_operator, + apply_test_operator, + TestOperator + +export MixedFeature, CanonicalFeature, canonical_geq, canonical_leq + +export canonical_geq_95, canonical_geq_90, canonical_geq_85, canonical_geq_80, canonical_geq_75, canonical_geq_70, canonical_geq_60, + canonical_leq_95, canonical_leq_90, canonical_leq_85, canonical_leq_80, canonical_leq_75, canonical_leq_70, canonical_leq_60 + +export VarFeature, + UnivariateMin, UnivariateMax, + UnivariateSoftMin, UnivariateSoftMax, + MultivariateFeature + +export FullDimensionalFrame # Definitions for logical datasets (i.e., logisets) include("logisets/main.jl") +using .DimensionalDatasets: OneWorld, Interval, Interval2D +using .DimensionalDatasets: IARelations +using .DimensionalDatasets: IA2DRelations +using .DimensionalDatasets: identityrel +using .DimensionalDatasets: globalrel + +const GenericDataset = Union{SoleData.AbstractDimensionalDataset,AbstractDataFrame,AbstractLogiset,MultiLogiset} + end diff --git a/src/logisets/dimensional-structures/logiset.jl b/src/logisets/dimensional-structures/logiset.jl index 330e0b9..c8b7201 100644 --- a/src/logisets/dimensional-structures/logiset.jl +++ b/src/logisets/dimensional-structures/logiset.jl @@ -351,27 +351,27 @@ end ############################################################################################ function instances( - X::UniformFullDimensionalLogiset{U,W,N}, + X::UniformFullDimensionalLogiset{U,W,0}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false) -) where {U,W<:OneWorld,N} - UniformFullDimensionalLogiset{U,W,N}(if return_view == Val(true) @view X.featstruct[inds,:] else X.featstruct[inds,:] end, features(X)) +) where {U,W} + UniformFullDimensionalLogiset{U,W,0}(if return_view == Val(true) @view X.featstruct[inds,:] else X.featstruct[inds,:] end, features(X)) end function instances( - X::UniformFullDimensionalLogiset{U,W,N}, + X::UniformFullDimensionalLogiset{U,W,1}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false) -) where {U,W<:Interval,N} - UniformFullDimensionalLogiset{U,W,N}(if return_view == Val(true) @view X.featstruct[:,:,inds,:] else X.featstruct[:,:,inds,:] end, features(X)) +) where {U,W} + UniformFullDimensionalLogiset{U,W,1}(if return_view == Val(true) @view X.featstruct[:,:,inds,:] else X.featstruct[:,:,inds,:] end, features(X)) end function instances( - X::UniformFullDimensionalLogiset{U,W,N}, + X::UniformFullDimensionalLogiset{U,W,2}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false) -) where {U,W<:Interval2D,N} - UniformFullDimensionalLogiset{U,W,N}(if return_view == Val(true) @view X.featstruct[:,:,:,:,inds,:] else X.featstruct[:,:,:,:,inds,:] end, features(X)) +) where {U,W} + UniformFullDimensionalLogiset{U,W,2}(if return_view == Val(true) @view X.featstruct[:,:,:,:,inds,:] else X.featstruct[:,:,:,:,inds,:] end, features(X)) end ############################################################################################ @@ -380,7 +380,7 @@ function concatdatasets(Xs::UniformFullDimensionalLogiset{U,W,N}...) where {U,W< @assert allequal(features.(Xs)) "Cannot concatenate " * "UniformFullDimensionalLogiset's with different features: " * "$(@show features.(Xs))" - UniformFullDimensionalLogiset{U,W,N}(concatdatasets([X.featstruct for X in Xs]...), features(first(Xs))) + UniformFullDimensionalLogiset{U,W,N}(cat([X.featstruct for X in Xs]...; dims=1+N*2), features(first(Xs))) end isminifiable(::UniformFullDimensionalLogiset) = true diff --git a/src/logisets/dimensional-structures/main.jl b/src/logisets/dimensional-structures/main.jl index ab5468b..daba00a 100644 --- a/src/logisets/dimensional-structures/main.jl +++ b/src/logisets/dimensional-structures/main.jl @@ -16,7 +16,7 @@ import SoleLogics: worldtype, accessibles, allworlds, alphabet using SoleData import SoleData: _isnan, hasnans, nvariables, maxchannelsize, channelsize -import SoleData: instance, get_instance, instances, concatdatasets +import SoleData: instance, get_instance, concatdatasets import SoleData: displaystructure import SoleData: dimensionality @@ -46,8 +46,6 @@ import SoleModels: worldtype, allworlds, featvalue, featvalue! import SoleModels: featchannel, readfeature, featvalues!, allfeatvalues import SoleData: get_instance, ninstances, nvariables, channelsize, eltype -export nvariables - ############################################################################################ # Frame-specific logisets @@ -57,6 +55,8 @@ include("onestep-memosets.jl") export initlogiset, ninstances, maxchannelsize, worldtype, dimensionality, allworlds, featvalue +export nvariables + include("computefeature.jl") # Bindings for interpreting dataset structures as logisets diff --git a/src/logisets/logiset.jl b/src/logisets/logiset.jl index 40b3c67..66b962b 100644 --- a/src/logisets/logiset.jl +++ b/src/logisets/logiset.jl @@ -108,7 +108,7 @@ function instances( return_view::Union{Val{true},Val{false}} = Val(false); kwargs... ) - error("Please, provide method instances(::$(typeof(X))).") + error("Please, provide method instances(::$(typeof(X)), ::$(typeof(inds)), ::$(typeof(return_view))).") end function concatdatasets(Xs::AbstractLogiset...) @@ -157,6 +157,13 @@ frametype(X::AbstractLogiset) = frametype(typeof(X)) representatives(X::AbstractLogiset, i_instance::Integer, args...) = representatives(frame(X, i_instance), args...) +############################################################################################ +# Non mandatory + +function nfeatures(X::AbstractLogiset) + error("Please, provide method nfeatures(::$(typeof(X))).") +end + ############################################################################################ diff --git a/src/logisets/main.jl b/src/logisets/main.jl index bb8e8ba..bdf10da 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -33,7 +33,7 @@ include("memosets.jl") include("supported-logiset.jl") -export MultiLogiset, modalities, worldtypes, nmodalities +export MultiLogiset, eachmodality, worldtypes, nmodalities # Multiframe version of logisets, for representing multimodal datasets include("multilogiset.jl") @@ -44,43 +44,16 @@ export check, AnchoredFormula include("check-modes.jl") include("check.jl") -# TODO remove? -function nfeatures end +export nfeatures include("scalar/main.jl") -include("dimensional-structures/main.jl") - -# export get_ontology, -# get_interval_ontology - -# export DimensionalLogiset, Logiset, SupportedScalarLogiset - -# using .DimensionalDatasets: nfeatures, nrelations, -# # -# relations, -# # -# GenericModalDataset, -# AbstractLogiset, -# AbstractActiveScalarLogiset, -# DimensionalLogiset, -# Logiset, -# SupportedScalarLogiset -# using .DimensionalDatasets: AbstractWorld, AbstractRelation -# using .DimensionalDatasets: AbstractWorldSet, WorldSet -# using .DimensionalDatasets: FullDimensionalFrame +export initlogiset, ninstances, maxchannelsize, worldtype, dimensionality, allworlds, featvalue -# using .DimensionalDatasets: Ontology, worldtype +export nvariables -# using .DimensionalDatasets: get_ontology, -# get_interval_ontology - -# using .DimensionalDatasets: OneWorld, OneWorldOntology - -# using .DimensionalDatasets: Interval, Interval2D - -# using .DimensionalDatasets: IARelations +include("dimensional-structures/main.jl") function default_relmemoset_type(X::AbstractLogiset) # TODO? diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index d97fae4..e7848aa 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -1,4 +1,4 @@ -import SoleData: modality, nmodalities +import SoleData: modality, nmodalities, eachmodality """ struct MultiLogiset{L<:AbstractLogiset} @@ -39,44 +39,44 @@ struct MultiLogiset{L<:AbstractLogiset} end end -modalities(X::MultiLogiset) = X.modalities +eachmodality(X::MultiLogiset) = X.modalities Base.length(X::MultiLogiset) = length(nmodalities(X)) -Base.push!(X::MultiLogiset, f::AbstractLogiset) = push!(modalities(X), f) -Base.getindex(X::MultiLogiset, i_modality::Integer) = modalities(X)[i_modality] +Base.push!(X::MultiLogiset, f::AbstractLogiset) = push!(eachmodality(X), f) +Base.getindex(X::MultiLogiset, i_modality::Integer) = eachmodality(X)[i_modality] Base.iterate(X::MultiLogiset, state=1) = state > nmodalities(X) ? nothing : (modality(X, state), state+1) modalitytype(::Type{<:MultiLogiset{L}}) where {L<:AbstractLogiset} = L modalitytype(X::MultiLogiset) = modalitytype(typeof(X)) -modality(X::MultiLogiset, i_modality::Integer) = modalities(X)[i_modality] -nmodalities(X::MultiLogiset) = length(modalities(X)) +modality(X::MultiLogiset, i_modality::Integer) = eachmodality(X)[i_modality] +nmodalities(X::MultiLogiset) = length(eachmodality(X)) ninstances(X::MultiLogiset) = ninstances(modality(X, 1)) worldtype(X::MultiLogiset, i_modality::Integer) = worldtype(modality(X, i_modality)) -worldtypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(worldtype.(modalities(X))) +worldtypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(worldtype.(eachmodality(X))) featvaltype(X::MultiLogiset, i_modality::Integer) = featvaltype(modality(X, i_modality)) -featvaltypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(featvaltype.(modalities(X))) +featvaltypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(featvaltype.(eachmodality(X))) featuretype(X::MultiLogiset, i_modality::Integer) = featuretype(modality(X, i_modality)) -featuretypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(featuretype.(modalities(X))) +featuretypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(featuretype.(eachmodality(X))) frametype(X::MultiLogiset, i_modality::Integer) = frametype(modality(X, i_modality)) -frametypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(frametype.(modalities(X))) +frametypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(frametype.(eachmodality(X))) function instances( X::MultiLogiset{L}, inds::AbstractVector{<:Integer}, - args...; + return_view::Union{Val{true},Val{false}} = Val(false); kwargs... ) where {L<:AbstractLogiset} - MultiLogiset{L}(Vector{L}(map(modality->instances(modality, inds, args...; kwargs...), modalities(X)))) + MultiLogiset{L}(Vector{L}(map(modality->instances(modality, inds, return_view; kwargs...), eachmodality(X)))) end function concatdatasets(Xs::MultiLogiset...) MultiLogiset([ - concatdatasets([modalities(X)[i_mod] for X in Xs]...) for i_mod in 1:nmodalities(Xs) + concatdatasets([eachmodality(X)[i_mod] for X in Xs]...) for i_mod in 1:nmodalities(Xs) ]) end @@ -92,7 +92,7 @@ function displaystructure(X::MultiLogiset; indent_str = "", include_ninstances = push!(pieces, indent_str * "├ # instances:\t$(ninstances(X))") end # push!(pieces, indent_str * "├ modalitytype:\t$(modalitytype(X))") - for (i_modality, mod) in enumerate(modalities(X)) + for (i_modality, mod) in enumerate(eachmodality(X)) out = "" if i_modality == nmodalities(X) out *= "$(indent_str)└" @@ -120,19 +120,19 @@ function featvalue( end -hasnans(X::MultiLogiset) = any(hasnans.(modalities(X))) +hasnans(X::MultiLogiset) = any(hasnans.(eachmodality(X))) -isminifiable(X::MultiLogiset) = any(isminifiable.(modalities(X))) +isminifiable(X::MultiLogiset) = any(isminifiable.(eachmodality(X))) function minify(X::MultiLogiset) - if !any(map(isminifiable, modalities(X))) - if !all(map(isminifiable, modalities(X))) - @error "Cannot perform minification with modalities of types $(typeof.(modalities(X))). Please use a minifiable format (e.g., SupportedScalarLogiset)." + if !any(map(isminifiable, eachmodality(X))) + if !all(map(isminifiable, eachmodality(X))) + @error "Cannot perform minification with modalities of types $(typeof.(eachmodality(X))). Please use a minifiable format (e.g., SupportedScalarLogiset)." else - @warn "Cannot perform minification on some of the modalities provided. Please use a minifiable format (e.g., SupportedScalarLogiset) ($(typeof.(modalities(X))) were used instead)." + @warn "Cannot perform minification on some of the modalities provided. Please use a minifiable format (e.g., SupportedScalarLogiset) ($(typeof.(eachmodality(X))) were used instead)." end end - X, backmap = zip([!isminifiable(X) ? minify(X) : (X, identity) for X in modalities(X)]...) + X, backmap = zip([!isminifiable(X) ? minify(X) : (X, identity) for X in eachmodality(X)]...) X, backmap end @@ -151,11 +151,11 @@ end # TODO remove: -# Base.size(X::MultiLogiset) = map(size, modalities(X)) +# Base.size(X::MultiLogiset) = map(size, eachmodality(X)) -# # maxchannelsize(X::MultiLogiset) = map(maxchannelsize, modalities(X)) # TODO: figure if this is useless or not. Note: channelsize doesn't make sense at this point. -# nfeatures(X::MultiLogiset) = map(nfeatures, modalities(X)) # Note: used for safety checks in fit_tree.jl -# # nrelations(X::MultiLogiset) = map(nrelations, modalities(X)) # TODO: figure if this is useless or not +# # maxchannelsize(X::MultiLogiset) = map(maxchannelsize, eachmodality(X)) # TODO: figure if this is useless or not. Note: channelsize doesn't make sense at this point. +# nfeatures(X::MultiLogiset) = map(nfeatures, eachmodality(X)) # Note: used for safety checks in fit_tree.jl +# # nrelations(X::MultiLogiset) = map(nrelations, eachmodality(X)) # TODO: figure if this is useless or not # nfeatures(X::MultiLogiset, i_modality::Integer) = nfeatures(modality(X, i_modality)) # nrelations(X::MultiLogiset, i_modality::Integer) = nrelations(modality(X, i_modality)) diff --git a/src/logisets/scalar/canonical-conditions.jl b/src/logisets/scalar/canonical-conditions.jl index 1866d83..8ccfc45 100644 --- a/src/logisets/scalar/canonical-conditions.jl +++ b/src/logisets/scalar/canonical-conditions.jl @@ -8,12 +8,22 @@ struct CanonicalFeatureLeq <: CanonicalFeature end; const canonical_leq = Canon # ⪴_α and ⪳_α, that is, "*at least α⋅100 percent* of the values on this world are at least, or at most ..." struct CanonicalFeatureGeqSoft <: CanonicalFeature - alpha :: AbstractFloat - CanonicalFeatureGeqSoft(a::T) where {T<:Real} = (a > 0 && a < 1) ? new(a) : throw_n_log("Invalid instantiation for test operator: CanonicalFeatureGeqSoft($(a))") + alpha :: AbstractFloat + function CanonicalFeatureGeqSoft(a::T) where {T<:Real} + if ! (a > 0 && a < 1) + error("Invalid instantiation of feature: CanonicalFeatureGeqSoft($(a))") + end + new(a) + end end; struct CanonicalFeatureLeqSoft <: CanonicalFeature - alpha :: AbstractFloat - CanonicalFeatureLeqSoft(a::T) where {T<:Real} = (a > 0 && a < 1) ? new(a) : throw_n_log("Invalid instantiation for test operator: CanonicalFeatureLeqSoft($(a))") + alpha :: AbstractFloat + function CanonicalFeatureLeqSoft(a::T) where {T<:Real} + if ! (a > 0 && a < 1) + error("Invalid instantiation of feature: CanonicalFeatureLeqSoft($(a))") + end + new(a) + end end; const canonical_geq_95 = CanonicalFeatureGeqSoft((Rational(95,100))); diff --git a/src/logisets/scalar/main.jl b/src/logisets/scalar/main.jl index 2f0ec83..090c3e8 100644 --- a/src/logisets/scalar/main.jl +++ b/src/logisets/scalar/main.jl @@ -1,6 +1,3 @@ -export inverse_test_operator, dual_test_operator, - apply_test_operator, - TestOperator # Features for (multi)variate data include("var-features.jl") @@ -14,11 +11,6 @@ include("conditions.jl") # Templates for formulas of scalar conditions (e.g., templates for ⊤, f ⋈ t, ⟨R⟩ f ⋈ t, etc.) include("templated-formulas.jl") -export MixedFeature, CanonicalFeature, canonical_geq, canonical_leq - -export canonical_geq_95, canonical_geq_90, canonical_geq_85, canonical_geq_80, canonical_geq_75, canonical_geq_70, canonical_geq_60, - canonical_leq_95, canonical_leq_90, canonical_leq_85, canonical_leq_80, canonical_leq_75, canonical_leq_70, canonical_leq_60 - # Types for representing common associations between features and operators include("canonical-conditions.jl") @@ -33,7 +25,3 @@ include("dataset-bindings.jl") include("memosets.jl") include("onestep-memoset.jl") - -export UnivariateMin, UnivariateMax, - UnivariateSoftMin, UnivariateSoftMax, - MultivariateFeature diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index 5f344da..f0cae67 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -91,7 +91,8 @@ struct SupportedLogiset{ end @assert allequal([ninstances(base), ninstances.(supports)...]) "Consistency " * - "check failed! Mismatching ninstances for base and memoset(s): $(ninstances(base)) and $(ninstances.(supports))" + "check failed! Mismatching ninstances for " * + "base and memoset(s): $(ninstances(base)) and $(ninstances.(supports))" N = length(supports) MS = typeof(supports) @@ -244,10 +245,10 @@ function instances( return_view::Union{Val{true},Val{false}} = Val(false); kwargs... ) - _instances = (X)->instances(X, inds, return_view; kwargs...) + _instances = (_X)->instances(_X, inds, return_view; kwargs...) SupportedLogiset( _instances(base(X)), - [_instances(supp) for supp in supports(X)]..., + (_instances.(supports(X)))..., ) end @@ -256,7 +257,7 @@ function concatdatasets(Xs::SupportedLogiset...) "SupportedLogiset's with different nsupports: " * "$(@show nsupports.(Xs))" SupportedLogiset( - concatdatasets([base(X) for X in Xs]), + concatdatasets([base(X) for X in Xs]...), [concatdatasets([supports(X)[i_supp] for X in Xs]...) for i_supp in 1:nsupports(first(Xs))]..., ) end diff --git a/src/other/main.jl b/src/other/main.jl index 6243c09..acd6e09 100644 --- a/src/other/main.jl +++ b/src/other/main.jl @@ -11,7 +11,7 @@ # export DimensionalLogiset, Logiset, SupportedScalarLogiset -# const GenericModalDataset = Union{AbstractDimensionalDataset,AbstractLogiset,MultiLogiset} +# const GenericDataset = Union{AbstractDimensionalDataset,AbstractLogiset,MultiLogiset} # # Dimensional ontologies # include("dimensional-ontologies.jl") diff --git a/test/logisets/cube2logiset.jl b/test/logisets/cube2logiset.jl index 7f13972..d6340bc 100644 --- a/test/logisets/cube2logiset.jl +++ b/test/logisets/cube2logiset.jl @@ -66,6 +66,10 @@ c3 = @test_nowarn [ @test c1 == c3 + +@test_nowarn slicedataset(logiset, [1]) +@test_nowarn slicedataset(complete_supported_logiset, [1]) + @test_nowarn concatdatasets(logiset, logiset, logiset) @test_nowarn concatdatasets(complete_supported_logiset, complete_supported_logiset) diff --git a/test/logisets/dataframe2logiset.jl b/test/logisets/dataframe2logiset.jl index de7eda4..ebad62f 100644 --- a/test/logisets/dataframe2logiset.jl +++ b/test/logisets/dataframe2logiset.jl @@ -3,6 +3,7 @@ using StatsBase using Random using SoleLogics using SoleModels +using DataFrames using SoleModels.DimensionalDatasets n_instances = 2 @@ -63,3 +64,10 @@ c2 = @test_nowarn [ for w in allworlds(complete_logiset, i_instance)] @test c1 == c2 + + +@test_nowarn slicedataset(logiset, [1]) +@test_nowarn slicedataset(complete_logiset, [1]) + +@test_nowarn concatdatasets(logiset, logiset, logiset) +@test_nowarn concatdatasets(complete_logiset, complete_logiset, complete_logiset) diff --git a/test/logisets/logisets.jl b/test/logisets/logisets.jl index f1eef15..b9d97c5 100644 --- a/test/logisets/logisets.jl +++ b/test/logisets/logisets.jl @@ -252,3 +252,15 @@ c4 = @test_nowarn [check(φ, bool_logiset_3layer, 1, w; perform_normalization = @test SoleModels.nmemoizedvalues(bool_logiset_3layer.supports[1].relmemoset) > 0 + +@test_nowarn slicedataset(bool_relationalmemoset, [1]) +@test_nowarn slicedataset(bool_globalmemoset, [1]) +@test_nowarn slicedataset(bool_onestepmemoset, [1]) +@test_nowarn slicedataset(bool_logiset_2layer, [1]) +@test_nowarn slicedataset(bool_logiset_3layer, [1]) + +@test_nowarn concatdatasets(bool_relationalmemoset, bool_relationalmemoset, bool_relationalmemoset) +@test_nowarn concatdatasets(bool_globalmemoset, bool_globalmemoset, bool_globalmemoset) +@test_nowarn concatdatasets(bool_onestepmemoset, bool_onestepmemoset, bool_onestepmemoset) +@test_nowarn concatdatasets(bool_logiset_2layer, bool_logiset_2layer, bool_logiset_2layer) +@test_nowarn concatdatasets(bool_logiset_3layer, bool_logiset_3layer, bool_logiset_3layer) diff --git a/test/logisets/multilogisets.jl b/test/logisets/multilogisets.jl index 9457296..6e209ae 100644 --- a/test/logisets/multilogisets.jl +++ b/test/logisets/multilogisets.jl @@ -25,6 +25,13 @@ multilogiset = @test_nowarn scalarlogiset(multidataset; use_full_memoization = t metaconditions = vcat([[ScalarMetaCondition(feature, >), ScalarMetaCondition(feature, <)] for feature in generic_features]...) complete_supported_multilogiset = @test_nowarn scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) + +@test_nowarn slicedataset(multilogiset, [2,1]) +@test_nowarn slicedataset(complete_supported_multilogiset, [2,1]) +@test_nowarn concatdatasets(multilogiset, multilogiset, multilogiset) +@test_nowarn concatdatasets(complete_supported_multilogiset, complete_supported_multilogiset) + + rng = Random.MersenneTwister(1) alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, generic_features), rand(rng, [>, <]), rand(rng)) for i in 1:n_instances]); # syntaxstring.(alph) @@ -60,6 +67,3 @@ c1 = @test_nowarn [check(φ, multilogiset, i_instance) for φ in multiformulas] c3 = @test_nowarn [check(φ, complete_supported_multilogiset, i_instance) for φ in multiformulas] @test c1 == c3 - -@test_nowarn concatdatasets(multilogiset, multilogiset, multilogiset) -@test_nowarn concatdatasets(complete_supported_multilogiset, complete_supported_multilogiset) diff --git a/test/runtests.jl b/test/runtests.jl index 26ae8ab..b97fdb3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -24,7 +24,7 @@ test_suites = [ ]), ("Models", ["base.jl", ]), ("Miscellaneous", ["misc.jl", "minify.jl"]), - # ("Parse", ["parse.jl", ]), + ("Parse", ["parse.jl", ]), ] @testset "SoleModels.jl" begin From 0e7f9ded8999d06fc04f94db959ac224d8881033 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Wed, 21 Jun 2023 22:12:39 +0200 Subject: [PATCH 18/77] FinalModel -> LeafModel. Fix around while fixing ModalDecisionTrees.jl --- README.md | 2 +- src/SoleModels.jl | 7 +- .../dimensional-structures/computefeature.jl | 2 +- .../dataset-bindings.jl | 10 +- .../representatives/Full1DFrame+IA.jl | 12 +-- .../representatives/Full1DFrame+RCC.jl | 12 +-- src/logisets/features.jl | 12 +-- src/logisets/logiset.jl | 4 +- src/logisets/main.jl | 2 +- src/logisets/multilogiset.jl | 9 +- src/logisets/scalar/conditions.jl | 44 +++++++-- src/logisets/scalar/dataset-bindings.jl | 8 +- src/logisets/scalar/onestep-memoset.jl | 35 ++++--- src/logisets/scalar/templated-formulas.jl | 8 +- src/logisets/scalar/test-operators.jl | 16 ++++ src/logisets/scalar/var-features.jl | 16 +++- src/logisets/supported-logiset.jl | 2 +- src/machine-learning.jl | 8 +- src/models/base.jl | 96 ++++++++++++------- src/models/rule-evaluation.jl | 2 +- src/models/symbolic-utils.jl | 50 +++++++--- src/other/active-logiset.jl | 2 +- src/other/dimensional-logiset.jl | 6 +- src/other/dimensional-ontologies.jl | 8 +- src/other/passive-dimensional-logiset.jl | 4 +- src/other/scalar-logiset.jl | 7 +- src/utils.jl | 41 -------- test/base.jl | 6 +- test/logisets/logisets.jl | 6 +- test/misc.jl | 6 +- test/runtests.jl | 2 +- 31 files changed, 257 insertions(+), 188 deletions(-) diff --git a/README.md b/README.md index 17b5b8f..0f189b8 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ These definitions provide a unified base for implementing symbolic algorithms, s ### Basic models: -- Final models: wrapping native Julia computation (e.g., constants, functions); +- Leaf models: wrapping native Julia computation (e.g., constants, functions); - Rules: structures with `IF antecedent THEN consequent END` semantics; - Branches: structures with `IF antecedent THEN pos_consequent ELSE neg_consequent END` semantics. diff --git a/src/SoleModels.jl b/src/SoleModels.jl index a609466..46c2a0c 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -56,8 +56,7 @@ export minify, isminifiable # Minification interface for lossless data compression include("utils/minify.jl") -export AbstractFeature, - Feature, ExternalFWDFeature +export AbstractFeature, Feature export propositions @@ -86,10 +85,6 @@ export initlogiset, maxchannelsize, worldtype, dimensionality, allworlds, featva export ScalarMetaCondition -export inverse_test_operator, dual_test_operator, - apply_test_operator, - TestOperator - export MixedFeature, CanonicalFeature, canonical_geq, canonical_leq export canonical_geq_95, canonical_geq_90, canonical_geq_85, canonical_geq_80, canonical_geq_75, canonical_geq_70, canonical_geq_60, diff --git a/src/logisets/dimensional-structures/computefeature.jl b/src/logisets/dimensional-structures/computefeature.jl index 8b73f00..e580111 100644 --- a/src/logisets/dimensional-structures/computefeature.jl +++ b/src/logisets/dimensional-structures/computefeature.jl @@ -19,7 +19,7 @@ function computeunivariatefeature(f::UnivariateFeature{U}, varchannel::Union{T,A (f.f(SoleBase.vectorize(varchannel);))::U end function computeunivariatefeature(f::UnivariateNamedFeature, varchannel::Union{T,AbstractArray{T}}) where {T} - @error "Cannot intepret UnivariateNamedFeature on any structure at all." + error("Cannot intepret UnivariateNamedFeature on any structure at all.") end function computeunivariatefeature(f::UnivariateValue{U}, varchannel::Union{T,AbstractArray{T}}) where {U<:Real,T} varchannel::U diff --git a/src/logisets/dimensional-structures/dataset-bindings.jl b/src/logisets/dimensional-structures/dataset-bindings.jl index 6f75d23..6e93d86 100644 --- a/src/logisets/dimensional-structures/dataset-bindings.jl +++ b/src/logisets/dimensional-structures/dataset-bindings.jl @@ -1,9 +1,7 @@ using SoleModels: AbstractUnivariateFeature using SoleData: AbstractDimensionalDataset, - UniformDimensionalDataset, - DimensionalInstance, - DimensionalChannel + UniformDimensionalDataset import SoleData: ninstances, nvariables @@ -67,9 +65,9 @@ function readfeature( w::W, f::VarFeature{U}, ) where {U,W<:AbstractWorld} - _interpret_world(::OneWorld, instance::DimensionalInstance{T,1}) where {T} = instance - _interpret_world(w::Interval, instance::DimensionalInstance{T,2}) where {T} = instance[w.x:w.y-1,:] - _interpret_world(w::Interval2D, instance::DimensionalInstance{T,3}) where {T} = instance[w.x.x:w.x.y-1,w.y.x:w.y.y-1,:] + _interpret_world(::OneWorld, instance::AbstractArray{T,1}) where {T} = instance + _interpret_world(w::Interval, instance::AbstractArray{T,2}) where {T} = instance[w.x:w.y-1,:] + _interpret_world(w::Interval2D, instance::AbstractArray{T,3}) where {T} = instance[w.x.x:w.x.y-1,w.y.x:w.y.y-1,:] wchannel = _interpret_world(w, featchannel) computefeature(f, wchannel)::U end diff --git a/src/logisets/dimensional-structures/representatives/Full1DFrame+IA.jl b/src/logisets/dimensional-structures/representatives/Full1DFrame+IA.jl index 7df404a..d242ac8 100644 --- a/src/logisets/dimensional-structures/representatives/Full1DFrame+IA.jl +++ b/src/logisets/dimensional-structures/representatives/Full1DFrame+IA.jl @@ -13,12 +13,12 @@ using SoleLogics: _IA_AorO, _IA_AiorOi, _IA_DorBorE, _IA_DiorBiorEi, _IA_I, IA72 ############################################################################################ # When defining `representatives` for minimum & maximum features, we find that we can # categorize interval relations according to their behavior. -# Consider the decision ⟨R⟩ (minimum(A1) ≥ 10) evaluated on a world w = (x,y): -# - With R = identityrel, it requires computing minimum(A1) on w; -# - With R = globalrel, it requires computing maximum(A1) on 1:(X(fr)+1) (the largest world); -# - With R = Begins inverse, it requires computing minimum(A1) on (x,y+1), if such interval exists; -# - With R = During, it requires computing maximum(A1) on (x+1,y-1), if such interval exists; -# - With R = After, it requires reading the single value in (y,y+1) (or, alternatively, computing minimum(A1) on it), if such interval exists; +# Consider the decision ⟨R⟩ (minimum[V1] ≥ 10) evaluated on a world w = (x,y): +# - With R = identityrel, it requires computing minimum[V1] on w; +# - With R = globalrel, it requires computing maximum[V1] on 1:(X(fr)+1) (the largest world); +# - With R = Begins inverse, it requires computing minimum[V1] on (x,y+1), if such interval exists; +# - With R = During, it requires computing maximum[V1] on (x+1,y-1), if such interval exists; +# - With R = After, it requires reading the single value in (y,y+1) (or, alternatively, computing minimum[V1] on it), if such interval exists; # # Here is the categorization assuming feature = minimum and test_operator = ≥: # diff --git a/src/logisets/dimensional-structures/representatives/Full1DFrame+RCC.jl b/src/logisets/dimensional-structures/representatives/Full1DFrame+RCC.jl index cbf61be..dba70f7 100644 --- a/src/logisets/dimensional-structures/representatives/Full1DFrame+RCC.jl +++ b/src/logisets/dimensional-structures/representatives/Full1DFrame+RCC.jl @@ -3,17 +3,17 @@ #= -computeModalThresholdDual(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::DimensionalChannel{T,1}) where {T} = begin +computeModalThresholdDual(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::AbstractArray{T,1}) where {T} = begin maxExtrema( map((IA_r)->(yieldReprs(test_operator, enum_acc_repr(test_operator, w, IA_r, length(channel)), channel)), topo2IARelations(r)) ) end -apply_aggregator(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::DimensionalChannel{T,1}) where {T} = begin +apply_aggregator(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::AbstractArray{T,1}) where {T} = begin maximum( map((IA_r)->(yieldRepr(test_operator, enum_acc_repr(test_operator, w, IA_r, length(channel)), channel)), topo2IARelations(r)) ) end -apply_aggregator(test_operator::CanonicalFeatureLeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::DimensionalChannel{T,1}) where {T} = begin +apply_aggregator(test_operator::CanonicalFeatureLeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::AbstractArray{T,1}) where {T} = begin mininimum( map((IA_r)->(yieldRepr(test_operator, enum_acc_repr(test_operator, w, IA_r, length(channel)), channel)), topo2IARelations(r)) ) @@ -22,17 +22,17 @@ end enum_acc_repr(fr::Full1DFrame, test_operator::TestOperator, w::Interval{Int}?, ::_Topo_NTPP,) = enum_acc_repr(test_operator, fr, w, IA_D) enum_acc_repr(fr::Full1DFrame, test_operator::TestOperator, w::Interval{Int}?, ::_Topo_NTPPi,) = enum_acc_repr(test_operator, fr, w, IA_Di) -computeModalThresholdDual(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC5Relation, channel::DimensionalChannel{T,1}) where {T} = begin +computeModalThresholdDual(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC5Relation, channel::AbstractArray{T,1}) where {T} = begin maxExtrema( map((IA_r)->(yieldReprs(test_operator, enum_acc_repr(test_operator, w, IA_r, size(channel)...), channel)), [IA_r for RCC8_r in RCC52RCC8Relations(r) for IA_r in topo2IARelations(RCC8_r)]) ) end -apply_aggregator(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC5Relation, channel::DimensionalChannel{T,1}) where {T} = begin +apply_aggregator(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC5Relation, channel::AbstractArray{T,1}) where {T} = begin maximum( map((IA_r)->(yieldRepr(test_operator, enum_acc_repr(test_operator, w, IA_r, size(channel)...), channel)), [IA_r for RCC8_r in RCC52RCC8Relations(r) for IA_r in topo2IARelations(RCC8_r)]) ) end -apply_aggregator(test_operator::CanonicalFeatureLeq, w::Interval{Int}?, r::RCC5Relation, channel::DimensionalChannel{T,1}) where {T} = begin +apply_aggregator(test_operator::CanonicalFeatureLeq, w::Interval{Int}?, r::RCC5Relation, channel::AbstractArray{T,1}) where {T} = begin mininimum( map((IA_r)->(yieldRepr(test_operator, enum_acc_repr(test_operator, w, IA_r, size(channel)...), channel)), [IA_r for RCC8_r in RCC52RCC8Relations(r) for IA_r in topo2IARelations(RCC8_r)]) ) diff --git a/src/logisets/features.jl b/src/logisets/features.jl index 9f57716..894e5bb 100644 --- a/src/logisets/features.jl +++ b/src/logisets/features.jl @@ -62,20 +62,20 @@ end ############################################################################################ """ - struct ExternalFWDFeature <: AbstractFeature + struct ExplicitFeature{T} <: AbstractFeature name::String - fwd::Any + featstruct end -A feature encoded explicitly as (a slice of) an FWD structure (see `AbstractFeatureLookupSet`). +A feature encoded explicitly as a slice of feature structure (see `AbstractFeatureLookupSet`). See also [`AbstractFeatureLookupSet`](@ref), [`AbstractFeature`](@ref). """ -struct ExternalFWDFeature <: AbstractFeature +struct ExplicitFeature{T} <: AbstractFeature name::String - fwd::Any + featstruct::T end -syntaxstring(f::ExternalFWDFeature; kwargs...) = f.name +syntaxstring(f::ExplicitFeature; kwargs...) = f.name ############################################################################################ diff --git a/src/logisets/logiset.jl b/src/logisets/logiset.jl index 66b962b..2f3638d 100644 --- a/src/logisets/logiset.jl +++ b/src/logisets/logiset.jl @@ -298,7 +298,7 @@ function displaystructure( if include_ninstances out *= indent_str * "├ " * padattribute("# instances:", ninstances(X)) * "\n" end - out *= indent_str * "└ " * padattribute("# world density (countmap):", "$(countmap([nworlds(X, i_instance) for i_instance in 1:ninstances(X)]))") * "\n" + out *= indent_str * "└ " * padattribute("# world density (countmap):", "$(countmap([nworlds(X, i_instance) for i_instance in 1:ninstances(X)]))") out end @@ -431,7 +431,7 @@ function displaystructure( if include_ninstances out *= indent_str * "├ " * padattribute("# instances:", "$(ninstances(X))") * "\n" end - out *= indent_str * "└ " * padattribute("# world density (countmap):", "$(countmap([nworlds(X, i_instance) for i_instance in 1:ninstances(X)]))") * "\n" + out *= indent_str * "└ " * padattribute("# world density (countmap):", "$(countmap([nworlds(X, i_instance) for i_instance in 1:ninstances(X)]))") out end diff --git a/src/logisets/main.jl b/src/logisets/main.jl index bdf10da..6eb603c 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -33,7 +33,7 @@ include("memosets.jl") include("supported-logiset.jl") -export MultiLogiset, eachmodality, worldtypes, nmodalities +export MultiLogiset, eachmodality, modality, nmodalities # Multiframe version of logisets, for representing multimodal datasets include("multilogiset.jl") diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index e7848aa..846761b 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -54,7 +54,6 @@ nmodalities(X::MultiLogiset) = length(eachmodality(X)) ninstances(X::MultiLogiset) = ninstances(modality(X, 1)) worldtype(X::MultiLogiset, i_modality::Integer) = worldtype(modality(X, i_modality)) -worldtypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(worldtype.(eachmodality(X))) featvaltype(X::MultiLogiset, i_modality::Integer) = featvaltype(modality(X, i_modality)) featvaltypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(featvaltype.(eachmodality(X))) @@ -127,9 +126,13 @@ isminifiable(X::MultiLogiset) = any(isminifiable.(eachmodality(X))) function minify(X::MultiLogiset) if !any(map(isminifiable, eachmodality(X))) if !all(map(isminifiable, eachmodality(X))) - @error "Cannot perform minification with modalities of types $(typeof.(eachmodality(X))). Please use a minifiable format (e.g., SupportedScalarLogiset)." + error("Cannot perform minification with modalities " * + "of types $(typeof.(eachmodality(X))). Please use a " * + "minifiable format (e.g., SupportedLogiset).") else - @warn "Cannot perform minification on some of the modalities provided. Please use a minifiable format (e.g., SupportedScalarLogiset) ($(typeof.(eachmodality(X))) were used instead)." + @warn "Cannot perform minification on some of the modalities " * + "provided. Please use a minifiable format (e.g., " * + "SupportedLogiset) ($(typeof.(eachmodality(X))) were used instead)." end end X, backmap = zip([!isminifiable(X) ? minify(X) : (X, identity) for X in eachmodality(X)]...) diff --git a/src/logisets/scalar/conditions.jl b/src/logisets/scalar/conditions.jl index 9ee3cc6..f3da426 100644 --- a/src/logisets/scalar/conditions.jl +++ b/src/logisets/scalar/conditions.jl @@ -15,7 +15,7 @@ of an instance of a logical dataset. A test operator is a binary mathematical relation, comparing the computed feature value and an external threshold value (see `ScalarCondition`). A metacondition can also be used for representing the infinite set of conditions that arise with a free threshold -(see `UnboundedScalarConditions`): \${min(V1) ≥ a, a ∈ ℝ}\$. +(see `UnboundedScalarConditions`): \${min[V1] ≥ a, a ∈ ℝ}\$. See also [`AbstractCondition`](@ref), @@ -64,14 +64,18 @@ _st_featop_abbr(feature::AbstractFeature, test_operator::TestOperator; kwargs. ############################################################################################ -function groupbyfeature(metaconditions::AbstractVector{<:ScalarMetaCondition}) - _features = unique(feature.(metaconditions)) - grouped_features = [begin +function groupbyfeature( + metaconditions::AbstractVector{<:ScalarMetaCondition}, + features::Union{Nothing,AbstractVector{<:AbstractFeature}} = nothing, +) + if isnothing(features) + features = unique(feature.(metaconditions)) + end + return map(_feature->begin these_metaconds = filter(m->feature(m) == _feature, metaconditions) # these_testops = unique(test_operator.(these_metaconds)) (_feature, these_metaconds) - end for _feature in _features] - grouped_features + end, features) end ############################################################################################ @@ -86,7 +90,7 @@ A scalar condition comparing a computed feature value (see `ScalarMetaCondition` and a threshold value `a`. It can be evaluated on a world of an instance of a logical dataset. -For example: \$min(V1) ≥ 10\$, which translates to +For example: \$min[V1] ≥ 10\$, which translates to "Within this world, the minimum of variable 1 is greater or equal than 10." In this case, the feature a [`UnivariateMin`](@ref) object. @@ -139,8 +143,28 @@ function checkcondition(c::ScalarCondition, args...; kwargs...) apply_test_operator(test_operator(c), featvalue(feature(c), args...; kwargs...), threshold(c)) end -syntaxstring(m::ScalarCondition; threshold_decimals = nothing, kwargs...) = - "$(_syntaxstring_metacondition(metacond(m); kwargs...)) $((isnothing(threshold_decimals) ? threshold(m) : round(threshold(m); digits=threshold_decimals)))" +function syntaxstring( + m::ScalarCondition; + threshold_decimals::Union{Nothing,Integer} = nothing, + threshold_display_method::Union{Nothing,Base.Callable} = nothing, + kwargs... +) + if (!isnothing(threshold_decimals) && !isnothing(threshold_display_method)) + @warn "Prioritizing threshold_display_method parameter over threshold_decimals " * + "in syntaxstring for `ScalarCondition`." + end + threshold_display_method = begin + if !isnothing(threshold_display_method) + threshold_display_method + elseif !isnothing(threshold_decimals) + x->round(x; digits=threshold_decimals) + else + identity + end + end + string(_syntaxstring_metacondition(metacond(m); kwargs...)) * " " * + string(threshold_display_method(threshold(m))) +end function parsecondition( ::Type{C}, @@ -238,7 +262,7 @@ abstract type AbstractConditionalAlphabet{C<:ScalarCondition} <: AbstractAlphabe An infinite alphabet of conditions induced from a finite set of metaconditions. For example, if `metaconditions = [ScalarMetaCondition(UnivariateMin(1), ≥)]`, -the alphabet represents the (infinite) set: \${min(V1) ≥ a, a ∈ ℝ}\$. +the alphabet represents the (infinite) set: \${min[V1] ≥ a, a ∈ ℝ}\$. See also [`BoundedScalarConditions`](@ref), diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 6670a63..33cf1d8 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -166,9 +166,9 @@ function scalarlogiset( # Initialize the logiset structure X = initlogiset(dataset, features) - # Load external features (if any) - if any(isa.(features, ExternalFWDFeature)) - i_external_features = first.(filter(((i_feature,is_external_fwd),)->(is_external_fwd), collect(enumerate(isa.(features, ExternalFWDFeature))))) + # Load explicit features (if any) + if any(isa.(features, ExplicitFeature)) + i_external_features = first.(filter(((i_feature,isexplicit),)->(isexplicit), collect(enumerate(isa.(features, ExplicitFeature))))) for i_feature in i_external_features feature = features[i_feature] featvalues!(X, feature.X, i_feature) @@ -176,7 +176,7 @@ function scalarlogiset( end # Load internal features - i_features = first.(filter(((i_feature,is_external_fwd),)->!(is_external_fwd), collect(enumerate(isa.(features, ExternalFWDFeature))))) + i_features = first.(filter(((i_feature,isexplicit),)->!(isexplicit), collect(enumerate(isa.(features, ExplicitFeature))))) enum_features = zip(i_features, features[i_features]) _ninstances = ninstances(dataset) diff --git a/src/logisets/scalar/onestep-memoset.jl b/src/logisets/scalar/onestep-memoset.jl index 3942616..0fe0b26 100644 --- a/src/logisets/scalar/onestep-memoset.jl +++ b/src/logisets/scalar/onestep-memoset.jl @@ -244,6 +244,7 @@ struct ScalarOneStepMemoset{ relations :: AbstractVector{<:AbstractRelation}, # relational_memoset_type :: Type{<:AbstractScalarOneStepRelationalMemoset} = default_relmemoset_type(X); relational_memoset_type :: Type = default_relmemoset_type(X); + features = nothing, precompute_globmemoset :: Bool = true, precompute_relmemoset :: Bool = false, ) where {W<:AbstractWorld,U} @@ -284,21 +285,12 @@ struct ScalarOneStepMemoset{ nrelations = length(relations) nmetaconditions = length(metaconditions) - grouped_metaconditions = groupbyfeature(metaconditions) - - grouped_metaconditions = map(((feature,these_metaconditions),)->begin - these_metaconditions = map(_metacond->begin - i_metacond = findfirst(isequal(_metacond), metaconditions) - aggregator = existential_aggregator(test_operator(_metacond)) - (i_metacond, aggregator, _metacond) - end, these_metaconditions) - (feature,these_metaconditions) - end, grouped_metaconditions) + _grouped_metaconditions = grouped_metaconditions(metaconditions, features) # p = Progress(n_instances, 1, "Computing EMD supports...") Threads.@threads for i_instance in 1:n_instances - for (_feature, these_metaconditions) in grouped_metaconditions + for (_feature, these_metaconditions) in _grouped_metaconditions _featchannel = featchannel(X, i_instance, _feature) @@ -346,6 +338,23 @@ globmemoset(Xm::ScalarOneStepMemoset) = Xm.globmemoset ninstances(Xm::ScalarOneStepMemoset) = ninstances(relmemoset(Xm)) +function grouped_metaconditions( + metaconditions::AbstractVector{<:ScalarMetaCondition}, + features::Union{Nothing,AbstractVector{<:AbstractFeature}} = nothing, +) + if isnothing(features) + features = unique(feature.(metaconditions)) + end + return map(((feature,these_metaconditions),)->begin + these_metaconditions = map(_metacond->begin + i_metacond = findfirst(isequal(_metacond), metaconditions) + aggregator = existential_aggregator(test_operator(_metacond)) + (i_metacond, aggregator, _metacond) + end, these_metaconditions) + (feature,these_metaconditions) + end, groupbyfeature(metaconditions, features)) +end + function featchannel_onestep_aggregation( X::AbstractLogiset{W,U}, Xm::ScalarOneStepMemoset, @@ -657,8 +666,8 @@ function displaystructure( return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") end -# @inline function Base.setindex!(Xm::ScalarOneStepRelationalMemoset{W,U}, threshold::U, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {W,U} -# Xm.d[i_instance, i_featsnaggr, i_relation][w] = threshold +# @inline function Base.setindex!(Xm::ScalarOneStepRelationalMemoset{W,U}, threshold::U, i_instance::Integer, w::AbstractWorld, i_metacond::Integer, i_relation::Integer) where {W,U} +# Xm.d[i_instance, i_metacond, i_relation][w] = threshold # end ############################################################################################ diff --git a/src/logisets/scalar/templated-formulas.jl b/src/logisets/scalar/templated-formulas.jl index 8d54b4b..eea9451 100644 --- a/src/logisets/scalar/templated-formulas.jl +++ b/src/logisets/scalar/templated-formulas.jl @@ -14,7 +14,7 @@ struct ScalarPropositionFormula{U} <: ScalarFormula{U} p :: ScalarCondition{U} end -proposition(d::ScalarPropositionFormula) = d.p +proposition(d::ScalarPropositionFormula) = Proposition(d.p) feature(d::ScalarPropositionFormula) = feature(proposition(d)) test_operator(d::ScalarPropositionFormula) = test_operator(proposition(d)) threshold(d::ScalarPropositionFormula) = threshold(proposition(d)) @@ -28,7 +28,7 @@ negation(d::ScalarPropositionFormula{U}) where {U} = abstract type ScalarOneStepFormula{U} <: ScalarFormula{U} end relation(d::ScalarOneStepFormula) = d.relation -proposition(d::ScalarOneStepFormula) = d.p +proposition(d::ScalarOneStepFormula) = Proposition(d.p) metacond(d::ScalarOneStepFormula) = metacond(proposition(d)) feature(d::ScalarOneStepFormula) = feature(proposition(d)) test_operator(d::ScalarOneStepFormula) = test_operator(proposition(d)) @@ -90,7 +90,7 @@ struct ScalarExistentialFormula{U} <: ScalarOneStepFormula{U} end end -tree(d::ScalarExistentialFormula) = DiamondRelationalOperator(d.relation)(d.p) +tree(d::ScalarExistentialFormula) = DiamondRelationalOperator(d.relation)(Proposition(d.p)) """ Templated formula for [R] f ⋈ t. @@ -100,7 +100,7 @@ struct ScalarUniversalFormula{U} <: ScalarOneStepFormula{U} p :: ScalarCondition{U} end -tree(d::ScalarUniversalFormula) = BoxRelationalOperator(d.relation)(d.p) +tree(d::ScalarUniversalFormula) = BoxRelationalOperator(d.relation)(Proposition(d.p)) function negation(formula::ScalarExistentialFormula{U}) where {U} ScalarUniversalFormula{U}( diff --git a/src/logisets/scalar/test-operators.jl b/src/logisets/scalar/test-operators.jl index ff22dd8..a94c3a9 100644 --- a/src/logisets/scalar/test-operators.jl +++ b/src/logisets/scalar/test-operators.jl @@ -5,6 +5,10 @@ A test operator is a binary Julia `Function` used for comparing a feature value and a threshold. In a crisp (i.e., boolean, non-fuzzy) setting, the test operator returns a boolean value, and `<`, `>`, `≥`, `≤`, `!=`, and `==` are typically used. + +See also +[`Aggregator`](@ref), +[`ScalarCondition`](@ref). """ const TestOperator = Function @@ -20,6 +24,18 @@ the (binary) test operator function. operator(featval, threshold) end +""" + const Aggregator = Function + +A test operator is a binary Julia `Function` used for comparing a feature value and +a threshold. In a crisp (i.e., boolean, non-fuzzy) setting, the test operator returns +a boolean value, and `<`, `>`, `≥`, `≤`, `!=`, and `==` are typically used. + +See also +[`ScalarCondition`](@ref), +[`ScalarOneStepMemoset`](@ref), +[`TestOperator`](@ref). +""" const Aggregator = Function ############################################################################################ diff --git a/src/logisets/scalar/var-features.jl b/src/logisets/scalar/var-features.jl index dab7a55..4e03844 100644 --- a/src/logisets/scalar/var-features.jl +++ b/src/logisets/scalar/var-features.jl @@ -15,8 +15,22 @@ const UVF_VARPREFIX = "V" abstract type VarFeature{U} <: AbstractFeature end Abstract type for feature functions that can be computed on (multi)variate data. +Instances of multivariate datasets have values for a number of *variables*, +which can be used to define logical features. -See also [`featvaltype`](@ref), [`computefeature`](@ref). +For example, with dimensional data (e.g., multivariate time series, digital images +and videos), features can be computed as the minimum value for a given variable +on a specific interval/rectangle/cuboid (in general, a [`GeometricalWorld`](@ref)[`GeometricalWorld`](@ref)). + +As an example of a dimensional feature, consider *min[V1]*, +which computes the minimum for variable 1 for a given world. +`ScalarCondition`s such as *min[V1] >= 10* can be, then, evaluated on worlds. + +See also +[`scalarlogiset`](@ref), +[`featvaltype`](@ref), +[`computefeature`](@ref), +[`Interval`](@ref). """ abstract type VarFeature{U} <: AbstractFeature end diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index f0cae67..4899c5a 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -163,7 +163,7 @@ struct SupportedLogiset{ # TODO Helper # function SupportedLogiset( - # X :: ... AbstractActiveScalarLogiset{W,V,FT,Bool,FR}; + # X :: ... AbstractScalarLogiset{W,V,FT,FR}; # kwargs..., # ) where {V,FT<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} # SupportedLogiset(Logiset(X); kwargs...) diff --git a/src/machine-learning.jl b/src/machine-learning.jl index f1d2e3e..5f66572 100644 --- a/src/machine-learning.jl +++ b/src/machine-learning.jl @@ -90,10 +90,10 @@ function bestguess( end if !suppress_parity_warning && sum(counts[argmax(counts)] .== values(counts)) > 1 - println("Warning: parity encountered in bestguess.") - println("Counts ($(length(labels)) elements): $(counts)") - println("Argmax: $(argmax(counts))") - println("Max: $(counts[argmax(counts)]) (sum = $(sum(values(counts))))") + @warn "Parity encountered in bestguess! " * + "Counts ($(length(labels)) elements): $(counts)" * + "Argmax: $(argmax(counts))" * + "Max: $(counts[argmax(counts)]) (sum = $(sum(values(counts))))" end argmax(counts) end diff --git a/src/models/base.jl b/src/models/base.jl index f6899c7..5804a92 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -314,31 +314,33 @@ info(m::AbstractModel)::NamedTuple = m.info ############################################################################################ """ - abstract type FinalModel{O} <: AbstractModel{O} end + abstract type LeafModel{O} <: AbstractModel{O} end -A `FinalModel` is a model which outcomes do not depend on another model. -An `AbstractModel` can generally wrap other `AbstractModel`s. In such case, the outcome can +Abstract type for leaf models, that is, models which outcomes do not depend +other models, and represents the bottom of the computation. +In general, an `AbstractModel` can generally wrap other `AbstractModel`s; +in such case, the outcome can depend on the inner models being applied on the instance object. Otherwise, the model is -considered final; that is, it is a leaf of a tree of `AbstractModel`s. +considered as a *leaf*, or *final*, and is the *leaf* of a tree of `AbstractModel`s. See also [`ConstantModel`](@ref), [`FunctionModel`](@ref), [`AbstractModel`](@ref). """ -abstract type FinalModel{O} <: AbstractModel{O} end +abstract type LeafModel{O} <: AbstractModel{O} end """ - struct ConstantModel{O} <: FinalModel{O} + struct ConstantModel{O} <: LeafModel{O} outcome::O info::NamedTuple end The simplest type of model is the `ConstantModel`; -it is a `FinalModel` that always outputs the same outcome. +it is a `LeafModel` that always outputs the same outcome. # Examples ```julia-repl -julia> SoleModels.FinalModel(2) isa SoleModels.ConstantModel +julia> SoleModels.LeafModel(2) isa SoleModels.ConstantModel -julia> SoleModels.FinalModel(sum) isa SoleModels.FunctionModel +julia> SoleModels.LeafModel(sum) isa SoleModels.FunctionModel ┌ Warning: Over efficiency concerns, please consider wrappingJulia Function's into FunctionWrapper{O,Tuple{SoleModels.AbstractInterpretation}} structures,where O is their return type. └ @ SoleModels ~/.julia/dev/SoleModels/src/models/base.jl:337 true @@ -348,9 +350,9 @@ true See also [`apply`](@ref), [`FunctionModel`](@ref), -[`FinalModel`](@ref). +[`LeafModel`](@ref). """ -struct ConstantModel{O} <: FinalModel{O} +struct ConstantModel{O} <: LeafModel{O} outcome::O info::NamedTuple @@ -387,21 +389,21 @@ convert(::Type{ConstantModel{O}}, o::O) where {O} = ConstantModel{O}(o) convert(::Type{<:AbstractModel{F}}, m::ConstantModel) where {F} = ConstantModel{F}(m) """ - struct FunctionModel{O} <: FinalModel{O} + struct FunctionModel{O} <: LeafModel{O} f::FunctionWrapper{O} info::NamedTuple end -A `FunctionModel` is a `FinalModel` that applies a native Julia `Function` +A `FunctionModel` is a `LeafModel` that applies a native Julia `Function` in order to compute the outcome. Over efficiency concerns, it is mandatory to make explicit the output type `O` by wrapping the `Function` into an object of type `FunctionWrapper{O}`. TODO @Michele explain functional_args/functional_kwargs -See also [`ConstantModel`](@ref), [`FunctionWrapper`](@ref), [`FinalModel`](@ref). +See also [`ConstantModel`](@ref), [`FunctionWrapper`](@ref), [`LeafModel`](@ref). """ -struct FunctionModel{O} <: FinalModel{O} +struct FunctionModel{O} <: LeafModel{O} f::FunctionWrapper{O} # isopen::Bool TODO info::NamedTuple @@ -491,7 +493,7 @@ simply returned (no wrapping is performed); See also [`ConstantModel`](@ref), [`FunctionModel`](@ref), -[`ConstrainedModel`](@ref), [`FinalModel`](@ref). +[`ConstrainedModel`](@ref), [`LeafModel`](@ref). """ wrap(o::Any, FM::Type{<:AbstractModel}) = convert(FM, wrap(o)) wrap(m::AbstractModel) = m @@ -500,7 +502,7 @@ wrap(o::FunctionWrapper{O}) where {O} = FunctionModel{O}(o) wrap(o::O) where {O} = convert(ConstantModel{O}, o) # Helper -FinalModel(o) = wrap(o) +LeafModel(o) = wrap(o) ############################################################################################ ############################################################################################ @@ -509,7 +511,7 @@ FinalModel(o) = wrap(o) """ An `AbstractModel` can wrap another `AbstractModel`, and use it to compute the outcome. As such, an `AbstractModel` can actually be the result of a composition of many models, -and enclose a *tree* of `AbstractModel`s (with `FinalModel`s at the leaves). +and enclose a *tree* of `AbstractModel`s (with `LeafModel`s at the leaves). In order to typebound the Feasible Models (`FM`) allowed in the sub-tree, the `ConstrainedModel` type is introduced: @@ -519,7 +521,7 @@ For example, `ConstrainedModel{String, Union{Branch{String}, ConstantModel{Strin supertypes models that with `String` outcomes that make use of `Branch{String}` and `ConstantModel{String}` (essentially, a decision trees with `String`s at the leaves). -See also [`FinalModel`](@ref), [`AbstractModel`](@ref). +See also [`LeafModel`](@ref), [`AbstractModel`](@ref). """ abstract type ConstrainedModel{O,FM<:AbstractModel} <: AbstractModel{O} end @@ -534,8 +536,8 @@ See also [`ConstrainedModel`](@ref). """ feasiblemodelstype(::Type{M}) where {O, M<:AbstractModel{O}} = AbstractModel{<:O} feasiblemodelstype(::Type{M}) where {M<:AbstractModel} = AbstractModel -feasiblemodelstype(::Type{M}) where {O, M<:FinalModel{O}} = Union{} -feasiblemodelstype(::Type{M}) where {M<:FinalModel} = Union{} +feasiblemodelstype(::Type{M}) where {O, M<:LeafModel{O}} = Union{} +feasiblemodelstype(::Type{M}) where {M<:LeafModel} = Union{} feasiblemodelstype(::Type{<:ConstrainedModel{O,FM}}) where {O,FM} = FM feasiblemodelstype(m::ConstrainedModel) = outcometype(typeof(m)) @@ -575,7 +577,7 @@ function check_model_constraints( # @assert I_M <: FM || typename(I_M) <: typename(FM) "Cannot instantiate $(M) with inner model $(I_M))! $(I_M) <: $(FM) || $(typename(I_M)) <: $(typename(FM)) should hold." @assert I_M <: FM "Cannot instantiate $(M) with inner model $(I_M))! " * "$(I_M) <: $(FM) should hold." - if ! (I_M<:FinalModel{<:FM_O}) + if ! (I_M<:LeafModel{<:FM_O}) # @assert I_M<:ConstrainedModel{FM_O,<:FM} "ConstrainedModels require I_M<:ConstrainedModel{O,<:FM}, but $(I_M) does not subtype $(ConstrainedModel{FM_O,<:FM})." @assert I_M<:ConstrainedModel{<:FM_O,<:FM} "ConstrainedModels require " * "I_M<:ConstrainedModel{<:O,<:FM}, but $(I_M) does not " * @@ -1212,12 +1214,12 @@ where the antecedents are conditions to be tested and the consequents are the fe local outcomes of the block. In practice, a `DecisionTree` simply wraps a constrained -sub-tree of `Branch` and `FinalModel`: +sub-tree of `Branch` and `LeafModel`: struct DecisionTree{ O, C<:AbstractBooleanCondition, - FFM<:FinalModel + FFM<:LeafModel } <: ConstrainedModel{O, Union{<:Branch{<:O,<:C}, <:FFM}} root::M where {M<:Union{FFM,Branch}} info::NamedTuple @@ -1232,7 +1234,7 @@ See also [`ConstrainedModel`](@ref), [`MixedSymbolicModel`](@ref), [`DecisionLis struct DecisionTree{ O, C<:AbstractBooleanCondition, - FFM<:FinalModel + FFM<:LeafModel } <: ConstrainedModel{O, Union{<:Branch{<:O,<:C}, <:FFM}} root::M where {M<:Union{FFM,Branch}} info::NamedTuple @@ -1240,7 +1242,7 @@ struct DecisionTree{ function DecisionTree( root::Union{FFM,Branch{O,C,Union{Branch{<:O,C2},FFM}}}, info::NamedTuple = (;), - ) where {O, C<:AbstractBooleanCondition, C2<:C, FFM<:FinalModel{<:O}} + ) where {O, C<:AbstractBooleanCondition, C2<:C, FFM<:LeafModel{<:O}} new{O,C,FFM}(root, info) end @@ -1251,15 +1253,15 @@ struct DecisionTree{ root = wrap(root) M = typeof(root) O = outcometype(root) - C = (root isa FinalModel ? AbstractBooleanCondition : conditiontype(M)) + C = (root isa LeafModel ? AbstractBooleanCondition : conditiontype(M)) # FM = typeintersect(Union{M,feasiblemodelstype(M)}, AbstractModel{<:O}) FM = typeintersect(Union{propagate_feasiblemodels(M)}, AbstractModel{<:O}) - FFM = typeintersect(FM, FinalModel{<:O}) + FFM = typeintersect(FM, LeafModel{<:O}) @assert M <: Union{<:FFM,<:Branch{<:O,<:C,<:Union{Branch,FFM}}} "" * "Cannot instantiate DecisionTree{$(O),$(C),$(FFM)}(...) with root of " * - "type $(typeof(root)). Note that the should be either a FinalNode or a " * + "type $(typeof(root)). Note that the should be either a LeafModel or a " * "bounded Branch. " * - "$(M) <: $(Union{FinalModel,Branch{<:O,<:C,<:Union{Branch,FFM}}}) should hold." + "$(M) <: $(Union{LeafModel,Branch{<:O,<:C,<:Union{Branch,FFM}}}) should hold." check_model_constraints(DecisionTree{O}, typeof(root), FM, O) new{O,C,FFM}(root, info) end @@ -1293,6 +1295,18 @@ function apply( apply(root(m), d; kwargs...) end +function nnodes(t::DecisionTree) + nsubmodels(t) +end + +function nleaves(t::DecisionTree) + nleafmodels(t) +end + +function height(t::DecisionTree) + submodelsheight(t) +end + ############################################################################################ """ @@ -1301,7 +1315,7 @@ A `Decision Forest` is a symbolic model that wraps an ensemble of models struct DecisionForest{ O, C<:AbstractBooleanCondition, - FFM<:FinalModel + FFM<:LeafModel } <: ConstrainedModel{O, Union{<:Branch{<:O,<:C}, <:FFM}} trees::Vector{<:DecisionTree} info::NamedTuple @@ -1314,7 +1328,7 @@ See also [`ConstrainedModel`](@ref), [`MixedSymbolicModel`](@ref), [`DecisionLis struct DecisionForest{ O, C<:AbstractBooleanCondition, - FFM<:FinalModel + FFM<:LeafModel } <: ConstrainedModel{O, Union{<:Branch{<:O,<:C}, <:FFM}} trees::Vector{<:DecisionTree} info::NamedTuple @@ -1327,7 +1341,7 @@ struct DecisionForest{ O = Union{outcometype.(trees)...} C = Union{conditiontype.(trees)...} FM = typeintersect(Union{propagate_feasiblemodels.(trees)...}, AbstractModel{<:O}) - FFM = typeintersect(FM, FinalModel{<:O}) + FFM = typeintersect(FM, LeafModel{<:O}) check_model_constraints.(DecisionForest{O}, typeof.(trees), FM, O) new{O,C,FFM}(trees, info) end @@ -1358,6 +1372,18 @@ function apply( return [bestguess(pred[i,:]) for i in 1:size(pred,1)] end +function nnodes(f::DecisionForest) + nsubmodels(f) +end + +function nleaves(f::DecisionForest) + nleafmodels(f) +end + +function height(f::DecisionForest) + submodelsheight(f) +end + ############################################################################################ """ @@ -1384,7 +1410,7 @@ In Sole.jl, this logic can implemented using `ConstrainedModel`s such as a `MixedSymbolicModel`: struct MixedSymbolicModel{O,FM<:AbstractModel} <: ConstrainedModel{O,FM} - root::M where {M<:Union{FinalModel{<:O},ConstrainedModel{<:O,<:FM}}} + root::M where {M<:Union{LeafModel{<:O},ConstrainedModel{<:O,<:FM}}} info::NamedTuple end @@ -1393,7 +1419,7 @@ Note that `FM` refers to the Feasible Models (`FM`) allowed in the model's sub-t See also [`ConstrainedModel`](@ref), [`DecisionTree`](@ref), [`DecisionList`](@ref). """ struct MixedSymbolicModel{O,FM<:AbstractModel} <: ConstrainedModel{O,FM} - root::M where {M<:Union{FinalModel{<:O},ConstrainedModel{<:O,<:FM}}} + root::M where {M<:Union{LeafModel{<:O},ConstrainedModel{<:O,<:FM}}} info::NamedTuple function MixedSymbolicModel( diff --git a/src/models/rule-evaluation.jl b/src/models/rule-evaluation.jl index aeffb67..edb93fb 100644 --- a/src/models/rule-evaluation.jl +++ b/src/models/rule-evaluation.jl @@ -1,5 +1,5 @@ using SoleModels -using SoleModels: FinalModel +using SoleModels: LeafModel import SoleLogics: npropositions """ diff --git a/src/models/symbolic-utils.jl b/src/models/symbolic-utils.jl index f80b6f8..fa632ae 100644 --- a/src/models/symbolic-utils.jl +++ b/src/models/symbolic-utils.jl @@ -7,7 +7,7 @@ immediatesubmodels(m::AbstractModel) Return the list of immediate child models. -Note: if the model is final, then the returned list will be empty. +Note: if the model is a leaf model, then the returned list will be empty. # Examples ```julia-repl @@ -38,7 +38,7 @@ ConstantModel See also [`submodels`](@ref), -[`FinalModel`](@ref), +[`LeafModel`](@ref), [`AbstractModel`](@ref). """ function immediatesubmodels( @@ -47,13 +47,22 @@ function immediatesubmodels( error("Please, provide method immediatesubmodels(::$(typeof(m))).") end -immediatesubmodels(m::FinalModel{O}) where {O} = Vector{<:AbstractModel{<:O}}[] +immediatesubmodels(m::LeafModel{O}) where {O} = Vector{<:AbstractModel{<:O}}[] immediatesubmodels(m::Rule) = [consequent(m)] immediatesubmodels(m::Branch) = [posconsequent(m), negconsequent(m)] immediatesubmodels(m::DecisionList) = [rulebase(m)..., defaultconsequent(m)] immediatesubmodels(m::DecisionTree) = immediatesubmodels(root(m)) +immediatesubmodels(m::DecisionForest) = trees(m) immediatesubmodels(m::MixedSymbolicModel) = immediatesubmodels(root(m)) +nimmediatesubmodels(m::LeafModel) = 0 +nimmediatesubmodels(m::Rule) = 1 +nimmediatesubmodels(m::Branch) = 2 +nimmediatesubmodels(m::DecisionList) = length(rulebase(m)) + 1 +nimmediatesubmodels(m::DecisionTree) = nimmediatesubmodels(root(m)) +nimmediatesubmodels(m::DecisionForest) = length(trees(m)) +nimmediatesubmodels(m::MixedSymbolicModel) = nimmediatesubmodels(root(m)) + """ submodels(m::AbstractModel) @@ -103,12 +112,31 @@ false See also [`immediatesubmodels`](@ref), -[`FinalModel`](@ref), +[`LeafModel`](@ref), [`AbstractModel`](@ref). """ submodels(m::AbstractModel) = [Iterators.flatten(_submodels.(immediatesubmodels(m)))...] _submodels(m::AbstractModel) = [m, Iterators.flatten(_submodels.(immediatesubmodels(m)))...] +_submodels(m::DecisionList) = [Iterators.flatten(_submodels.(immediatesubmodels(m)))...] +_submodels(m::DecisionTree) = [Iterators.flatten(_submodels.(immediatesubmodels(m)))...] +_submodels(m::DecisionForest) = [Iterators.flatten(_submodels.(immediatesubmodels(m)))...] +_submodels(m::MixedSymbolicModel) = [Iterators.flatten(_submodels.(immediatesubmodels(m)))...] + +nsubmodels(m::AbstractModel) = 1 + sum(nsubmodels, immediatesubmodels(m)) +nsubmodels(m::DecisionList) = sum(nsubmodels, immediatesubmodels(m)) +nsubmodels(m::DecisionTree) = sum(nsubmodels, immediatesubmodels(m)) +nsubmodels(m::DecisionForest) = sum(nsubmodels, immediatesubmodels(m)) +nsubmodels(m::MixedSymbolicModel) = sum(nsubmodels, immediatesubmodels(m)) + +leafmodels(m::AbstractModel) = [Iterators.flatten(leafmodels.(immediatesubmodels(m)))...] + +nleafmodels(m::AbstractModel) = sum(nleafmodels, immediatesubmodels(m)) +submodelsheight(m::AbstractModel) = 1 + maximum(submodelsheight, immediatesubmodels(m)) +submodelsheight(m::DecisionList) = maximum(submodelsheight, immediatesubmodels(m)) +submodelsheight(m::DecisionTree) = maximum(submodelsheight, immediatesubmodels(m)) +submodelsheight(m::DecisionForest) = maximum(submodelsheight, immediatesubmodels(m)) +submodelsheight(m::MixedSymbolicModel) = maximum(submodelsheight, immediatesubmodels(m)) ############################################################################################ ############################################################################################ @@ -141,7 +169,7 @@ listimmediaterules(m::AbstractModel{O} where {O})::Rule{<:O} = end end) -listimmediaterules(m::FinalModel) = [Rule(TrueCondition, m)] +listimmediaterules(m::LeafModel) = [Rule(TrueCondition, m)] listimmediaterules(m::Rule) = [m] @@ -235,7 +263,7 @@ julia> print(join(displaymodel.(listrules(mixed_symbolic_model); header = false) └ ✔ 1.5 ``` -See also [`listimmediaterules`](@ref), [`issymbolic`](@ref), [`FinalModel`](@ref), +See also [`listimmediaterules`](@ref), [`issymbolic`](@ref), [`LeafModel`](@ref), [`AbstractModel`](@ref). """ function listrules(m::AbstractModel; kwargs...) @@ -248,7 +276,7 @@ function listrules(m::AbstractModel; kwargs...) end) end -listrules(m::FinalModel; kwargs...) = [m] +listrules(m::LeafModel; kwargs...) = [m] function listrules( m::Rule{O,<:TrueCondition}; @@ -276,12 +304,12 @@ function listrules( ) where {O} pos_rules = begin submodels = listrules(posconsequent(m); kwargs...) - submodels isa Vector{<:FinalModel} ? [Rule{O,TrueCondition}(fm) for fm in submodels] : submodels + submodels isa Vector{<:LeafModel} ? [Rule{O,TrueCondition}(fm) for fm in submodels] : submodels end neg_rules = begin submodels = listrules(negconsequent(m); kwargs...) - submodels isa Vector{<:FinalModel} ? [Rule{O,TrueCondition}(fm) for fm in submodels] : submodels + submodels isa Vector{<:LeafModel} ? [Rule{O,TrueCondition}(fm) for fm in submodels] : submodels end return [ @@ -300,7 +328,7 @@ function listrules( ant = tree(formula(m)) map(subm-> begin - if subm isa FinalModel + if subm isa LeafModel Rule(LogicalTruthCondition(ant), subm) else f = formula(subm) @@ -321,7 +349,7 @@ function listrules( ant = ¬(tree(formula(m))) map(subm-> begin - if subm isa FinalModel + if subm isa LeafModel Rule(LogicalTruthCondition(ant), subm) else f = formula(subm) diff --git a/src/other/active-logiset.jl b/src/other/active-logiset.jl index 13a02b0..848d44a 100644 --- a/src/other/active-logiset.jl +++ b/src/other/active-logiset.jl @@ -110,7 +110,7 @@ end # # A function for setting threshold values for a single feature (from a feature slice, experimental) # Base.@propagate_inbounds @inline function fwd_set_feature(featstruct::GenericFWD{V}, i_feature::Integer, fwdslice::Any) where {V} -# throw_n_log("Warning! fwd_set_feature with GenericFWD is not yet implemented!") +# error("Warning! fwd_set_feature with GenericFWD is not yet implemented!") # for ((i_instance,w),threshold::V) in read_fwdslice(fwdslice) # featstruct.d[i_instance][w][i_feature] = threshold # end diff --git a/src/other/dimensional-logiset.jl b/src/other/dimensional-logiset.jl index d3c0070..9e63b5d 100644 --- a/src/other/dimensional-logiset.jl +++ b/src/other/dimensional-logiset.jl @@ -14,10 +14,10 @@ struct DimensionalLogiset{ N, W<:AbstractWorld, D<:PassiveDimensionalLogiset{N,W}, - FT<:AbstractFeature{V}, + FT<:AbstractFeature, G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, -} <: AbstractActiveScalarLogiset{W,V,FT,Bool,FullDimensionalFrame{N,W}} +} <: AbstractScalarLogiset{W,V,FT,FullDimensionalFrame{N,W}} # Core data (a dimensional domain) domain :: D @@ -152,7 +152,7 @@ struct DimensionalLogiset{ single_var_feats_n_featsnops(i_var,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},typeof(minimum)}) = (test_ops,DimensionalDatasets.UnivariateMin{V}(i_var)) single_var_feats_n_featsnops(i_var,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},typeof(maximum)}) = (test_ops,DimensionalDatasets.UnivariateMax{V}(i_var)) single_var_feats_n_featsnops(i_var,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},Function}) = (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_var, (x)->(V(cf(x))))) - single_var_feats_n_featsnops(i_var,::Any) = throw_n_log("Unknown mixed_feature type: $(cf), $(typeof(cf))") + single_var_feats_n_featsnops(i_var,::Any) = error("Unknown mixed_feature type: $(cf), $(typeof(cf))") for i_var in 1:nvariables(domain) for (test_ops,cf) in map((cf)->single_var_feats_n_featsnops(i_var,cf),variable_specific_cfs) diff --git a/src/other/dimensional-ontologies.jl b/src/other/dimensional-ontologies.jl index abc2bbe..4e721fb 100644 --- a/src/other/dimensional-ontologies.jl +++ b/src/other/dimensional-ontologies.jl @@ -65,13 +65,13 @@ get_interval_ontology(N::Val, relations::Union{Symbol,AbstractVector{<:AbstractR # Any world type W must provide an `interpret_world` method for interpreting a world # onto a modal instance: # interpret_world(::W, modal_instance) -# Note: for dimensional world types: modal_instance::DimensionalInstance +# Note: for dimensional world types: modal_instance::AbstractArray ############################################################################################ # Dimensionality: 0 # Dimensional world type: it can be interpreted on dimensional instances. -interpret_world(::OneWorld, instance::DimensionalInstance{T,1}) where {T} = instance +interpret_world(::OneWorld, instance::AbstractArray{T,1}) where {T} = instance const OneWorldOntology = Ontology{OneWorld}(AbstractRelation[]) @@ -79,7 +79,7 @@ const OneWorldOntology = Ontology{OneWorld}(AbstractRelation[]) # Dimensionality: 1 # Dimensional world type: it can be interpreted on dimensional instances. -interpret_world(w::Interval2D, instance::DimensionalInstance{T,3}) where {T} = instance[w.x.x:w.x.y-1,w.y.x:w.y.y-1,:] +interpret_world(w::Interval2D, instance::AbstractArray{T,3}) where {T} = instance[w.x.x:w.x.y-1,w.y.x:w.y.y-1,:] const IntervalOntology = Ontology{Interval{Int}}(IARelations) const Interval3Ontology = Ontology{Interval}(SoleLogics.IA3Relations) @@ -92,7 +92,7 @@ const IntervalRCC5Ontology = Ontology{Interval{Int}}(RCC5Relations) # Dimensionality: 2 # Dimensional world type: it can be interpreted on dimensional instances. -interpret_world(w::Interval, instance::DimensionalInstance{T,2}) where {T} = instance[w.x:w.y-1,:] +interpret_world(w::Interval, instance::AbstractArray{T,2}) where {T} = instance[w.x:w.y-1,:] const Interval2DOntology = Ontology{Interval2D{Int}}(IA2DRelations) const Interval2DRCC8Ontology = Ontology{Interval2D{Int}}(RCC8Relations) diff --git a/src/other/passive-dimensional-logiset.jl b/src/other/passive-dimensional-logiset.jl index 4849740..5e74a91 100644 --- a/src/other/passive-dimensional-logiset.jl +++ b/src/other/passive-dimensional-logiset.jl @@ -1,9 +1,7 @@ using SoleData: slicedataset import SoleData: get_instance, ninstances, nvariables, channelsize, maxchannelsize, dimensionality, eltype using SoleData: AbstractDimensionalDataset, - UniformDimensionalDataset, - DimensionalInstance, - DimensionalChannel + UniformDimensionalDataset """ Scalar logiset with of dimensionality `N`. diff --git a/src/other/scalar-logiset.jl b/src/other/scalar-logiset.jl index 6821c3b..fbca24f 100644 --- a/src/other/scalar-logiset.jl +++ b/src/other/scalar-logiset.jl @@ -2,13 +2,12 @@ """ Active scalar datasets are active logical datasets with scalar features. """ -const AbstractActiveScalarLogiset{ +const AbstractScalarLogiset{ W<:AbstractWorld, V<:Number, FT<:AbstractFeature{V}, - T<:TruthValue, - FR<:AbstractFrame{W,T} -} = AbstractLogiset{W,V,FT,T,FR} + FR<:AbstractFrame{W} +} = AbstractLogiset{W,V,FT,FR} function grouped_featsaggrsnops(X::AbstractScalarLogiset) return error("Please, provide method grouped_featsaggrsnops(::$(typeof(X))).") diff --git a/src/utils.jl b/src/utils.jl index 8de146e..5302931 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -70,44 +70,3 @@ function displaysyntaxvector(a, maxnum = 8) end end - -# https://discourse.julialang.org/t/groupby-function/9896 - -""" - group items of list l according to the corresponding values in list v - - julia> _groupby([31,28,31,30,31,30,31,31,30,31,30,31], - [:Jan,:Feb,:Mar,:Apr,:May,:Jun,:Jul,:Aug,:Sep,:Oct,:Nov,:Dec]) - Dict{Int64,Array{Symbol,1}} with 3 entries: - 31 => Symbol[:Jan, :Mar, :May, :Jul, :Aug, :Oct, :Dec] - 28 => Symbol[:Feb] - 30 => Symbol[:Apr, :Jun, :Sep, :Nov] - -""" -function _groupby(v::AbstractVector, l::AbstractVector) - @assert length(v) == length(l) "$(@show v, l)" - res = Dict{eltype(v),Vector{eltype(l)}}() - for (k, val) in zip(v, l) - push!(get!(res, k, similar(l, 0)), val) - end - res -end - -""" - group items of list l according to the values taken by function f on them - - julia> _groupby(iseven,1:10) - Dict{Bool,Array{Int64,1}} with 2 entries: - false => [1, 3, 5, 7, 9] - true => [2, 4, 6, 8, 10] - -Note:in this version l is required to be non-empty since I do not know how to -access the return type of a function -""" -function _groupby(f,l::AbstractVector) - res = Dict(f(l[1]) => [l[1]]) # l should be nonempty - for val in l[2:end] - push!(get!(res, f(val), similar(l, 0)), val) - end - res -end diff --git a/test/base.jl b/test/base.jl index bf48299..4723de6 100644 --- a/test/base.jl +++ b/test/base.jl @@ -2,7 +2,7 @@ using SoleModels using SoleLogics using FunctionWrappers: FunctionWrapper using SoleModels: AbstractModel -using SoleModels: ConstantModel, FinalModel +using SoleModels: ConstantModel, LeafModel using SoleModels: ConstrainedModel, check_model_constraints using Test @@ -19,7 +19,7 @@ formula_q = SoleLogics.parseformula("q") formula_r = SoleLogics.parseformula("r") formula_s = SoleLogics.parseformula("s") -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Final models ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Leaf models ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @test_nowarn ConstantModel(1,(;)) @test_nowarn ConstantModel(1) @@ -78,7 +78,7 @@ rmodel_float = @test_nowarn Rule{Float64}(phi,rmodel_float0) rmodel_float2 = @test_nowarn Rule{Float64}(phi,rmodel_float) @test typeof(rmodel_float2) == typeof(rmodel_float) # @test typeof(rmodel_float) == typeof(Rule{Float64,Union{Rule{Float64},ConstantModel{Float64}}}(phi,rmodel_float0)) -# @test typeof(rmodel_float) != typeof(Rule{Float64,Union{Rule{Float64},FinalModel{Float64}}}(phi,rmodel_float0)) +# @test typeof(rmodel_float) != typeof(Rule{Float64,Union{Rule{Float64},LeafModel{Float64}}}(phi,rmodel_float0)) # @test typeof(rmodel_float) == typeof(Rule{Float64,Union{Rule,ConstantModel}}(phi,rmodel_float0)) rmodel2_float = @test_nowarn Rule(phi2, rmodel_float) diff --git a/test/logisets/logisets.jl b/test/logisets/logisets.jl index b9d97c5..23c2253 100644 --- a/test/logisets/logisets.jl +++ b/test/logisets/logisets.jl @@ -95,9 +95,9 @@ end w = worlds[1] W = worldtype(bool_logiset) -bool_supported_logiset = SupportedLogiset(bool_logiset) -scalar_supported_logiset = SupportedLogiset(scalar_logiset) -nonscalar_supported_logiset = SupportedLogiset(nonscalar_logiset) +bool_supported_logiset = @test_nowarn SupportedLogiset(bool_logiset) +scalar_supported_logiset = @test_nowarn SupportedLogiset(scalar_logiset) +nonscalar_supported_logiset = @test_nowarn SupportedLogiset(nonscalar_logiset) @test SoleModels.featvalue(nonscalar_logiset, 1, worlds[1], features[1]) == SoleModels.featvalue(nonscalar_supported_logiset, 1, worlds[1], features[1]) diff --git a/test/misc.jl b/test/misc.jl index d8cdbc4..e3b99c1 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -6,13 +6,13 @@ using Test using SoleLogics using SoleModels using SoleModels: AbstractModel -using SoleModels: ConstantModel, FinalModel +using SoleModels: ConstantModel, LeafModel using SoleModels: LogicalTruthCondition, TrueCondition using SoleModels: listrules, formula, displaymodel, submodels io = IOBuffer() -################################### FinalModel ############################################# +################################### LeafModel ############################################# outcome_int = @test_nowarn ConstantModel(2) outcome_float = @test_nowarn ConstantModel(1.5) outcome_string = @test_nowarn ConstantModel("YES") @@ -146,7 +146,7 @@ rmodel_float = @test_nowarn Rule{Float64}(phi,rmodel_float0) rmodel_float2 = @test_nowarn Rule{Float64}(phi,rmodel_float) @test typeof(rmodel_float2) == typeof(rmodel_float) # @test typeof(rmodel_float) == typeof(Rule{Float64,Union{Rule{Float64},ConstantModel{Float64}}}(phi,rmodel_float0)) -# @test typeof(rmodel_float) != typeof(Rule{Float64,Union{Rule{Float64},FinalModel{Float64}}}(phi,rmodel_float0)) +# @test typeof(rmodel_float) != typeof(Rule{Float64,Union{Rule{Float64},LeafModel{Float64}}}(phi,rmodel_float0)) # @test typeof(rmodel_float) == typeof(Rule{Float64,Union{Rule,ConstantModel}}(phi,rmodel_float0)) rmodel2_float = @test_nowarn Rule(phi2, rmodel_float) diff --git a/test/runtests.jl b/test/runtests.jl index b97fdb3..a711232 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,7 +17,7 @@ println("Julia version: ", VERSION) test_suites = [ ("Logisets", [ - "logisets/logisets.jl", + # "logisets/logisets.jl", TODO bring back "logisets/cube2logiset.jl", "logisets/dataframe2logiset.jl", "logisets/multilogisets.jl", From be4ac9fd2269e1f4c0d25432817e3c12008d5fb5 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Mon, 26 Jun 2023 15:11:52 +0200 Subject: [PATCH 19/77] naturalconditions, naturalgrouping, MixedFeature->MixedCondition --- Project.toml | 1 + src/SoleModels.jl | 4 +- src/logisets/Tables.jl | 108 ++++++++++ .../dataset-bindings.jl | 20 +- src/logisets/dimensional-structures/main.jl | 2 +- src/logisets/logiset.jl | 4 + src/logisets/main.jl | 2 + src/logisets/multilogiset.jl | 9 +- src/logisets/scalar/dataset-bindings.jl | 197 +++++++++++++++++- src/logisets/scalar/main.jl | 10 +- src/logisets/supported-logiset.jl | 20 ++ src/machine-learning.jl | 6 +- src/other/dimensional-logiset.jl | 8 +- 13 files changed, 360 insertions(+), 31 deletions(-) create mode 100644 src/logisets/Tables.jl diff --git a/Project.toml b/Project.toml index bcb6933..78a2958 100644 --- a/Project.toml +++ b/Project.toml @@ -23,6 +23,7 @@ SoleData = "123f1ae1-6307-4526-ab5b-aab3a92a2b8c" SoleLogics = "b002da8f-3cb3-4d91-bbe3-2953433912b5" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" +Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" ThreadSafeDicts = "4239201d-c60e-5e0a-9702-85d713665ba7" UniqueVectors = "2fbcfb34-fd0c-5fbb-b5d7-e826d8f5b0a9" diff --git a/src/SoleModels.jl b/src/SoleModels.jl index 46c2a0c..c6a827b 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -81,11 +81,11 @@ export computefeature export scalarlogiset -export initlogiset, maxchannelsize, worldtype, dimensionality, allworlds, featvalue, nvariables +export initlogiset, maxchannelsize, worldtype, dimensionality, frame, featvalue, nvariables export ScalarMetaCondition -export MixedFeature, CanonicalFeature, canonical_geq, canonical_leq +export MixedCondition, CanonicalFeature, canonical_geq, canonical_leq export canonical_geq_95, canonical_geq_90, canonical_geq_85, canonical_geq_80, canonical_geq_75, canonical_geq_70, canonical_geq_60, canonical_leq_95, canonical_leq_90, canonical_leq_85, canonical_leq_80, canonical_leq_75, canonical_leq_70, canonical_leq_60 diff --git a/src/logisets/Tables.jl b/src/logisets/Tables.jl new file mode 100644 index 0000000..64299a1 --- /dev/null +++ b/src/logisets/Tables.jl @@ -0,0 +1,108 @@ +import SoleData: eachinstance + +function eachinstance(X::AbstractLogiset) + map(i_instance->(X,i_instance), 1:ninstances(X)) +end + +function eachinstance(X::MultiLogiset) + map(i_instance->(X,i_instance), 1:ninstances(X)) +end + + + +function featchannel( + X::AbstractLogiset{W}, + i_instance::Integer, + i_feature::Integer, +) where {W<:AbstractWorld} + featchannel(X, i_instance, features(X)[i_feature]) +end + +function readfeature( + X::AbstractLogiset{W}, + featchannel::Any, + w::W, + i_feature::Integer, +) where {W<:AbstractWorld} + readfeature(X, featchannel, w, features(X)[i_feature]) +end + + +function featvalue( + X::AbstractLogiset{W}, + i_instance::Integer, + w::W, + i_feature::Integer, +) where {W<:AbstractWorld} + featvalue(X, i_instance, w, features(X)[i_feature]) +end + +function featvalue!( + X::AbstractLogiset{W}, + featval, + i_instance::Integer, + w::W, + i_feature::Integer, +) where {W<:AbstractWorld} + featvalue(X, featval, i_instance, w, features(X)[i_feature]) +end + +function featvalues!( + X::AbstractLogiset{W}, + featslice, + i_feature::Integer, +) where {W<:AbstractWorld} + featvalues(X, featslice, features(X)[i_feature]) +end + +import Tables: istable, rows, subset, getcolumn, columnnames, rowaccess + +Tables.istable(X::AbstractLogiset) = true +Tables.istable(X::MultiLogiset) = true + +Tables.rowaccess(X::AbstractLogiset) = true +Tables.rowaccess(X::MultiLogiset) = true + +function Tables.rows(X::AbstractLogiset) + eachinstance(X) +end +function Tables.rows(X::MultiLogiset) + eachinstance(X) +end + +function Tables.subset(X::AbstractLogiset, inds; viewhint=nothing) + slicedataset(X, inds; return_view = (isnothing(viewhint) || viewhint == true)) +end + +function Tables.subset(X::MultiLogiset, inds; viewhint=nothing) + slicedataset(X, inds; return_view = (isnothing(viewhint) || viewhint == true)) +end + +function Tables.getcolumn(row::Tuple{AbstractLogiset,Integer}, i::Int) + (features(row[1])[i],featchannel(row[1], row[2], i)) +end + +function Tables.columnnames(row::Tuple{AbstractLogiset,Integer}) + 1:nfeatures(row[1]) +end + +function Tables.getcolumn(row::Tuple{MultiLogiset,Integer}, i::Int) + m = modality(row[1], i) + (Tables.getcolumn((m, row[2]), i_feature) for i_feature in 1:nfeatures(m)) +end + +function Tables.columnnames(row::Tuple{MultiLogiset,Integer}) + 1:nmodalities(row[1]) +end + +# function Tables.materializer(::Type{<:AbstractLogiset}) +# function materialize(columns) +# columns +# end +# end + +import Base: vcat + +Base.vcat(Xs::AbstractLogiset...) = concatdatasets(Xs...) +Base.vcat(Xs::MultiLogiset...) = concatdatasets(Xs...) + diff --git a/src/logisets/dimensional-structures/dataset-bindings.jl b/src/logisets/dimensional-structures/dataset-bindings.jl index 6e93d86..4c9204c 100644 --- a/src/logisets/dimensional-structures/dataset-bindings.jl +++ b/src/logisets/dimensional-structures/dataset-bindings.jl @@ -6,7 +6,7 @@ using SoleData: AbstractDimensionalDataset, import SoleData: ninstances, nvariables import SoleModels: scalarlogiset, - initlogiset, initlogiset, allworlds, + initlogiset, initlogiset, frame, featchannel, readfeature, featvalue, vareltype function initlogiset( @@ -44,11 +44,11 @@ function initlogiset( return UniformFullDimensionalLogiset{U,W,N}(featstruct, features) end -function allworlds( +function frame( dataset::Union{UniformDimensionalDataset,AbstractArray}, i_instance::Integer ) - allworlds(FullDimensionalFrame(channelsize(dataset))) + FullDimensionalFrame(channelsize(dataset)) end function featchannel( @@ -103,13 +103,21 @@ function initlogiset( initlogiset(cube, features) end -function allworlds( +function frame( dataset::AbstractDataFrame, i_instance::Integer ) # dataset_cube, varnames = SoleData.dataframe2cube(dataset; dry_run = true) - # allworlds(FullDimensionalFrame(channelsize(dataset_cube))) - allworlds(FullDimensionalFrame(size(dataset[i_instance,1]))) + # FullDimensionalFrame(channelsize(dataset_cube)) + column = dataset[:,1] + frame(column, i_instance) +end + +function frame( + column::Vector, + i_instance::Integer +) + FullDimensionalFrame(size(column[i_instance])) end function featchannel( diff --git a/src/logisets/dimensional-structures/main.jl b/src/logisets/dimensional-structures/main.jl index daba00a..ae20ae7 100644 --- a/src/logisets/dimensional-structures/main.jl +++ b/src/logisets/dimensional-structures/main.jl @@ -26,11 +26,11 @@ using SoleModels.utils using SoleModels: Aggregator, AbstractCondition using SoleModels: BoundedScalarConditions -using SoleModels: CanonicalFeatureGeq, CanonicalFeatureGeqSoft, CanonicalFeatureLeq, CanonicalFeatureLeqSoft using SoleModels: AbstractLogiset, AbstractMultiModalFrame using SoleModels: MultiLogiset, AbstractLogiset using SoleModels: apply_test_operator, existential_aggregator, aggregator_bottom, aggregator_to_binary +import SoleModels: features, nfeatures using SoleModels: worldtype, featvaltype, featuretype, frametype import SoleModels: representatives, ScalarMetaCondition, ScalarCondition, featvaltype diff --git a/src/logisets/logiset.jl b/src/logisets/logiset.jl index 2f3638d..fb02896 100644 --- a/src/logisets/logiset.jl +++ b/src/logisets/logiset.jl @@ -160,6 +160,10 @@ representatives(X::AbstractLogiset, i_instance::Integer, args...) = representati ############################################################################################ # Non mandatory +function features(X::AbstractLogiset) + error("Please, provide method features(::$(typeof(X))).") +end + function nfeatures(X::AbstractLogiset) error("Please, provide method nfeatures(::$(typeof(X))).") end diff --git a/src/logisets/main.jl b/src/logisets/main.jl index 6eb603c..f59fdd8 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -49,6 +49,8 @@ export nfeatures include("scalar/main.jl") +include("Tables.jl") + export initlogiset, ninstances, maxchannelsize, worldtype, dimensionality, allworlds, featvalue export nvariables diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index 846761b..248a9df 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -41,11 +41,6 @@ end eachmodality(X::MultiLogiset) = X.modalities -Base.length(X::MultiLogiset) = length(nmodalities(X)) -Base.push!(X::MultiLogiset, f::AbstractLogiset) = push!(eachmodality(X), f) -Base.getindex(X::MultiLogiset, i_modality::Integer) = eachmodality(X)[i_modality] -Base.iterate(X::MultiLogiset, state=1) = state > nmodalities(X) ? nothing : (modality(X, state), state+1) - modalitytype(::Type{<:MultiLogiset{L}}) where {L<:AbstractLogiset} = L modalitytype(X::MultiLogiset) = modalitytype(typeof(X)) @@ -161,6 +156,10 @@ end # # nrelations(X::MultiLogiset) = map(nrelations, eachmodality(X)) # TODO: figure if this is useless or not # nfeatures(X::MultiLogiset, i_modality::Integer) = nfeatures(modality(X, i_modality)) # nrelations(X::MultiLogiset, i_modality::Integer) = nrelations(modality(X, i_modality)) +# Base.length(X::MultiLogiset) = length(nmodalities(X)) +# Base.push!(X::MultiLogiset, f::AbstractLogiset) = push!(eachmodality(X), f) +# Base.getindex(X::MultiLogiset, i_modality::Integer) = eachmodality(X)[i_modality] +# Base.iterate(X::MultiLogiset, state=1) = state > nmodalities(X) ? nothing : (modality(X, state), state+1) ############################################################################################ diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 33cf1d8..2a5ff18 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -10,8 +10,8 @@ end function nvariables(dataset) error("Please, provide method nvariables(dataset::$(typeof(dataset))).") end -function allworlds(dataset, i_instance) - error("Please, provide method allworlds(dataset::$(typeof(dataset)), i_instance::Integer).") +function frame(dataset, i_instance) + error("Please, provide method frame(dataset::$(typeof(dataset)), i_instance::Integer).") end function featvalue(dataset, i_instance, w, feature) error("Please, provide method featvalue(dataset::$(typeof(dataset)), i_instance::Integer, w::$(typeof(w)), feature::$(typeof(feature))).") @@ -20,6 +20,10 @@ function vareltype(dataset, i_variable) error("Please, provide method vareltype(dataset::$(typeof(dataset)), i_variable::Integer).") end +function allworlds(dataset, i_instance) + allworlds(frame(dataset, i_instance)) +end + # Multimodal dataset interface function ismultimodal(dataset) false @@ -56,7 +60,7 @@ If `dataset` is not a multimodal dataset, the following methods should be define initlogiset(dataset, features::AbstractVector{<:VarFeature}) ninstances(dataset) nvariables(dataset) - allworlds(dataset, i_instance::Integer) + frame(dataset, i_instance::Integer) featvalue(dataset, i_instance::Integer, w::AbstractWorld, feature::VarFeature) vareltype(dataset, i_variable::Integer) ``` @@ -142,8 +146,8 @@ function scalarlogiset( features = collect(Iterators.flatten([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:nvariables(dataset)])) end - features_ok = filter(f->isconcretetype(featvaltype(f)), features) - features_notok = filter(f->!isconcretetype(featvaltype(f)), features) + features_ok = filter(f->isconcretetype(SoleModels.featvaltype(f)), features) + features_notok = filter(f->!isconcretetype(SoleModels.featvaltype(f)), features) if length(features_notok) > 0 if all(preserveseltype, features_notok) && all(f->f isa AbstractUnivariateFeature, features_notok) @@ -206,3 +210,186 @@ function scalarlogiset( ) end end + + +const MixedCondition = Union{ + <:Union{SoleModels.VarFeature,Base.Callable}, # feature (i.e., callables to be associated to all variables, or SoleModels.VarFeature objects); + <:Tuple{Base.Callable,Integer}, # (callable,var_id); + <:Tuple{TestOperator,<:Union{SoleModels.VarFeature,Base.Callable}}, # (test_operator,features); + <:ScalarMetaCondition, # ScalarMetaCondition; +} + +function naturalconditions( + dataset, + mixed_conditions :: AbstractVector, + featvaltype :: Type = Real +) + nvars = nvariables(dataset) + V = featvaltype + + @assert all(isa.(mixed_conditions, MixedCondition)) "" * + "Unknown condition seed encountered! " * + "$(filter(f->!isa(f, MixedCondition), mixed_conditions)), " * + "$(typeof.(filter(f->!isa(f, MixedCondition), mixed_conditions)))" + + mixed_conditions = Vector{MixedCondition}(mixed_conditions) + + is_propositional_dataset = all(i_instance->nworlds(frame(dataset, i_instance)) == 1, 1:ninstances(dataset)) + + def_test_operators = is_propositional_dataset ? [≥] : [≥, <] + + # univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeq) = ([≥],DimensionalDatasets.UnivariateMin{V}(i_var)) + # univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeq) = ([<],DimensionalDatasets.UnivariateMax{V}(i_var)) + # univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeqSoft) = ([≥],DimensionalDatasets.UnivariateSoftMin{V}(i_var, cond.alpha)) + # univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeqSoft) = ([<],DimensionalDatasets.UnivariateSoftMax{V}(i_var, cond.alpha)) + function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},typeof(minimum)}) + return (test_ops,DimensionalDatasets.UnivariateMin{V}(i_var)) + end + function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},typeof(maximum)}) + return (test_ops,DimensionalDatasets.UnivariateMax{V}(i_var)) + end + function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},Base.Callable}) + return (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_var, (x)->(V(cond(x))))) + end + univar_condition(i_var,::Any) = throw_n_log("Unknown mixed_feature type: $(cond), $(typeof(cond))") + + + # readymade conditions + unpackcondition(cond::ScalarMetaCondition) = [cond] + unpackcondition(feature::AbstractFeature) = [ScalarMetaCondition(feature, test_op) for test_op in def_test_operators] + unpackcondition(cond::Tuple{TestOperator,AbstractFeature}) = [ScalarMetaCondition(cond[2], cond[1])] + + # single-variable conditions + unpackcondition(cond::Any) = cond + # unpackcondition(cond::CanonicalFeature) = cond + unpackcondition(cond::Base.Callable) = (def_test_operators, cond) + function unpackcondition(cond::Tuple{Base.Callable,Integer}) + return univar_condition(cond[2], (def_test_operators, cond[1])) + end + unpackcondition(cond::Tuple{TestOperator,Base.Callable}) = ([cond[1]], cond[2]) + + metaconditions = ScalarMetaCondition[] + + mixed_conditions = unpackcondition.(mixed_conditions) + + readymade_conditions = filter(x-> + isa(x, Vector{ScalarMetaCondition}), + mixed_conditions, + ) + variable_specific_conditions = filter(x-> + # isa(x, CanonicalFeature) || + # isa(x, Tuple{<:AbstractVector{<:TestOperator},Base.Callable}) || + (isa(x, Tuple{AbstractVector,Base.Callable}) && !isa(x, Tuple{AbstractVector,AbstractFeature})), + mixed_conditions, + ) + + @assert length(readymade_conditions) + length(variable_specific_conditions) == length(mixed_conditions) "" * + "Unexpected " * + "mixed_conditions. $(mixed_conditions). " * + "$(filter(x->(! (x in readymade_conditions) && ! (x in variable_specific_conditions)), mixed_conditions)). " * + "$(length(readymade_conditions)) + $(length(variable_specific_conditions)) == $(length(mixed_conditions))." + + for cond in readymade_conditions + push!(metaconditions, cond) + end + + for i_var in 1:nvars + for (test_ops,feature) in map((cond)->univar_condition(i_var,cond),variable_specific_conditions) + for test_op in test_ops + cond = ScalarMetaCondition(feature, test_op) + push!(metaconditions, cond) + end + end + end + metaconditions +end + +""" + naturalgrouping( + X::AbstractDataFrame; + allow_variable_drop = false, + )::AbstractVector{<:AbstractVector{<:Symbol}} + +Return variables grouped by their logical nature; +the nature of a variable is automatically derived +from its type (e.g., Real, Vector{<:Real} or Matrix{<:Real}) and frame. +All instances must have the same frame (e.g., channel size/number of worlds). + +TODO example +""" +function naturalgrouping( + X::AbstractDataFrame; + allow_variable_drop = false, + # allow_nonuniform_variable_types = false, + # allow_nonuniform_variables = false, +) #::AbstractVector{<:AbstractVector{<:Symbol}} + + coltypes = eltype.(eachcol(X)) + + function _frame(datacolumn, i_instance) + if hasmethod(frame, (typeof(datacolumn), Integer)) + frame(datacolumn, i_instance) + else + missing + end + end + + # Check that columns with same dimensionality have same eltype's. + for T in [Real, Vector, Matrix] + these_coltypes = filter((t)->(t<:T), coltypes) + @assert all([eltype(t) <: Real for t in these_coltypes]) "$(these_coltypes). Cannot " * + "apply this algorithm on variables types with non-Real " * + "eltype's: $(filter((t)->(!(eltype(t) <: Real)), these_coltypes))." + @assert length(unique(these_coltypes)) <= 1 "$(these_coltypes). Cannot " * + "apply this algorithm on dataset with non-uniform types for variables " * + "with eltype = $(T). Please, convert all values to $(promote_type(these_coltypes...))." + end + + columnnames = names(X) + percol_framess = [unique((i_instance)->(_frame(X[:,col], i_instance)), 1:ninstances(X)) for col in columnnames] + + # Must have common frame across instances + _uniform_columns = (length.(percol_framess) .== 1) + _framed_columns = (((cs)->all((!).(ismissing.(cs)))).(percol_framess)) + + __nonuniform_cols = columnnames[(!).(_uniform_columns)] + if length(__nonuniform_cols) > 0 + if allow_variable_drop + @warn "Dropping columns due to non-uniform frame across instances: $(join(__nonuniform_cols, ", "))..." + else + error("Non-uniform frame across instances for columns $(join(__nonuniform_cols, ", "))") + end + end + __uniform_nonframed_cols = columnnames[_uniform_columns .&& (!).(_framed_columns)] + if length(__uniform_nonframed_cols) > 0 + if allow_variable_drop + @warn "Dropping columns due to unspecified frame: $(join(__uniform_nonframed_cols, ", "))..." + else + error("Could not derive frame for columns $(join(__uniform_nonframed_cols, ", "))") + end + end + + _good_columns = _uniform_columns .&& _framed_columns + + if length(_good_columns) == 0 + error("Could not find any suitable variables in DataFrame.") + end + + percol_framess = percol_framess[_good_columns] + columnnames = columnnames[_good_columns] + percol_frames = getindex.(percol_framess, 1) + + var_grouping = begin + unique_frames = sort(unique(percol_frames)) + + percol_modality = [findfirst((ucs)->(ucs==cs), unique_frames) for cs in percol_frames] + + var_grouping = Dict([modality => [] for modality in unique(percol_modality)]) + for (modality, col) in zip(percol_modality, columnnames) + push!(var_grouping[modality], col) + end + [var_grouping[modality] for modality in unique(percol_modality)] + end + + var_grouping +end diff --git a/src/logisets/scalar/main.jl b/src/logisets/scalar/main.jl index 090c3e8..7026a9e 100644 --- a/src/logisets/scalar/main.jl +++ b/src/logisets/scalar/main.jl @@ -11,17 +11,17 @@ include("conditions.jl") # Templates for formulas of scalar conditions (e.g., templates for ⊤, f ⋈ t, ⟨R⟩ f ⋈ t, etc.) include("templated-formulas.jl") -# Types for representing common associations between features and operators -include("canonical-conditions.jl") - -const MixedFeature = Union{AbstractFeature,CanonicalFeature,Function,Tuple{TestOperator,Function},Tuple{TestOperator,AbstractFeature}} - include("random.jl") include("representatives.jl") include("dataset-bindings.jl") +# # Types for representing common associations between features and operators +# include("canonical-conditions.jl") TODO remove +# const MixedCondition = Union{AbstractFeature,CanonicalFeature,Function,Tuple{TestOperator,Function},Tuple{TestOperator,AbstractFeature}} +# Union{AbstractFeature,CanonicalFeature,Function,Tuple{TestOperator,Function},Tuple{TestOperator,AbstractFeature}} + include("memosets.jl") include("onestep-memoset.jl") diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index 4899c5a..bc699f0 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -183,6 +183,23 @@ capacity(X::SupportedLogiset) = sum(capacity.(supports(X))) nmemoizedvalues(X::SupportedLogiset) = sum(nmemoizedvalues.(supports(X))) +function featchannel( + X::AbstractLogiset{W}, + i_instance::Integer, + feature::AbstractFeature, +) where {W<:AbstractWorld} + featchannel(base(X), i_instance, feature) +end + +function readfeature( + X::AbstractLogiset{W}, + featchannel::Any, + w::W, + feature::AbstractFeature, +) where {W<:AbstractWorld} + featchannel(base(X), featchannel, w, feature) +end + function featvalue( X::SupportedLogiset, i_instance::Integer, @@ -328,3 +345,6 @@ end # TODO remove: support(X::SupportedLogiset) = first(supports(X)) + +features(X::SupportedLogiset) = features(base(X)) +nfeatures(X::SupportedLogiset) = nfeatures(base(X)) diff --git a/src/machine-learning.jl b/src/machine-learning.jl index 5f66572..8f4df01 100644 --- a/src/machine-learning.jl +++ b/src/machine-learning.jl @@ -91,9 +91,9 @@ function bestguess( if !suppress_parity_warning && sum(counts[argmax(counts)] .== values(counts)) > 1 @warn "Parity encountered in bestguess! " * - "Counts ($(length(labels)) elements): $(counts)" * - "Argmax: $(argmax(counts))" * - "Max: $(counts[argmax(counts)]) (sum = $(sum(values(counts))))" + "counts ($(length(labels)) elements): $(counts), " * + "argmax: $(argmax(counts)), " * + "max: $(counts[argmax(counts)]) (sum = $(sum(values(counts))))" end argmax(counts) end diff --git a/src/other/dimensional-logiset.jl b/src/other/dimensional-logiset.jl index 9e63b5d..672e9d0 100644 --- a/src/other/dimensional-logiset.jl +++ b/src/other/dimensional-logiset.jl @@ -101,11 +101,11 @@ struct DimensionalLogiset{ ) where {V,N,W<:AbstractWorld} domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalLogiset{N,W}(domain) : domain) - @assert all(isa.(mixed_features, MixedFeature)) "Unknown feature encountered! " * - "$(filter(f->!isa(f, MixedFeature), mixed_features)), " * - "$(typeof.(filter(f->!isa(f, MixedFeature), mixed_features)))" + @assert all(isa.(mixed_features, MixedCondition)) "Unknown feature encountered! " * + "$(filter(f->!isa(f, MixedCondition), mixed_features)), " * + "$(typeof.(filter(f->!isa(f, MixedCondition), mixed_features)))" - mixed_features = Vector{MixedFeature}(mixed_features) + mixed_features = Vector{MixedCondition}(mixed_features) _features, featsnops = begin _features = AbstractFeature[] From 481e0693671074f85bd43d3c5d0873848bb93c8b Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:02:34 +0200 Subject: [PATCH 20/77] Fixes, add MLJ utils --- Project.toml | 3 + src/MLJ-utils.jl | 28 ++ src/SoleModels.jl | 5 + src/logisets/{Tables.jl => MLJ-interface.jl} | 23 +- src/logisets/check.jl | 4 +- .../dimensional-structures/computefeature.jl | 2 +- .../dimensional-structures/logiset.jl | 24 +- src/logisets/dimensional-structures/main.jl | 5 +- .../onestep-memosets.jl | 2 + src/logisets/logiset.jl | 8 - src/logisets/main.jl | 10 +- src/logisets/multilogiset.jl | 8 +- src/logisets/scalar/conditions.jl | 19 +- src/logisets/scalar/dataset-bindings.jl | 40 ++- src/logisets/scalar/onestep-memoset.jl | 10 +- src/logisets/scalar/templated-formulas.jl | 28 +- src/logisets/scalar/var-features.jl | 7 +- src/models/base.jl | 18 +- src/models/print.jl | 9 +- src/other/_parse-dimensional-condition.jl | 205 ------------- src/other/active-logiset.jl | 273 ------------------ src/other/dimensional-logiset.jl | 271 ----------------- src/other/dimensional-ontologies.jl | 104 ------- src/other/main.jl | 17 -- src/other/ontology.jl | 61 ---- src/other/passive-dimensional-logiset.jl | 139 --------- src/other/scalar-logiset.jl | 114 -------- src/other/test-dimensional-datasets.jl | 50 ---- test/logisets/MLJ.jl | 28 ++ test/runtests.jl | 1 + 30 files changed, 210 insertions(+), 1306 deletions(-) create mode 100644 src/MLJ-utils.jl rename src/logisets/{Tables.jl => MLJ-interface.jl} (83%) delete mode 100644 src/other/_parse-dimensional-condition.jl delete mode 100644 src/other/active-logiset.jl delete mode 100644 src/other/dimensional-logiset.jl delete mode 100644 src/other/dimensional-ontologies.jl delete mode 100644 src/other/main.jl delete mode 100644 src/other/ontology.jl delete mode 100644 src/other/passive-dimensional-logiset.jl delete mode 100644 src/other/scalar-logiset.jl delete mode 100644 src/other/test-dimensional-datasets.jl create mode 100644 test/logisets/MLJ.jl diff --git a/Project.toml b/Project.toml index 78a2958..b9258e3 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.1.0" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" +CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" @@ -14,6 +15,8 @@ Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" Lazy = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" +MLJBase = "a7f614a8-145f-11e9-1d2a-a57a1082229d" +MLJModelInterface = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" diff --git a/src/MLJ-utils.jl b/src/MLJ-utils.jl new file mode 100644 index 0000000..9496e6b --- /dev/null +++ b/src/MLJ-utils.jl @@ -0,0 +1,28 @@ +module MLJUtils + +using CategoricalArrays +using MLJBase + +export fix_y + +function fix_y(y) + if isnothing(y) + return nothing, nothing + end + is_classification = (eltype(MLJBase.scitype(y)) != Continuous) + classes_seen = begin + if is_classification + y isa CategoricalArray ? filter(in(unique(y)), MLJBase.classes(y)) : unique(y) + else + nothing + end + end + y = Vector{(is_classification ? String : Float64)}(y) + y, classes_seen +end + +# function is_classification(y) +# !isnothing(y) && eltype(MLJBase.scitype(y)) != Continuous +# end + +end diff --git a/src/SoleModels.jl b/src/SoleModels.jl index c6a827b..d4bbbaa 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -1,7 +1,10 @@ module SoleModels using SoleBase + using SoleData +using SoleData: _isnan + using SoleLogics using SoleLogics: AbstractInterpretation, AbstractInterpretationSet using SoleLogics: AbstractSyntaxToken @@ -108,4 +111,6 @@ using .DimensionalDatasets: globalrel const GenericDataset = Union{SoleData.AbstractDimensionalDataset,AbstractDataFrame,AbstractLogiset,MultiLogiset} +include("MLJ-utils.jl") + end diff --git a/src/logisets/Tables.jl b/src/logisets/MLJ-interface.jl similarity index 83% rename from src/logisets/Tables.jl rename to src/logisets/MLJ-interface.jl index 64299a1..2dc8155 100644 --- a/src/logisets/Tables.jl +++ b/src/logisets/MLJ-interface.jl @@ -1,4 +1,3 @@ -import SoleData: eachinstance function eachinstance(X::AbstractLogiset) map(i_instance->(X,i_instance), 1:ninstances(X)) @@ -55,8 +54,6 @@ function featvalues!( featvalues(X, featslice, features(X)[i_feature]) end -import Tables: istable, rows, subset, getcolumn, columnnames, rowaccess - Tables.istable(X::AbstractLogiset) = true Tables.istable(X::MultiLogiset) = true @@ -70,11 +67,11 @@ function Tables.rows(X::MultiLogiset) eachinstance(X) end -function Tables.subset(X::AbstractLogiset, inds; viewhint=nothing) +function Tables.subset(X::AbstractLogiset, inds; viewhint = nothing) slicedataset(X, inds; return_view = (isnothing(viewhint) || viewhint == true)) end -function Tables.subset(X::MultiLogiset, inds; viewhint=nothing) +function Tables.subset(X::MultiLogiset, inds; viewhint = nothing) slicedataset(X, inds; return_view = (isnothing(viewhint) || viewhint == true)) end @@ -95,11 +92,17 @@ function Tables.columnnames(row::Tuple{MultiLogiset,Integer}) 1:nmodalities(row[1]) end -# function Tables.materializer(::Type{<:AbstractLogiset}) -# function materialize(columns) -# columns -# end -# end +using MLJBase +using MLJModelInterface +import MLJModelInterface: selectrows, _selectrows + +# From MLJModelInferface.jl/src/data_utils.jl +function MLJModelInterface.selectrows(::MLJBase.FI, ::Val{:table}, X::Union{AbstractLogiset,MultiLogiset}, r) + r = r isa Integer ? (r:r) : r + return Tables.subset(X, r) +end + + import Base: vcat diff --git a/src/logisets/check.jl b/src/logisets/check.jl index 8da16a2..19eea11 100644 --- a/src/logisets/check.jl +++ b/src/logisets/check.jl @@ -17,8 +17,8 @@ function check( φ::SoleLogics.SyntaxTree, X::AbstractLogiset{W,U}, i_instance::Integer, - w::Union{Nothing,W,AbstractVector{<:W}} = nothing; - use_memo::Union{Nothing,AbstractMemoset{W},AbstractVector{<:AbstractDict{<:FT,<:WorldSet}}} = nothing, + w::Union{Nothing,<:AbstractWorld,AbstractVector{<:AbstractWorld}} = nothing; + use_memo::Union{Nothing,AbstractMemoset{<:AbstractWorld},AbstractVector{<:AbstractDict{<:FT,<:WorldSet}}} = nothing, perform_normalization::Bool = true, memo_max_height::Union{Nothing,Int} = nothing, onestep_memoset_is_complete = false, diff --git a/src/logisets/dimensional-structures/computefeature.jl b/src/logisets/dimensional-structures/computefeature.jl index e580111..61c6742 100644 --- a/src/logisets/dimensional-structures/computefeature.jl +++ b/src/logisets/dimensional-structures/computefeature.jl @@ -22,7 +22,7 @@ function computeunivariatefeature(f::UnivariateNamedFeature, varchannel::Union{T error("Cannot intepret UnivariateNamedFeature on any structure at all.") end function computeunivariatefeature(f::UnivariateValue{U}, varchannel::Union{T,AbstractArray{T}}) where {U<:Real,T} - varchannel::U + (varchannel isa U ? varchannel : first(varchannel))::U end function computeunivariatefeature(f::UnivariateMin{U}, varchannel::AbstractArray{T}) where {U<:Real,T} (minimum(varchannel))::U diff --git a/src/logisets/dimensional-structures/logiset.jl b/src/logisets/dimensional-structures/logiset.jl index c8b7201..2bc9d68 100644 --- a/src/logisets/dimensional-structures/logiset.jl +++ b/src/logisets/dimensional-structures/logiset.jl @@ -69,13 +69,21 @@ struct UniformFullDimensionalLogiset{ UniformFullDimensionalLogiset{U,W,N,D,FT,FullDimensionalFrame{N,W}}(featstruct, features) end - # function UniformFullDimensionalLogiset( - # dataset::Any, - # features::AbstractVector{<:VarFeature}, - # ) - # U = Union{featvaltype.(features)...} - # UniformFullDimensionalLogiset{U}(dataset, features) - # end + function UniformFullDimensionalLogiset( + featstruct::Any, + features::AbstractVector{<:VarFeature}, + ) + _worldtype(featstruct::AbstractArray{T,2}) where {T} = OneWorld + _worldtype(featstruct::AbstractArray{T,4}) where {T} = Interval{Int} + _worldtype(featstruct::AbstractArray{T,6}) where {T} = Interval2D{Int} + _dimensionality(featstruct::AbstractArray{T,2}) where {T} = 0 + _dimensionality(featstruct::AbstractArray{T,4}) where {T} = 1 + _dimensionality(featstruct::AbstractArray{T,6}) where {T} = 2 + U = Union{featvaltype.(features)...} + W = _worldtype(featstruct) + N = _dimensionality(featstruct) + UniformFullDimensionalLogiset{U,W,N}(featstruct, features) + end end @@ -471,3 +479,5 @@ function hasnans(X::UniformFullDimensionalLogiset{U,<:Interval2D}) where {U} for xx in 1:size(X, 1) for xy in (xx+1):size(X, 2) for yx in 1:size(X, 3) for yy in (yx+1):size(X, 4)]) end + +############################################################################################ diff --git a/src/logisets/dimensional-structures/main.jl b/src/logisets/dimensional-structures/main.jl index ae20ae7..878d981 100644 --- a/src/logisets/dimensional-structures/main.jl +++ b/src/logisets/dimensional-structures/main.jl @@ -15,7 +15,8 @@ using SoleLogics: AbstractFrame, AbstractDimensionalFrame, FullDimensionalFrame import SoleLogics: worldtype, accessibles, allworlds, alphabet using SoleData -import SoleData: _isnan, hasnans, nvariables, maxchannelsize, channelsize +using SoleData: _isnan +import SoleData: hasnans, nvariables, maxchannelsize, channelsize import SoleData: instance, get_instance, concatdatasets import SoleData: displaystructure import SoleData: dimensionality @@ -48,6 +49,8 @@ import SoleData: get_instance, ninstances, nvariables, channelsize, eltype ############################################################################################ +export UniformFullDimensionalLogiset + # Frame-specific logisets include("logiset.jl") diff --git a/src/logisets/dimensional-structures/onestep-memosets.jl b/src/logisets/dimensional-structures/onestep-memosets.jl index 5f29bd5..4840106 100644 --- a/src/logisets/dimensional-structures/onestep-memosets.jl +++ b/src/logisets/dimensional-structures/onestep-memosets.jl @@ -307,3 +307,5 @@ function displaystructure( return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") end + +############################################################################################ diff --git a/src/logisets/logiset.jl b/src/logisets/logiset.jl index fb02896..15a8ab2 100644 --- a/src/logisets/logiset.jl +++ b/src/logisets/logiset.jl @@ -129,14 +129,6 @@ end hasnans(::AbstractLogiset) = any.(isnan, allfeatvalues(X)) -function initialworldset( - X::AbstractLogiset, - i_instance::Integer, - args... -) - initialworldset(frame(X, i_instance), args...) -end - ############################################################################################ function Base.show(io::IO, X::AbstractLogiset; kwargs...) diff --git a/src/logisets/main.jl b/src/logisets/main.jl index f59fdd8..41257ae 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -10,6 +10,10 @@ import SoleData: ninstances import SoleData: hasnans, instances, concatdatasets import SoleData: displaystructure +# TODO fix +import SoleData: eachinstance +import Tables: istable, rows, subset, getcolumn, columnnames, rowaccess + # Features to be computed on worlds of dataset instances include("features.jl") @@ -26,7 +30,7 @@ include("representatives.jl") export ninstances, featvalue, displaystructure, isminifiable, minify -# Logical datasets, where the instances are Kripke models with conditional alphabets +# Logical datasets, where the instances are Kripke structures with conditional alphabets include("logiset.jl") include("memosets.jl") @@ -48,8 +52,8 @@ export nfeatures include("scalar/main.jl") - -include("Tables.jl") +# Tables interface for Logiset's, so that it can be integrated with MLJ +include("MLJ-interface.jl") export initlogiset, ninstances, maxchannelsize, worldtype, dimensionality, allworlds, featvalue diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index 248a9df..338036f 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -178,6 +178,10 @@ struct MultiFormula{ formulas::Dict{Int,F} end +function MultiFormula(i_modality, formula::SyntaxTree) + MultiFormula(Dict{Int,SyntaxTree}(i_modality => formula)) +end + function SoleLogics.tree(f::MultiFormula) error("Cannot convert object of type MultiFormula to a SyntaxTree.") end @@ -186,12 +190,12 @@ function syntaxstring(f::MultiFormula; kwargs...) join(["{$(i_modality)}($(syntaxstring(f.formulas[i_modality])))" for i_modality in sort(collect(keys(f.formulas)))], " ∧ ") end -function joinformulas(op::typeof(CONJUNCTION), children::NTuple{N,MultiFormula{F}}) where {N,F} +function joinformulas(op::SoleLogics.AbstractOperator, children::NTuple{N,MultiFormula{F}}) where {N,F} formulas = Dict{Int,F}() i_modalities = unique(vcat(collect.(keys.([ch.formulas for ch in children]))...)) for i_modality in i_modalities chs = filter(ch->haskey(ch.formulas, i_modality), children) - fs = filter(ch->ch.formulas[i_modality], chs) + fs = map(ch->ch.formulas[i_modality], chs) formulas[i_modality] = joinformulas(op, fs) end return MultiFormula(formulas) diff --git a/src/logisets/scalar/conditions.jl b/src/logisets/scalar/conditions.jl index f3da426..7b03ae6 100644 --- a/src/logisets/scalar/conditions.jl +++ b/src/logisets/scalar/conditions.jl @@ -56,7 +56,11 @@ function _syntaxstring_metacondition( end end -_st_featop_name(feature::AbstractFeature, test_operator::TestOperator; kwargs...) = "$(syntaxstring(feature; kwargs...)) $(test_operator)" +_st_featop_name(feature::AbstractFeature, test_operator::TestOperator; kwargs...) = "$(syntaxstring(feature; kwargs...)) $(_st_testop_name(test_operator))" + +_st_testop_name(test_op::Any) = "$(test_op)" +_st_testop_name(::typeof(>=)) = "≥" +_st_testop_name(::typeof(<=)) = "≤" # Abbreviations @@ -71,11 +75,22 @@ function groupbyfeature( if isnothing(features) features = unique(feature.(metaconditions)) end - return map(_feature->begin + groups = map(_feature->begin these_metaconds = filter(m->feature(m) == _feature, metaconditions) # these_testops = unique(test_operator.(these_metaconds)) (_feature, these_metaconds) end, features) + all_matched_metaconds = unique(vcat(last.(groups)...)) + unmatched_metaconds = filter(m->!(m in all_matched_metaconds), metaconditions) + if length(unmatched_metaconds) != 0 + if length(unmatched_metaconds) == length(metaconditions) + error("Could not find features for any of the $(length(metaconditions)) " * + "metaconditions: $(metaconditions). Features: $(features).") + end + @warn "Could not find features for $(length(unmatched_metaconds)) " * + "metaconditions: $(unmatched_metaconds)." + end + return groups end ############################################################################################ diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 2a5ff18..776b03b 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -143,7 +143,14 @@ function scalarlogiset( end if isnothing(features) - features = collect(Iterators.flatten([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:nvariables(dataset)])) + is_propositional_dataset = all(i_instance->nworlds(frame(dataset, i_instance)) == 1, 1:ninstances(dataset)) + features = begin + if is_propositional_dataset + [UnivariateValue(i_var) for i_var in 1:nvariables(dataset)] + else + cat([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:nvariables(dataset)]...) + end + end end features_ok = filter(f->isconcretetype(SoleModels.featvaltype(f)), features) @@ -225,7 +232,6 @@ function naturalconditions( featvaltype :: Type = Real ) nvars = nvariables(dataset) - V = featvaltype @assert all(isa.(mixed_conditions, MixedCondition)) "" * "Unknown condition seed encountered! " * @@ -238,17 +244,37 @@ function naturalconditions( def_test_operators = is_propositional_dataset ? [≥] : [≥, <] - # univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeq) = ([≥],DimensionalDatasets.UnivariateMin{V}(i_var)) - # univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeq) = ([<],DimensionalDatasets.UnivariateMax{V}(i_var)) - # univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeqSoft) = ([≥],DimensionalDatasets.UnivariateSoftMin{V}(i_var, cond.alpha)) - # univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeqSoft) = ([<],DimensionalDatasets.UnivariateSoftMax{V}(i_var, cond.alpha)) + # univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeq) = ([≥],DimensionalDatasets.UnivariateMin{featvaltype}(i_var)) + # univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeq) = ([<],DimensionalDatasets.UnivariateMax{featvaltype}(i_var)) + # univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeqSoft) = ([≥],DimensionalDatasets.UnivariateSoftMin{featvaltype}(i_var, cond.alpha)) + # univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeqSoft) = ([<],DimensionalDatasets.UnivariateSoftMax{featvaltype}(i_var, cond.alpha)) + function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},typeof(identity)}) + V = vareltype(dataset, i_var) + if !isconcretetype(V) + @warn "Building UnivariateValue with non-concrete feature type: $(V)." + end + return (test_ops,DimensionalDatasets.UnivariateValue{V}(i_var)) + end function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},typeof(minimum)}) + V = vareltype(dataset, i_var) + if !isconcretetype(V) + @warn "Building UnivariateMin with non-concrete feature type: $(V)." + end return (test_ops,DimensionalDatasets.UnivariateMin{V}(i_var)) end function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},typeof(maximum)}) + V = vareltype(dataset, i_var) + if !isconcretetype(V) + @warn "Building UnivariateMax with non-concrete feature type: $(V)." + end return (test_ops,DimensionalDatasets.UnivariateMax{V}(i_var)) end function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},Base.Callable}) + V = featvaltype + if !isconcretetype(V) + @warn "Building UnivariateFeature with non-concrete feature type: $(V)." + "Please provide `featvaltype` parameter to naturalconditions." + end return (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_var, (x)->(V(cond(x))))) end univar_condition(i_var,::Any) = throw_n_log("Unknown mixed_feature type: $(cond), $(typeof(cond))") @@ -376,7 +402,7 @@ function naturalgrouping( end percol_framess = percol_framess[_good_columns] - columnnames = columnnames[_good_columns] + columnnames = Symbol.(columnnames[_good_columns]) percol_frames = getindex.(percol_framess, 1) var_grouping = begin diff --git a/src/logisets/scalar/onestep-memoset.jl b/src/logisets/scalar/onestep-memoset.jl index 0fe0b26..56707bb 100644 --- a/src/logisets/scalar/onestep-memoset.jl +++ b/src/logisets/scalar/onestep-memoset.jl @@ -17,7 +17,7 @@ function featchannel_onestep_aggregation(X::SupportedLogiset, args...) if length(onestep_supps) > 0 @assert length(onestep_supps) == 1 "Currently, using more " * "than one AbstractOneStepMemoset is not allowed." - featchannel_onestep_aggregation(base(X), onestep_supps(X)[1], args...) + featchannel_onestep_aggregation(base(X), onestep_supps[1], args...) else featchannel_onestep_aggregation(base(X), args...) end @@ -253,7 +253,10 @@ struct ScalarOneStepMemoset{ compute_globmemoset = begin if globalrel in relations relations = filter(l->l≠globalrel, relations) - if worldtype == OneWorld + if worldtype == OneWorld || all(i_instance->nworlds(frame(X, i_instance)) == 1, 1:ninstances(X)) + @warn "ScalarOneStepMemoset: " * + "Found globalrel in relations but single-world case is" * + "identified." false else true @@ -336,6 +339,9 @@ nrelations(Xm::ScalarOneStepMemoset) = length(Xm.relations) relmemoset(Xm::ScalarOneStepMemoset) = Xm.relmemoset globmemoset(Xm::ScalarOneStepMemoset) = Xm.globmemoset +capacity(Xm::ScalarOneStepMemoset) = sum(capacity, [relmemoset(Xm), globmemoset(Xm)]) +nmemoizedvalues(Xm::ScalarOneStepMemoset) = sum(nmemoizedvalues, [relmemoset(Xm), globmemoset(Xm)]) + ninstances(Xm::ScalarOneStepMemoset) = ninstances(relmemoset(Xm)) function grouped_metaconditions( diff --git a/src/logisets/scalar/templated-formulas.jl b/src/logisets/scalar/templated-formulas.jl index eea9451..1fbb4cb 100644 --- a/src/logisets/scalar/templated-formulas.jl +++ b/src/logisets/scalar/templated-formulas.jl @@ -14,25 +14,25 @@ struct ScalarPropositionFormula{U} <: ScalarFormula{U} p :: ScalarCondition{U} end -proposition(d::ScalarPropositionFormula) = Proposition(d.p) -feature(d::ScalarPropositionFormula) = feature(proposition(d)) -test_operator(d::ScalarPropositionFormula) = test_operator(proposition(d)) -threshold(d::ScalarPropositionFormula) = threshold(proposition(d)) +proposition(f::ScalarPropositionFormula) = Proposition(f.p) +feature(f::ScalarPropositionFormula) = feature(proposition(f)) +test_operator(f::ScalarPropositionFormula) = test_operator(proposition(f)) +threshold(f::ScalarPropositionFormula) = threshold(proposition(f)) -tree(d::ScalarPropositionFormula) = SyntaxTree(d.p) -negation(d::ScalarPropositionFormula{U}) where {U} = +tree(f::ScalarPropositionFormula) = SyntaxTree(f.p) +negation(f::ScalarPropositionFormula{U}) where {U} = ScalarPropositionFormula{U}(negation(p)) ############################################################################################ abstract type ScalarOneStepFormula{U} <: ScalarFormula{U} end -relation(d::ScalarOneStepFormula) = d.relation -proposition(d::ScalarOneStepFormula) = Proposition(d.p) -metacond(d::ScalarOneStepFormula) = metacond(proposition(d)) -feature(d::ScalarOneStepFormula) = feature(proposition(d)) -test_operator(d::ScalarOneStepFormula) = test_operator(proposition(d)) -threshold(d::ScalarOneStepFormula) = threshold(proposition(d)) +relation(f::ScalarOneStepFormula) = f.relation +proposition(f::ScalarOneStepFormula) = Proposition(f.p) +metacond(f::ScalarOneStepFormula) = metacond(atom(proposition(f))) +feature(f::ScalarOneStepFormula) = feature(atom(proposition(f))) +test_operator(f::ScalarOneStepFormula) = test_operator(atom(proposition(f))) +threshold(f::ScalarOneStepFormula) = threshold(atom(proposition(f))) """ Templated formula for ⟨R⟩ f ⋈ t. @@ -90,7 +90,7 @@ struct ScalarExistentialFormula{U} <: ScalarOneStepFormula{U} end end -tree(d::ScalarExistentialFormula) = DiamondRelationalOperator(d.relation)(Proposition(d.p)) +tree(f::ScalarExistentialFormula) = DiamondRelationalOperator(f.relation)(Proposition(f.p)) """ Templated formula for [R] f ⋈ t. @@ -100,7 +100,7 @@ struct ScalarUniversalFormula{U} <: ScalarOneStepFormula{U} p :: ScalarCondition{U} end -tree(d::ScalarUniversalFormula) = BoxRelationalOperator(d.relation)(Proposition(d.p)) +tree(f::ScalarUniversalFormula) = BoxRelationalOperator(f.relation)(Proposition(f.p)) function negation(formula::ScalarExistentialFormula{U}) where {U} ScalarUniversalFormula{U}( diff --git a/src/logisets/scalar/var-features.jl b/src/logisets/scalar/var-features.jl index 4e03844..631904d 100644 --- a/src/logisets/scalar/var-features.jl +++ b/src/logisets/scalar/var-features.jl @@ -48,6 +48,10 @@ See also [`AbstractWorld`](@ref). featvaltype(::Type{<:VarFeature{U}}) where {U} = U featvaltype(::VarFeature{U}) where {U} = U +# # TODO Necessary? +# Base.isequal(a::FT, b::FT) where {FT<:VarFeature} = Base.isequal(map(x->getfield(a, x), fieldnames(typeof(a))), map(x->getfield(b, x), fieldnames(typeof(b)))) +# Base.hash(a::VarFeature) = Base.hash(map(x->getfield(a, x), fieldnames(typeof(a)))) + Base.hash("") + """ computefeature(f::VarFeature{U}, featchannel; kwargs...)::U where {U} @@ -221,7 +225,8 @@ featurename(f::UnivariateNamedFeature) = f.name i_variable::Integer end -Notable univariate feature computing the minimum value for a given variable. +Simply the value of a scalar variable +(propositional case, when the frame has a single world). See also [`Interval`](@ref), [`Interval2D`](@ref), diff --git a/src/models/base.jl b/src/models/base.jl index 5804a92..77ec47a 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -517,7 +517,7 @@ the `ConstrainedModel` type is introduced: abstract type ConstrainedModel{O,FM<:AbstractModel} <: AbstractModel{O} end -For example, `ConstrainedModel{String, Union{Branch{String}, ConstantModel{String}}}` +For example, `ConstrainedModel{String,Union{Branch{String},ConstantModel{String}}}` supertypes models that with `String` outcomes that make use of `Branch{String}` and `ConstantModel{String}` (essentially, a decision trees with `String`s at the leaves). @@ -534,9 +534,9 @@ simply returns `FM`. See also [`ConstrainedModel`](@ref). """ -feasiblemodelstype(::Type{M}) where {O, M<:AbstractModel{O}} = AbstractModel{<:O} +feasiblemodelstype(::Type{M}) where {O,M<:AbstractModel{O}} = AbstractModel{<:O} feasiblemodelstype(::Type{M}) where {M<:AbstractModel} = AbstractModel -feasiblemodelstype(::Type{M}) where {O, M<:LeafModel{O}} = Union{} +feasiblemodelstype(::Type{M}) where {O,M<:LeafModel{O}} = Union{} feasiblemodelstype(::Type{M}) where {M<:LeafModel} = Union{} feasiblemodelstype(::Type{<:ConstrainedModel{O,FM}}) where {O,FM} = FM feasiblemodelstype(m::ConstrainedModel) = outcometype(typeof(m)) @@ -1220,7 +1220,7 @@ sub-tree of `Branch` and `LeafModel`: O, C<:AbstractBooleanCondition, FFM<:LeafModel - } <: ConstrainedModel{O, Union{<:Branch{<:O,<:C}, <:FFM}} + } <: ConstrainedModel{O,Union{<:Branch{<:O,<:C},<:FFM}} root::M where {M<:Union{FFM,Branch}} info::NamedTuple end @@ -1235,15 +1235,15 @@ struct DecisionTree{ O, C<:AbstractBooleanCondition, FFM<:LeafModel -} <: ConstrainedModel{O, Union{<:Branch{<:O,<:C}, <:FFM}} +} <: ConstrainedModel{O,Union{<:Branch{<:O,<:C}, <:FFM}} root::M where {M<:Union{FFM,Branch}} info::NamedTuple function DecisionTree( root::Union{FFM,Branch{O,C,Union{Branch{<:O,C2},FFM}}}, info::NamedTuple = (;), - ) where {O, C<:AbstractBooleanCondition, C2<:C, FFM<:LeafModel{<:O}} - new{O,C,FFM}(root, info) + ) where {O,C<:AbstractBooleanCondition,C2<:C,FFM<:LeafModel{<:O}} + new{O,root isa LeafModel ? AbstractBooleanCondition : C,FFM}(root, info) end function DecisionTree( @@ -1316,7 +1316,7 @@ A `Decision Forest` is a symbolic model that wraps an ensemble of models O, C<:AbstractBooleanCondition, FFM<:LeafModel - } <: ConstrainedModel{O, Union{<:Branch{<:O,<:C}, <:FFM}} + } <: ConstrainedModel{O,Union{<:Branch{<:O,<:C},<:FFM}} trees::Vector{<:DecisionTree} info::NamedTuple end @@ -1329,7 +1329,7 @@ struct DecisionForest{ O, C<:AbstractBooleanCondition, FFM<:LeafModel -} <: ConstrainedModel{O, Union{<:Branch{<:O,<:C}, <:FFM}} +} <: ConstrainedModel{O,Union{<:Branch{<:O,<:C},<:FFM}} trees::Vector{<:DecisionTree} info::NamedTuple diff --git a/src/models/print.jl b/src/models/print.jl index c0a4904..ca9ce4b 100644 --- a/src/models/print.jl +++ b/src/models/print.jl @@ -165,8 +165,8 @@ function displaymodel( #println(io, "$(pipe)$(antecedent(m))") println(io, "$(pipe)$(syntaxstring(antecedent(m); syntaxstring_kwargs...))") pad_str = indentation_str*repeat(" ", length(pipe)-length(indentation_last_space)+1) - print(io, "$(pad_str*indentation_last_first)$("✔ ")") - ind_str = pad_str*indentation_last_space*repeat(" ", length("✔ ")-length(indentation_last_space)+2) + print(io, "$(pad_str*indentation_last_first)$("✔")") + ind_str = pad_str*indentation_last_space*repeat(" ", length("✔")-length(indentation_last_space)+2) subm_str = @_display_submodel consequent(m) ind_str indentation depth max_depth show_subtree_info syntaxstring_kwargs kwargs print(io, subm_str) else @@ -206,7 +206,10 @@ function displaymodel( if isnothing(max_depth) || depth < max_depth pipe = "$(indentation_list_children) " println(io, "$(pipe)$(syntaxstring(antecedent(m); syntaxstring_kwargs...))") - for (consequent, indentation_flag_space, indentation_flag_first, f) in [(posconsequent(m), indentation_any_space, indentation_any_first, "✔ "), (negconsequent(m), indentation_last_space, indentation_last_first, "✘ ")] + for (consequent, indentation_flag_space, indentation_flag_first, f) in [ + (posconsequent(m), indentation_any_space, indentation_any_first, "✔ "), + (negconsequent(m), indentation_last_space, indentation_last_first, "✘ ") + ] # pad_str = indentation_str*indentation_flag_first**repeat(" ", length(pipe)-length(indentation_flag_first)) pad_str = "$(indentation_str*indentation_flag_first)$(f)" print(io, "$(pad_str)") diff --git a/src/other/_parse-dimensional-condition.jl b/src/other/_parse-dimensional-condition.jl deleted file mode 100644 index 4adc58f..0000000 --- a/src/other/_parse-dimensional-condition.jl +++ /dev/null @@ -1,205 +0,0 @@ -import SoleModels: parsecondition - -using StatsBase - -#= ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Code purpose ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Parse a string "min[V189] <= 250" into the corresponding ScalarCondition `parsecondition`. - -A ScalarCondition is built of three parts; for example, the ScalarCondition above has: - 1) feature -> UnivariateMin(189), - 2) test_operator -> <=, - 3) threshold -> 250, -which are assembled by `ScalarCondition(ScalarMetaCondition(feature, test_operator), threshold)`. - -`parsecondition` can be used within SoleLogics to recognize Proposition{ScalarCondition}, -when parsing a logical expression: - SoleLogics.parseformulatree( - "min[V189] <= 250 ∧ min[V37] > 20"; proposition_parser = parsecondition); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ =# - -""" -Aliases for specific features used for parsing `ScalarCondition`s. -""" -const BASE_FEATURE_ALIASES = Dict{String,Union{Type,Function}}( - # - "minimum" => UnivariateMin, - "min" => UnivariateMin, - "maximum" => UnivariateMax, - "max" => UnivariateMax, - # - "avg" => StatsBase.mean, - "mean" => StatsBase.mean, -) - -""" - parsecondition( - ::Type{ScalarCondition{VarFeature}}, - expression::String; - featvaltype::Type = $(DEFAULT_VARFEATVALTYPE), - opening_bracket::String = $(repr(UVF_OPENING_BRACKET)), - closing_bracket::String = $(repr(UVF_CLOSING_BRACKET)), - custom_feature_aliases = Dict{String,Union{Type,Function}}(), - variable_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, - variable_name_prefix::Union{Nothing,String} = nothing, - )::ScalarCondition - -Return a `ScalarCondition` which is the result of parsing `expression`. -This can be integrated with the `SoleLogics` parsing system (see Examples section). -Currently, this function can only parse `UnivariateFeature`s; for example, "min[V189]", -which is parsed as `UnivariateMin(189)`. - -With $(repr(UVF_OPENING_BRACKET)) and -$(repr(UVF_CLOSING_BRACKET)) for **opening\\_bracket** and -**closing\\_bracket**, respectively, -each `ScalarCondition` is shaped as follows: - - `"**feature[variable] test\\_operator threshold.**""` - -where: -- *feature* is the name of a Julia `Function` (such as `minimum` or `maximum`), - whose return type is `featvaltype`; -- *variable* is the name of a dataset variable; -- *test\\_operator* is a Julia `Function` for binary comparison (e.g., `[<=, >]`); -- *threshold* is a scalar value of type `featvaltype` to be compared with the - computed feature value. - -# Arguments -- `expression::String`: the string to be parsed; -- `featvaltype::Type`: type of the value wrapped by the feature; -- `opening_bracket::String = $(repr(UVF_OPENING_BRACKET))`: the feature's opening bracket; -- `closing_bracket::String = $(repr(UVF_CLOSING_BRACKET))`: the feature's closing bracket; -- `custom_feature_aliases = Dict{String,Union{Type,Function}}`: mapping from string - to feature types (or Julia `Function`s), for correctly recognizing - custom features/functions; - if not provided, `SoleModels.BASE_FEATURE_ALIASES` will be used. - -`variable_names_map`, `variable_name_prefix` can influence the -parsing of the variable name; please, refer to `variable_name` for their behavior. - -# Examples -```julia-repl -julia> syntaxstring(SoleModels.parsecondition(ScalarCondition{VarFeature}, "min[V1] <= 32")) -"min[V1] <= 32.0" - -julia> syntaxstring(parseformulatree("min[V1] <= 15 ∧ max[V1] >= 85"; proposition_parser=(x)->parsecondition(ScalarCondition{VarFeature}, x; featvaltype = Int64,))) -"min[V1] <= 15 ∧ max[V1] >= 85" -``` - -See also -[`variable_name`](@ref), -[`ScalarCondition`](@ref), -[`ScalarMetaCondition`](@ref), -[`parseformulatree`](@ref), -[`syntaxstring`](@ref). -""" -function parsecondition( - ::Type{ScalarCondition{VarFeature}}, - expression::String; - featvaltype::Union{Nothing,Type} = nothing, - opening_bracket::String = UVF_OPENING_BRACKET, - closing_bracket::String = UVF_CLOSING_BRACKET, - custom_feature_aliases = Dict{String,Union{Type,Function}}(), - variable_names_map::Union{Nothing,AbstractDict,AbstractVector} = nothing, - variable_name_prefix::Union{Nothing,String} = nothing, -) - @assert isnothing(variable_names_map) || isnothing(variable_name_prefix) "" * - "Cannot parse variable with both variable_names_map and variable_name_prefix. " * - "(expression = $(repr(expression)))" - - if isnothing(featvaltype) - featvaltype = DEFAULT_VARFEATVALTYPE - @warn "Please, specify a type for the feature values (featvaltype = ...). " * - "$(featvaltype) will be used, but note that this may raise type errors. " * - "(expression = $(repr(expression)))" - end - - @assert length(opening_bracket) == 1 || length(closing_bracket) - "Brackets must be single-character strings! " * - "$(repr(opening_bracket)) and $(repr(closing_bracket)) encountered." - - featdict = merge(BASE_FEATURE_ALIASES, custom_feature_aliases) - - (_feature, _variable, _test_operator, _threshold) = begin - # 4 slices are found initially in this order: - # 1) a feature name (e.g. "min"), - # 2) an variable inside feature's brackets (e.g. "[V189]"), - # 3) a test operator ("<=", ">=", "<" or ">"), - # 4) a threshold value. - # Regex is more or less: - # (\w*) *(\[.*\]) *(<=|>=|<|>) *(\d*). - variable_name_prefix = isnothing(variable_name_prefix) && - isnothing(variable_names_map) ? UVF_VARPREFIX : variable_name_prefix - variable_name_prefix = isnothing(variable_name_prefix) ? "" : variable_name_prefix - - r = Regex("^\\s*(\\w+)\\s*\\$(opening_bracket)\\s*$(variable_name_prefix)(\\S+)\\s*\\$(closing_bracket)\\s*([^\\s\\d]+)\\s*(\\S+)\\s*\$") - # r = Regex("^\\s*(\\w+)\\s*\\$(opening_bracket)\\s*$(variable_name_prefix)(\\S+)\\s*\\$(closing_bracket)\\s*(\\S+)\\s+(\\S+)\\s*\$") - slices = match(r, expression) - - # Assert for malformed strings (e.g. "123.4250.2") - @assert !isnothing(slices) && length(slices) == 4 "Could not parse condition from " * - "expression $(repr(expression))." - - slices = string.(slices) - (slices[1], slices[2], slices[3], slices[4]) - end - - threshold, featvaltype = begin - if isconcretetype(featvaltype) - threshold = tryparse(featvaltype, _threshold) - if isnothing(threshold) - error("Could not parse condition from " * - "expression `$expression`: could not parse " * - "$(repr(_threshold)) as $(featvaltype)") - end - threshold, featvaltype - else - threshold = nothing - # threshold = isnothing(threshold) ? tryparse(Int, _threshold) : threshold - threshold = isnothing(threshold) ? tryparse(Float64, _threshold) : threshold - if threshold isa featvaltype - @warn "Please, specify a concrete type for the feature values " * - "(featvaltype = ...); $(typeof(threshold)) was inferred." - else - error("Could not correctly infer feature value type from " * - "threshold $(repr(_threshold)) ($(typeof(threshold)) was inferred). " * - "Please, specify a concrete type for the feature values " * - "(featvaltype = ...).") - end - threshold, typeof(threshold) - end - end - - feature = begin - i_var = begin - if isnothing(variable_names_map) - parse(Int, _variable) - elseif variable_names_map isa Union{AbstractDict,AbstractVector} - findfirst(variable_names_map, variable) - else - error("Unexpected variable_names_map of type $(typeof(variable_names_map)) " * - "encountered.") - end - end - if haskey(featdict, _feature) - # If it is a known feature get it as - # a type (e.g., `UnivariateMin`), or Julia function (e.g., `minimum`). - feat_or_fun = featdict[_feature] - # If it is a function, wrap it into a UnivariateFeature - # otherwise, it is a feature, and it is used as a constructor. - if feat_or_fun isa Function - UnivariateFeature{featvaltype}(i_var, feat_or_fun) - else - feat_or_fun{featvaltype}(i_var) - end - else - # If it is not a known feature, interpret it as a Julia function, - # and wrap it into a UnivariateFeature. - f = eval(Meta.parse(_feature)) - UnivariateFeature{featvaltype}(i_var, f) - end - end - - test_operator = eval(Meta.parse(_test_operator)) - - return ScalarCondition(feature, test_operator, threshold) -end diff --git a/src/other/active-logiset.jl b/src/other/active-logiset.jl deleted file mode 100644 index 848d44a..0000000 --- a/src/other/active-logiset.jl +++ /dev/null @@ -1,273 +0,0 @@ - -""" - abstract type AbstractFeatureLookupSet{V,FR<:AbstractFrame} end - -Abstract type for feature lookup tables. -Structures of this type provide a feature value for each world of each instance -of a logiset. More specifically, if `featstruct isa AbstractFeatureLookupSet`, then -`featstruct[i, w, f]` is the value of feature `f` on world `w` on the `i`-th instance of the -dataset. - -See also -[`featvalue`](@ref). -""" -abstract type AbstractFeatureLookupSet{V,FR<:AbstractFrame} end - -featvaltype(::Type{<:AbstractFeatureLookupSet{V}}) where {V} = V -featvaltype(d::AbstractFeatureLookupSet) = featvaltype(typeof(d)) - -""" - @inline function featvalue( - featstruct :: AbstractFeatureLookupSet{V,FR}, - i_instance :: Integer, - w :: W, - feature :: FT, - ) where {V,FT<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} - -Return the feature value for `f` at world `w` on the `i`-th instance. - -See also -[`AbstractFeatureLookupSet`](@ref). -""" -function featvalue( - featstruct :: AbstractFeatureLookupSet{V,FR}, - i_instance :: Integer, - w :: W, - feature :: FT, -)::V where {V,FT<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} - error("Please, provide method featvalue(::$(typeof(featstruct)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), feature::$(typeof(feature)))::$(V).") -end - -""" - @inline function featvalue( - featstruct :: AbstractFeatureLookupSet{V,FR}, - i_instance :: Integer, - w :: W, - i_feature :: Integer, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} - -Return the value for the `i_feature`-th at world `w` on the `i`-th instance. - -See also -[`AbstractFeatureLookupSet`](@ref). -""" -function featvalue( - featstruct :: AbstractFeatureLookupSet{V,FR}, - i_instance :: Integer, - w :: W, - i_feature :: Integer, -)::V where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} - error("Please, provide method featvalue(::$(typeof(featstruct)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), i_feature::$(typeof(i_feature)))::$(V).") -end - -@inline Base.getindex(featstruct::AbstractFeatureLookupSet, args...) = featvalue(featstruct, args...) - -############################################################################################ -############################################################################################ -############################################################################################ - -# # The most generic featstruct structure is a matrix of dictionaries of size (ninstances × nfeatures) -# struct GenericFWD{ -# V, -# FT<:AbstractFeature, -# W<:AbstractWorld, -# FR<:AbstractFrame{W}, -# D<:AbstractVector{<:AbstractDict{<:W,<:AbstractVector{<:V}}} -# } <: AbstractFeatureLookupSet{V,FT,FR} -# d :: D -# nfeatures :: Integer -# end - -# Base.size(featstruct::GenericFWD{V}, args...) where {V} = size(featstruct.d, args...) -# ninstances(featstruct::GenericFWD{V}) where {V} = size(featstruct, 1) -# nfeatures(featstruct::GenericFWD{V}) where {V} = featstruct.d - -# # The matrix is initialized with #undef values -# function fwd_init(::Type{GenericFWD}, X::AbstractLogiset{W,V}) where {W,V} -# d = Array{Dict{W,V}, 2}(undef, ninstances(X)) -# for i in 1:ninstances -# d[i] = Dict{W,Array{V,1}}() -# end -# GenericFWD{V}(d, nfeatures(X)) -# end - -# # A function for initializing individual world slices -# function fwd_init_world_slice(featstruct::GenericFWD{V}, i_instance::Integer, w::AbstractWorld) where {V} -# featstruct.d[i_instance][w] = Array{V,1}(undef, featstruct.nfeatures) -# end - -# # A function for getting a threshold value from the lookup table -# Base.@propagate_inbounds @inline featvalue( -# featstruct :: GenericFWD{V}, -# i_instance :: Integer, -# w :: AbstractWorld, -# i_feature :: Integer) where {V} = featstruct.d[i_instance][w][i_feature] - -# # A function for setting a threshold value in the lookup table -# Base.@propagate_inbounds @inline function fwd_set(featstruct::GenericFWD{V}, w::AbstractWorld, i_instance::Integer, i_feature::Integer, threshold::V) where {V} -# featstruct.d[i_instance][w][i_feature] = threshold -# end - -# # A function for setting threshold values for a single feature (from a feature slice, experimental) -# Base.@propagate_inbounds @inline function fwd_set_feature(featstruct::GenericFWD{V}, i_feature::Integer, fwdslice::Any) where {V} -# error("Warning! fwd_set_feature with GenericFWD is not yet implemented!") -# for ((i_instance,w),threshold::V) in read_fwdslice(fwdslice) -# featstruct.d[i_instance][w][i_feature] = threshold -# end -# end - -# # A function for slicing the dataset -# function instances(featstruct::GenericFWD{V}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {V} -# GenericFWD{V}(if return_view == Val(true) @view featstruct.d[inds] else featstruct.d[inds] end, featstruct.nfeatures) -# end - -# Others... -# Base.@propagate_inbounds @inline fwdread_channeaoeu(featstruct::GenericFWD{V}, i_instance::Integer, i_feature::Integer) where {V} = TODO -# const GenericFeaturedChannel{V} = TODO -# readfeature(fwc::GenericFeaturedChannel{V}, w::AbstractWorld) where {V} = TODO - -# isminifiable(::AbstractFeatureLookupSet) = true - -# function minify(featstruct::AbstractFeatureLookupSet) -# minify(featstruct.d) #TODO improper -# end - -""" -Basic structure for representing active logisets, that is, logical datasets with features. ... TODO -It stores the feature values in a lookup table. - - -""" -struct ActiveLogiset{ - V, - W<:AbstractWorld, - FR<:AbstractFrame{W}, - FT<:AbstractFeature, - FWD<:AbstractFeatureLookupSet{V,FR}, -} <: AbstractLogiset{W,V,FT,Bool,FR} - - # Feature lookup structure - featstruct :: FWD - - # Features - features :: Vector{FT} - - # Accessibility relations - relations :: Vector{<:AbstractRelation} - - # Initial world(s) - initialworld :: Union{Nothing,W,AbstractWorldSet{<:W}} - - function ActiveLogiset{V,W,FR,FT,FWD}( - featstruct :: FWD, - features :: AbstractVector{FT}, - relations :: AbstractVector{<:AbstractRelation}, - ; - allow_no_instances = false, - initialworld = nothing, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W},FWD<:AbstractFeatureLookupSet{V,FR},FT<:AbstractFeature} - features = collect(features) - ty = "ActiveLogiset{$(V),$(W),$(FR),$(FT)}" - @assert allow_no_instances || ninstances(featstruct) > 0 "Cannot instantiate $(ty) with no instance. (featstruct's type $(typeof(featstruct)))" - @assert nfeatures(featstruct) == length(features) "Cannot instantiate $(ty) with different numbers of instances $(ninstances(featstruct)) and of features $(length(features))." - check_initialworld(ActiveLogiset, initialworld, W) - new{ - V, - W, - FR, - FT, - FWD, - }( - featstruct, - features, - relations, - initialworld, - ) - end - - function ActiveLogiset{V,W,FR}( - featstruct :: FWD, - features :: AbstractVector{<:AbstractFeature}, - args...; - kwargs... - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W},FWD<:AbstractFeatureLookupSet{V,FR}} - features = collect(features) - FT = Union{typeof.(features)...} - features = Vector{FT}(features) - ActiveLogiset{V,W,FR,FT,FWD}(featstruct, features, args...; kwargs...) - end - - function ActiveLogiset{V,W}( - featstruct :: AbstractFeatureLookupSet{V,FR}, - args...; - kwargs... - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} - ActiveLogiset{V,W,FR}(featstruct, args...; kwargs...) - end - - function ActiveLogiset( - featstruct :: AbstractFeatureLookupSet{V,FR}, - args...; - kwargs..., - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W}} - ActiveLogiset{V,W}(featstruct, args...; kwargs...) - end -end - - -@inline function Base.getindex(X::ActiveLogiset{V,W}, args...) where {V,W<:AbstractWorld} - Base.getindex(featstruct(X), args...)::V -end - -@inline function featvalue(X::ActiveLogiset{V,W}, args...) where {V,W<:AbstractWorld} - featvalue(featstruct(X), args...)::V -end - -Base.size(X::ActiveLogiset) = Base.size(featstruct(X)) - -featstruct(X::ActiveLogiset) = X.featstruct -relations(X::ActiveLogiset) = X.relations -features(X::ActiveLogiset) = X.features - -nfeatures(X::ActiveLogiset) = length(features(X)) -nrelations(X::ActiveLogiset) = length(relations(X)) -ninstances(X::ActiveLogiset) = ninstances(featstruct(X)) -worldtype(X::ActiveLogiset{V,W}) where {V,W<:AbstractWorld} = W - -frame(X::ActiveLogiset, i_instance::Integer) = frame(featstruct(X), i_instance) -initialworld(X::ActiveLogiset) = X.initialworld -function initialworld(X::ActiveLogiset, i_instance::Integer) - initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_instance] : initialworld(X) -end - -function instances(X::ActiveLogiset, inds::AbstractVector{<:Integer}, args...; kwargs...) - ActiveLogiset( - instances(featstruct(X), inds, args...; kwargs...), - features(X), - relations(X), - initialworld = initialworld(X) - ) -end - -function displaystructure(X::ActiveLogiset; indent_str = "") - out = "$(typeof(X))\t$(humansize(X))\n" - out *= indent_str * "├ features:\t\t$((length(features(X))))\t$(features(X))\n" - out *= indent_str * "├ relations:\t\t$((length(relations(X))))\t$(relations(X))\n" - out *= indent_str * "├ featstruct:\t\t$(typeof(featstruct(X)))\t$(Base.summarysize(featstruct(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "└ initialworld(s)\t$(initialworld(X))" - out -end - -hasnans(X::ActiveLogiset) = hasnans(featstruct(X)) - -isminifiable(::ActiveLogiset) = true - -function minify(X::ActiveLogiset) - new_fwd, backmap = minify(featstruct(X)) - X = ActiveLogiset( - new_fwd, - features(X), - relations(X), - ) - X, backmap -end diff --git a/src/other/dimensional-logiset.jl b/src/other/dimensional-logiset.jl deleted file mode 100644 index 672e9d0..0000000 --- a/src/other/dimensional-logiset.jl +++ /dev/null @@ -1,271 +0,0 @@ - -############################################################################################ -# Interpreted modal dataset -############################################################################################ -# -# A modal dataset can be instantiated in *implicit* form, from a dimensional domain, and a few -# objects inducing an interpretation on the domain; mainly, an ontology (determining worlds and -# relations), and structures for interpreting features onto the domain. -# -############################################################################################ - -struct DimensionalLogiset{ - V<:Number, - N, - W<:AbstractWorld, - D<:PassiveDimensionalLogiset{N,W}, - FT<:AbstractFeature, - G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, - G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, -} <: AbstractScalarLogiset{W,V,FT,FullDimensionalFrame{N,W}} - - # Core data (a dimensional domain) - domain :: D - - # Worlds & Relations - ontology :: Ontology{W} # Union{Nothing,} - - # Test operators associated with each feature, grouped by their respective aggregator - # Note: currently, cannot specify the full type (probably due to @computed) - grouped_featsaggrsnops :: G1 - - # Features and Aggregators - grouped_featsnaggrs :: G2 - - # Initial world(s) - initialworld :: Union{Nothing,W,AbstractWorldSet{<:W}} - - ######################################################################################## - - function DimensionalLogiset{V,N,W}( - domain::PassiveDimensionalLogiset{N}, - ontology::Ontology{W}, - features::AbstractVector{<:AbstractFeature}, - grouped_featsaggrsnops::AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}; - allow_no_instances = false, - initialworld = nothing, - ) where {V,N,W<:AbstractWorld} - ty = "DimensionalLogiset{$(V),$(N),$(W)}" - features = collect(features) - FT = Union{typeof.(features)...} - features = Vector{FT}(features) - @assert allow_no_instances || ninstances(domain) > 0 "" * - "Cannot instantiate $(ty) with no instance. (domain's type $(typeof(domain)))" - @assert length(features) == length(grouped_featsaggrsnops) "" * - "Cannot instantiate $(ty) with mismatching length(features) and " * - "length(grouped_featsaggrsnops): " * - "$(length(features)) != $(length(grouped_featsaggrsnops))" - @assert length(grouped_featsaggrsnops) > 0 && - sum(length.(grouped_featsaggrsnops)) > 0 && - sum(vcat([[length(test_ops) for test_ops in aggrs] for aggrs in grouped_featsaggrsnops]...)) > 0 "" * - "Cannot instantiate $(ty) with no test operator: $(grouped_featsaggrsnops)" - grouped_featsnaggrs = features_grouped_featsaggrsnops2grouped_featsnaggrs(features, grouped_featsaggrsnops) - check_initialworld(DimensionalLogiset, initialworld, W) - new{ - V, - N, - W, - typeof(domain), - FT, - typeof(grouped_featsaggrsnops), - typeof(grouped_featsnaggrs), - }( - domain, - ontology, - features, - grouped_featsaggrsnops, - grouped_featsnaggrs, - initialworld, - ) - end - - ######################################################################################## - - function DimensionalLogiset{V,N,W}( - domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, - ontology :: Ontology{W}, - features :: AbstractVector{<:AbstractFeature}, - grouped_featsnops :: AbstractVector; - kwargs..., - ) where {V,N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalLogiset{N,W}(domain) : domain) - grouped_featsaggrsnops = grouped_featsnops2grouped_featsaggrsnops(grouped_featsnops) - DimensionalLogiset{V,N,W}(domain, ontology, features, grouped_featsaggrsnops; kwargs...) - end - - function DimensionalLogiset{V,N,W}( - domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, - ontology :: Ontology{W}, - mixed_features :: AbstractVector; - kwargs..., - ) where {V,N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalLogiset{N,W}(domain) : domain) - - @assert all(isa.(mixed_features, MixedCondition)) "Unknown feature encountered! " * - "$(filter(f->!isa(f, MixedCondition), mixed_features)), " * - "$(typeof.(filter(f->!isa(f, MixedCondition), mixed_features)))" - - mixed_features = Vector{MixedCondition}(mixed_features) - - _features, featsnops = begin - _features = AbstractFeature[] - featsnops = Vector{<:TestOperator}[] - - # readymade features - cnv_feat(cf::AbstractFeature) = ([≥, ≤], cf) - cnv_feat(cf::Tuple{TestOperator,AbstractFeature}) = ([cf[1]], cf[2]) - # single-variable features - cnv_feat(cf::Any) = cf - cnv_feat(cf::CanonicalFeature) = cf - cnv_feat(cf::Function) = ([≥, ≤], cf) - cnv_feat(cf::Tuple{TestOperator,Function}) = ([cf[1]], cf[2]) - - mixed_features = cnv_feat.(mixed_features) - - readymade_cfs = filter(x-> - # isa(x, Tuple{<:AbstractVector{<:TestOperator},AbstractFeature}), - isa(x, Tuple{AbstractVector,AbstractFeature}), - mixed_features, - ) - variable_specific_cfs = filter(x-> - isa(x, CanonicalFeature) || - # isa(x, Tuple{<:AbstractVector{<:TestOperator},Function}) || - (isa(x, Tuple{AbstractVector,Function}) && !isa(x, Tuple{AbstractVector,AbstractFeature})), - mixed_features, - ) - - @assert length(readymade_cfs) + length(variable_specific_cfs) == length(mixed_features) "" * - "Unexpected " * - "mixed_features. $(mixed_features). " * - "$(filter(x->(! (x in readymade_cfs) && ! (x in variable_specific_cfs)), mixed_features)). " * - "$(length(readymade_cfs)) + $(length(variable_specific_cfs)) == $(length(mixed_features))." - - for (test_ops,cf) in readymade_cfs - push!(_features, cf) - push!(featsnops, test_ops) - end - - single_var_feats_n_featsnops(i_var,cf::SoleModels.CanonicalFeatureGeq) = ([≥],DimensionalDatasets.UnivariateMin{V}(i_var)) - single_var_feats_n_featsnops(i_var,cf::SoleModels.CanonicalFeatureLeq) = ([≤],DimensionalDatasets.UnivariateMax{V}(i_var)) - single_var_feats_n_featsnops(i_var,cf::SoleModels.CanonicalFeatureGeqSoft) = ([≥],DimensionalDatasets.UnivariateSoftMin{V}(i_var, cf.alpha)) - single_var_feats_n_featsnops(i_var,cf::SoleModels.CanonicalFeatureLeqSoft) = ([≤],DimensionalDatasets.UnivariateSoftMax{V}(i_var, cf.alpha)) - single_var_feats_n_featsnops(i_var,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},typeof(minimum)}) = (test_ops,DimensionalDatasets.UnivariateMin{V}(i_var)) - single_var_feats_n_featsnops(i_var,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},typeof(maximum)}) = (test_ops,DimensionalDatasets.UnivariateMax{V}(i_var)) - single_var_feats_n_featsnops(i_var,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},Function}) = (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_var, (x)->(V(cf(x))))) - single_var_feats_n_featsnops(i_var,::Any) = error("Unknown mixed_feature type: $(cf), $(typeof(cf))") - - for i_var in 1:nvariables(domain) - for (test_ops,cf) in map((cf)->single_var_feats_n_featsnops(i_var,cf),variable_specific_cfs) - push!(featsnops, test_ops) - push!(_features, cf) - end - end - _features, featsnops - end - DimensionalLogiset{V,N,worldtype(ontology)}(domain, ontology, _features, featsnops; kwargs...) - end - - ######################################################################################## - - function DimensionalLogiset{V,N}( - domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, - ontology :: Ontology{W}, - args...; - kwargs..., - ) where {V,N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalLogiset{N,W}(domain) : domain) - DimensionalLogiset{V,N,W}(domain, ontology, args...; kwargs...) - end - - ######################################################################################## - - function DimensionalLogiset{V}( - domain :: Union{PassiveDimensionalLogiset,AbstractDimensionalDataset}, - args...; - kwargs..., - ) where {V} - DimensionalLogiset{V,dimensionality(domain)}(domain, args...; kwargs...) - end - - ######################################################################################## - - function DimensionalLogiset( - domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, - ontology :: Ontology{W}, - features :: AbstractVector{<:AbstractFeature}, - args...; - kwargs..., - ) where {N,W<:AbstractWorld} - V = Union{featvaltype.(features)...} - DimensionalLogiset{V}(domain, ontology, features, args...; kwargs...) - end - - preserveseltype(::Any) = false - preserveseltype(::CanonicalFeature) = true - preserveseltype(::typeof(minimum)) = true # TODO fix - preserveseltype(::typeof(maximum)) = true # TODO fix - - function DimensionalLogiset( - domain :: Union{PassiveDimensionalLogiset{N,W},AbstractDimensionalDataset}, - ontology :: Ontology{W}, - mixed_features :: AbstractVector; - kwargs..., - ) where {N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalLogiset{dimensionality(domain),W}(domain) : domain) - @assert all((f)->(preserveseltype(f)), mixed_features) "Please, specify the feature output type V upon construction, as in: DimensionalLogiset{V}(...)." # TODO highlight and improve - V = eltype(domain) - DimensionalLogiset{V}(domain, ontology, mixed_features; kwargs...) - end - -end - -domain(X::DimensionalLogiset) = X.domain -ontology(X::DimensionalLogiset) = X.ontology -features(X::DimensionalLogiset) = X.features -grouped_featsaggrsnops(X::DimensionalLogiset) = X.grouped_featsaggrsnops -grouped_featsnaggrs(X::DimensionalLogiset) = X.grouped_featsnaggrs - -function Base.getindex(X::DimensionalLogiset, args...) - domain(X)[args...]::featvaltype(X) -end - -Base.size(X::DimensionalLogiset) = Base.size(domain(X)) - -dimensionality(X::DimensionalLogiset{V,N,W}) where {V,N,W} = N -worldtype(X::DimensionalLogiset{V,N,W}) where {V,N,W} = W - -ninstances(X::DimensionalLogiset) = ninstances(domain(X)) -nvariables(X::DimensionalLogiset) = nvariables(domain(X)) - -relations(X::DimensionalLogiset) = relations(ontology(X)) -nrelations(X::DimensionalLogiset) = length(relations(X)) -nfeatures(X::DimensionalLogiset) = length(features(X)) - -channelsize(X::DimensionalLogiset, args...) = channelsize(domain(X), args...) -maxchannelsize(X::DimensionalLogiset) = maxchannelsize(domain(X)) - -get_instance(X::DimensionalLogiset, args...) = get_instance(domain(X), args...) - -instances(X::DimensionalLogiset, inds::AbstractVector{<:Integer}, args...; kwargs...) = - DimensionalLogiset(instances(domain(X), inds, args...; kwargs...), ontology(X), features(X), grouped_featsaggrsnops(X); initialworld = initialworld(X)) - -frame(X::DimensionalLogiset, i_instance::Integer) = frame(domain(X), i_instance) -initialworld(X::DimensionalLogiset) = X.initialworld -function initialworld(X::DimensionalLogiset, i_instance::Integer) - initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_instance] : initialworld(X) -end - -function displaystructure(X::DimensionalLogiset; indent_str = "") - out = "$(typeof(X))\t$(humansize(X))\n" - out *= indent_str * "├ relations:\t($((nrelations(X))))\t[$(join(syntaxstring.(relations(X)), ", "))]\n" - out *= indent_str * "├ features:\t($((nfeatures(X))))\t[$(join(syntaxstring.(features(X)), ", "))]\n" - out *= indent_str * "├ domain shape:\t\t$(Base.size(domain(X)))\n" - out *= indent_str * "├ ninstances:\t\t$(ninstances(X))\n" - out *= indent_str * "├ nvariables:\t\t$(nvariables(X))\n" - out *= indent_str * "├ maxchannelsize:\t$(maxchannelsize(X))\n" - out *= indent_str * "└ initialworld(s):\t$(initialworld(X))" - out -end - -hasnans(X::DimensionalLogiset) = hasnans(domain(X)) - diff --git a/src/other/dimensional-ontologies.jl b/src/other/dimensional-ontologies.jl deleted file mode 100644 index 4e721fb..0000000 --- a/src/other/dimensional-ontologies.jl +++ /dev/null @@ -1,104 +0,0 @@ -############################################################################################ -# Ontologies -############################################################################################ - -# Here are the definitions for world types and relations for known modal logics -# - -get_ontology(N::Integer, args...) = get_ontology(Val(N), args...) -get_ontology(::Val{0}, args...) = OneWorldOntology -function get_ontology(::Val{1}, world = :interval, relations::Union{Symbol,AbstractVector{<:AbstractRelation}} = :IA) - world_possible_values = [:point, :interval, :rectangle, :hyperrectangle] - relations_possible_values = [:IA, :IA3, :IA7, :RCC5, :RCC8] - @assert world in world_possible_values "Unexpected value encountered for `world`: $(world). Legal values are in $(world_possible_values)" - @assert (relations isa AbstractVector{<:AbstractRelation}) || relations in relations_possible_values "Unexpected value encountered for `relations`: $(relations). Legal values are in $(relations_possible_values)" - - if world in [:point] - error("TODO point-based ontologies not implemented yet") - elseif world in [:interval, :rectangle, :hyperrectangle] - if relations isa AbstractVector{<:AbstractRelation} - Ontology{Interval{Int}}(relations) - elseif relations == :IA IntervalOntology - elseif relations == :IA3 Interval3Ontology - elseif relations == :IA7 Interval7Ontology - elseif relations == :RCC8 IntervalRCC8Ontology - elseif relations == :RCC5 IntervalRCC5Ontology - else - error("Unexpected value encountered for `relations`: $(relations). Legal values are in $(relations_possible_values)") - end - else - error("Unexpected value encountered for `world`: $(world). Legal values are in $(possible_values)") - end -end - -function get_ontology(::Val{2}, world = :interval, relations::Union{Symbol,AbstractVector{<:AbstractRelation}} = :IA) - world_possible_values = [:point, :interval, :rectangle, :hyperrectangle] - relations_possible_values = [:IA, :RCC5, :RCC8] - @assert world in world_possible_values "Unexpected value encountered for `world`: $(world). Legal values are in $(world_possible_values)" - @assert (relations isa AbstractVector{<:AbstractRelation}) || relations in relations_possible_values "Unexpected value encountered for `relations`: $(relations). Legal values are in $(relations_possible_values)" - - if world in [:point] - error("TODO point-based ontologies not implemented yet") - elseif world in [:interval, :rectangle, :hyperrectangle] - if relations isa AbstractVector{<:AbstractRelation} - Ontology{Interval2D{Int}}(relations) - elseif relations == :IA Interval2DOntology - elseif relations == :RCC8 Interval2DRCC8Ontology - elseif relations == :RCC5 Interval2DRCC5Ontology - else - error("Unexpected value encountered for `relations`: $(relations). Legal values are in $(relations_possible_values)") - end - else - error("Unexpected value encountered for `world`: $(world). Legal values are in $(possible_values)") - end -end - -############################################################################################ - -get_interval_ontology(N::Integer, args...) = get_interval_ontology(Val(N), args...) -get_interval_ontology(N::Val, relations::Union{Symbol,AbstractVector{<:AbstractRelation}} = :IA) = get_ontology(N, :interval, relations) - -############################################################################################ -# Worlds -############################################################################################ - -# Any world type W must provide an `interpret_world` method for interpreting a world -# onto a modal instance: -# interpret_world(::W, modal_instance) -# Note: for dimensional world types: modal_instance::AbstractArray - -############################################################################################ -# Dimensionality: 0 - -# Dimensional world type: it can be interpreted on dimensional instances. -interpret_world(::OneWorld, instance::AbstractArray{T,1}) where {T} = instance - -const OneWorldOntology = Ontology{OneWorld}(AbstractRelation[]) - -############################################################################################ -# Dimensionality: 1 - -# Dimensional world type: it can be interpreted on dimensional instances. -interpret_world(w::Interval2D, instance::AbstractArray{T,3}) where {T} = instance[w.x.x:w.x.y-1,w.y.x:w.y.y-1,:] - -const IntervalOntology = Ontology{Interval{Int}}(IARelations) -const Interval3Ontology = Ontology{Interval}(SoleLogics.IA3Relations) -const Interval7Ontology = Ontology{Interval}(SoleLogics.IA7Relations) - -const IntervalRCC8Ontology = Ontology{Interval{Int}}(RCC8Relations) -const IntervalRCC5Ontology = Ontology{Interval{Int}}(RCC5Relations) - -############################################################################################ -# Dimensionality: 2 - -# Dimensional world type: it can be interpreted on dimensional instances. -interpret_world(w::Interval, instance::AbstractArray{T,2}) where {T} = instance[w.x:w.y-1,:] - -const Interval2DOntology = Ontology{Interval2D{Int}}(IA2DRelations) -const Interval2DRCC8Ontology = Ontology{Interval2D{Int}}(RCC8Relations) -const Interval2DRCC5Ontology = Ontology{Interval2D{Int}}(RCC5Relations) - -############################################################################################ - -# get_ontology(::AbstractDimensionalDataset{T,D}, args...) where {T,D} = get_ontology(Val(D-2), args...) -# get_interval_ontology(::AbstractDimensionalDataset{T,D}, args...) where {T,D} = get_interval_ontology(Val(D-2), args...) diff --git a/src/other/main.jl b/src/other/main.jl deleted file mode 100644 index acd6e09..0000000 --- a/src/other/main.jl +++ /dev/null @@ -1,17 +0,0 @@ - -# include("dimensional-logiset.jl") - -# export parsecondition - -# # Conditions on features for dimensional datasets -# include("_parse-dimensional-condition.jl") - -# # Concrete type for ontologies -# include("ontology.jl") # TODO frame inside the ontology? - -# export DimensionalLogiset, Logiset, SupportedScalarLogiset - -# const GenericDataset = Union{AbstractDimensionalDataset,AbstractLogiset,MultiLogiset} - -# # Dimensional ontologies -# include("dimensional-ontologies.jl") diff --git a/src/other/ontology.jl b/src/other/ontology.jl deleted file mode 100644 index bcbb676..0000000 --- a/src/other/ontology.jl +++ /dev/null @@ -1,61 +0,0 @@ -using SoleLogics: AbstractRelation, AbstractWorld, FullDimensionalFrame -using SoleLogics: OneWorld, Interval, Interval2D -using SoleLogics: FullDimensionalFrame - -world2frametype = Dict([ - OneWorld => FullDimensionalFrame{0,OneWorld}, - Interval => FullDimensionalFrame{1,Interval{Int}}, - Interval2D => FullDimensionalFrame{2,Interval2D{Int}}, -]) - -# An ontology is a pair `world type` + `set of relations`, and represents the kind of -# modal frame that underlies a certain logic -struct Ontology{W<:AbstractWorld} - - relations :: AbstractVector{<:AbstractRelation} - - function Ontology{W}(_relations::AbstractVector) where {W<:AbstractWorld} - _relations = collect(unique(_relations)) - # for relation in _relations - # @assert goeswith(world2frametype[W], relation) "Cannot instantiate Ontology{$(W)} with relation $(relation)!" - # end - if W == OneWorld && length(_relations) > 0 - _relations = similar(_relations, 0) - @warn "Instantiating Ontology{$(W)} with empty set of relations!" - end - new{W}(_relations) - end - - Ontology(worldType::Type{<:AbstractWorld}, relations) = Ontology{worldType}(relations) -end - -worldtype(::Ontology{W}) where {W<:AbstractWorld} = W -relations(o::Ontology) = o.relations - -Base.show(io::IO, o::Ontology{W}) where {W<:AbstractWorld} = begin - if o == OneWorldOntology - print(io, "OneWorldOntology") - else - print(io, "Ontology{") - show(io, W) - print(io, "}(") - if issetequal(relations(o), SoleLogics.IARelations) - print(io, "IA") - elseif issetequal(relations(o), SoleLogics.IARelations_extended) - print(io, "IA_extended") - elseif issetequal(relations(o), SoleLogics.IA2DRelations) - print(io, "IA²") - elseif issetequal(relations(o), SoleLogics.IA2D_URelations) - print(io, "IA²_U") - elseif issetequal(relations(o), SoleLogics.IA2DRelations_extended) - print(io, "IA²_extended") - elseif issetequal(relations(o), SoleLogics.RCC8Relations) - print(io, "RCC8") - elseif issetequal(relations(o), SoleLogics.RCC5Relations) - print(io, "RCC5") - else - show(io, relations(o)) - end - print(io, ")") - end -end diff --git a/src/other/passive-dimensional-logiset.jl b/src/other/passive-dimensional-logiset.jl deleted file mode 100644 index 5e74a91..0000000 --- a/src/other/passive-dimensional-logiset.jl +++ /dev/null @@ -1,139 +0,0 @@ -using SoleData: slicedataset -import SoleData: get_instance, ninstances, nvariables, channelsize, maxchannelsize, dimensionality, eltype -using SoleData: AbstractDimensionalDataset, - UniformDimensionalDataset - -""" -Scalar logiset with of dimensionality `N`. - -See also -... -[`AbstractLogiset`](@ref). -""" -struct PassiveDimensionalLogiset{ - N, - W<:AbstractWorld, - DOM<:AbstractDimensionalDataset, - FR<:AbstractDimensionalFrame{N,W}, -} <: AbstractLogiset{W,U where U,FT where FT<:VarFeature,FR} - - d::DOM - - function PassiveDimensionalLogiset{N,W,DOM,FR}( - d::DOM, - ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset,FR<:AbstractDimensionalFrame{N,W}} - ty = "PassiveDimensionalLogiset{$(N),$(W),$(DOM),$(FR)}" - @assert N == dimensionality(d) "ERROR! Dimensionality mismatch: " * - "can't instantiate $(ty) with underlying structure" * - "$(DOM). $(N) == $(dimensionality(d)) should hold." - @assert SoleLogics.goeswithdim(W, N) "ERROR! Dimensionality mismatch: " * - "can't interpret worldtype $(W) on PassiveDimensionalLogiset" * - "of dimensionality = $(N)" - new{N,W,DOM,FR}(d) - end - - function PassiveDimensionalLogiset{N,W,DOM}( - d::DOM, - ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset} - FR = typeof(_frame(d)) - _W = worldtype(FR) - @assert W <: _W "This should hold: $(W) <: $(_W)" - PassiveDimensionalLogiset{N,_W,DOM,FR}(d) - end - - function PassiveDimensionalLogiset{N,W}( - d::DOM, - ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset} - PassiveDimensionalLogiset{N,W,DOM}(d) - end - - function PassiveDimensionalLogiset( - d::AbstractDimensionalDataset, - # worldtype::Type{<:AbstractWorld}, - ) - W = worldtype(_frame(d)) - PassiveDimensionalLogiset{dimensionality(d),W}(d) - end -end - -function featchannel( - X::PassiveDimensionalLogiset, - i_instance::Integer, - f::AbstractFeature, -) - get_instance(X.d, i_instance) -end - -function readfeature( - X::PassiveDimensionalLogiset, - featchannel::Any, - w::W, - f::AbstractFeature{U}, -) where {U,W<:AbstractWorld} - w_values = interpret_world(w, featchannel) - computefeature(f, w_values)::U -end - -ninstances(X::PassiveDimensionalLogiset) = ninstances(X.d) -frame(X::PassiveDimensionalLogiset, i_instance::Integer) = _frame(X.d, i_instance) - -function allfeatvalues( - X::PassiveDimensionalLogiset, - i_instance, - f, -) - [readfeature(X, featchannel(X, i_instance, f), w, f) for w in allworlds(X, i_instance)] -end - -Base.size(X::PassiveDimensionalLogiset) = Base.size(X.d) - -nvariables(X::PassiveDimensionalLogiset) = nvariables(X.d) -channelsize(X::PassiveDimensionalLogiset) = channelsize(X.d) -maxchannelsize(X::PassiveDimensionalLogiset) = maxchannelsize(X.d) -dimensionality(X::PassiveDimensionalLogiset) = dimensionality(X.d) -eltype(X::PassiveDimensionalLogiset) = eltype(X.d) - -get_instance(X::PassiveDimensionalLogiset, args...) = get_instance(X.d, args...) - -function instances( - X::PassiveDimensionalLogiset, - inds::AbstractVector{<:Integer}, - return_view::Union{Val{true},Val{false}} = Val(false); - kwargs... -) where {N,W} - PassiveDimensionalLogiset{N,W}(instances(X.d, inds, args...; kwargs...)) -end - -function concatdatasets(Xs::PassiveDimensionalLogiset{N,W}...) where {N,W} - PassiveDimensionalLogiset(concatdatasets([X.d for X in Xs]...)) -end - - -function displaystructure(Xm::PassiveDimensionalLogiset; indent_str = "", include_ninstances = true, include_nmetaconditions = true, include_nrelations = true) - padattribute(l,r) = string(l) * lpad(r,32+length(string(r))-(length(indent_str)+2+length(l))) - pieces = [] - push!(pieces, "PassiveDimensionalLogiset ($(memoizationinfo(Xm)), $(humansize(Xm)))") - push!(pieces, "$(padattribute("worldtype:", worldtype(Xm)))") - push!(pieces, "$(padattribute("featvaltype:", featvaltype(Xm)))") - push!(pieces, "$(padattribute("featuretype:", featuretype(Xm)))") - push!(pieces, "$(padattribute("frametype:", frametype(Xm)))") - push!(pieces, "$(padattribute("dimensionality:", dimensionality(Xm)))") - if include_ninstances - push!(pieces, "$(padattribute("# instances:", ninstances(Xm)))") - end - push!(pieces, "$(padattribute("# variables:", nvariables(Xm)))") - push!(pieces, "$(padattribute("channelsize:", channelsize(Xm)))") - push!(pieces, "$(padattribute("size × eltype:", "$(size(Xm.d)) × $(eltype(Xm.d))"))") - - return join(pieces, "\n$(indent_str)├ ", "\n$(indent_str)└ ") -end - -hasnans(X::PassiveDimensionalLogiset) = hasnans(X.d) - -worldtype(X::PassiveDimensionalLogiset{N,W}) where {N,W} = W - - -############################################################################################ - -_frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance::Integer) = _frame(X) -_frame(X::Union{UniformDimensionalDataset,AbstractArray}) = FullDimensionalFrame(channelsize(X)) diff --git a/src/other/scalar-logiset.jl b/src/other/scalar-logiset.jl deleted file mode 100644 index fbca24f..0000000 --- a/src/other/scalar-logiset.jl +++ /dev/null @@ -1,114 +0,0 @@ - -""" -Active scalar datasets are active logical datasets with scalar features. -""" -const AbstractScalarLogiset{ - W<:AbstractWorld, - V<:Number, - FT<:AbstractFeature{V}, - FR<:AbstractFrame{W} -} = AbstractLogiset{W,V,FT,FR} - -function grouped_featsaggrsnops(X::AbstractScalarLogiset) - return error("Please, provide method grouped_featsaggrsnops(::$(typeof(X))).") -end - -function check( - p::Proposition{<:ScalarCondition}, - X::AbstractScalarLogiset{W}, - i_instance::Integer, - w::W, -) where {W<:AbstractWorld} - cond = atom(p) - featval = featvalue(X, i_instance, w, feature(cond)) - apply_test_operator(test_operator(cond), featval, threshold(cond)) -end - -function grouped_metaconditions(X::AbstractScalarLogiset) - grouped_featsnops = grouped_featsaggrsnops2grouped_featsnops(grouped_featsaggrsnops(X)) - [begin - (feat,[ScalarMetaCondition(feat,op) for op in ops]) - end for (feat,ops) in zip(features(X),grouped_featsnops)] -end - -function alphabet(X::AbstractScalarLogiset) - conds = vcat([begin - thresholds = unique([ - X[i_instance, w, feature] - for i_instance in 1:ninstances(X) - for w in allworlds(X, i_instance) - ]) - [(mc, thresholds) for mc in metaconditions] - end for (feature, metaconditions) in grouped_metaconditions(X)]...) - C = ScalarCondition{featvaltype(X),featuretype(X),ScalarMetaCondition{featuretype(X)}} - BoundedScalarConditions{C}(collect(conds)) -end - - -# Convenience functions -function grouped_featsnops2grouped_featsaggrsnops( - grouped_featsnops::AbstractVector{<:AbstractVector{<:TestOperator}} -)::AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}} - grouped_featsaggrsnops = Dict{<:Aggregator,<:AbstractVector{<:TestOperator}}[] - for (i_feature, test_operators) in enumerate(grouped_featsnops) - aggrsnops = Dict{Aggregator,AbstractVector{<:TestOperator}}() - for test_operator in test_operators - aggregator = existential_aggregator(test_operator) - if (!haskey(aggrsnops, aggregator)) - aggrsnops[aggregator] = TestOperator[] - end - push!(aggrsnops[aggregator], test_operator) - end - push!(grouped_featsaggrsnops, aggrsnops) - end - grouped_featsaggrsnops -end - -function grouped_featsaggrsnops2grouped_featsnops( - grouped_featsaggrsnops::AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}} -)::AbstractVector{<:AbstractVector{<:TestOperator}} - grouped_featsnops = [begin - vcat(values(grouped_featsaggrsnops)...) - end for grouped_featsaggrsnops in grouped_featsaggrsnops] - grouped_featsnops -end - -function features_grouped_featsaggrsnops2featsnaggrs_grouped_featsnaggrs(features, grouped_featsaggrsnops) - featsnaggrs = Tuple{<:AbstractFeature,<:Aggregator}[] - grouped_featsnaggrs = AbstractVector{Tuple{<:Integer,<:Aggregator}}[] - i_featsnaggr = 1 - for (feat,aggrsnops) in zip(features, grouped_featsaggrsnops) - aggrs = [] - for aggr in keys(aggrsnops) - push!(featsnaggrs, (feat,aggr)) - push!(aggrs, (i_featsnaggr,aggr)) - i_featsnaggr += 1 - end - push!(grouped_featsnaggrs, aggrs) - end - featsnaggrs, grouped_featsnaggrs -end - -function features_grouped_featsaggrsnops2featsnaggrs(features, grouped_featsaggrsnops) - featsnaggrs = Tuple{<:AbstractFeature,<:Aggregator}[] - for (feat,aggrsnops) in zip(features, grouped_featsaggrsnops) - for aggr in keys(aggrsnops) - push!(featsnaggrs, (feat,aggr)) - end - end - featsnaggrs -end - -function features_grouped_featsaggrsnops2grouped_featsnaggrs(features, grouped_featsaggrsnops) - grouped_featsnaggrs = AbstractVector{Tuple{<:Integer,<:Aggregator}}[] - i_featsnaggr = 1 - for (feat,aggrsnops) in zip(features, grouped_featsaggrsnops) - aggrs = [] - for aggr in keys(aggrsnops) - push!(aggrs, (i_featsnaggr,aggr)) - i_featsnaggr += 1 - end - push!(grouped_featsnaggrs, aggrs) - end - grouped_featsnaggrs -end diff --git a/src/other/test-dimensional-datasets.jl b/src/other/test-dimensional-datasets.jl deleted file mode 100644 index 30856ea..0000000 --- a/src/other/test-dimensional-datasets.jl +++ /dev/null @@ -1,50 +0,0 @@ -using Test -using StatsBase -using SoleLogics -using SoleModels -using SoleModels.DimensionalDatasets - -X = Array(reshape(1.0:180.0, 3,3,2,10)) - -ontology = get_interval_ontology(2) -@test_nowarn DimensionalLogiset{Float64}(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]) - -dfd = @test_nowarn DimensionalLogiset(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]) - -@test_nowarn DimensionalLogiset{Float64}(X, ontology, [minimum, maximum]) -dfd2 = @test_nowarn DimensionalLogiset(X, ontology, [minimum, maximum]) - -@test_throws AssertionError DimensionalLogiset(X, ontology, [StatsBase.mean]) - - -@test_nowarn dfd |> alphabet |> propositions -@test_nowarn length(alphabet(dfd)) -@test_nowarn length(collect(propositions(alphabet(dfd)))) -@test length(collect(propositions(alphabet(dfd))))*2 == length(collect(propositions(alphabet(dfd2)))) - -@test all(((propositions(dfd |> SupportedScalarLogiset |> alphabet))) .== - ((propositions(dfd |> alphabet)))) - - -dfd3 = @test_nowarn DimensionalLogiset{Float64}(X, ontology, [SoleModels.canonical_geq, SoleModels.canonical_leq]; initialworld = Interval2D((2,3),(2,3))) - -check(SyntaxTree(⊤), dfd, 1) -check(SyntaxTree(⊤), dfd2, 1) -check(SyntaxTree(⊤), dfd3, 1) - - - -X = Array(reshape(1.0:180.0, 3,3,2,10)); - - -d = DimensionalLogiset{UInt16}( -X, -get_ontology(2, :interval, :RCC5), -[minimum, maximum]; -initialworld = SoleLogics.Interval2D((2,3),(2,3)), -) -@assert SoleLogics.Interval2D((2,3),(2,3)) == initialworld(d) -@assert SoleLogics.Interval2D((2,3),(2,3)) == initialworld(d, 1) - - - diff --git a/test/logisets/MLJ.jl b/test/logisets/MLJ.jl new file mode 100644 index 0000000..77bee4c --- /dev/null +++ b/test/logisets/MLJ.jl @@ -0,0 +1,28 @@ +using MLJBase + +_nvars = 2 +n_instances = 20 + +multidataset, multirelations = collect.(zip([ + (Array(reshape(1.0:40.0, _nvars,n_instances)), [globalrel]), + (Array(reshape(1.0:120.0, 3,_nvars,n_instances)), [IARelations..., globalrel]), + (Array(reshape(1.0:360.0, 3,3,_nvars,n_instances)), [IA2DRelations..., globalrel]), + ]...)) + +multilogiset = @test_nowarn scalarlogiset(multidataset) +multilogiset = scalarlogiset(multidataset; relations = multirelations, conditions = vcat([[SoleModels.ScalarMetaCondition(UnivariateMin(i), >), SoleModels.ScalarMetaCondition(UnivariateMax(i), <)] for i in 1:_nvars]...)) + +@test_nowarn X = modality(multilogiset, 1) +@test_nowarn selectrows(X, 1:10) +@test_nowarn selectrows(multilogiset, 1:10) +@test_nowarn selectrows(SoleModels.base(X), 1:10) +@test_nowarn X = modality(multilogiset, 2) +@test_nowarn selectrows(SoleModels.base(X), 1:10) + +mod2 = modality(multilogiset, 2) +mod2_part = modality(MLJBase.partition(multilogiset, 0.8)[1], 2) +check(SyntaxTree(Proposition(ScalarCondition(UnivariateMin(2), >, 301))), mod2_part, 1, SoleModels.Interval(1,2)) +check((DiamondRelationalOperator(IA_L)(Proposition(ScalarCondition(UnivariateMin(2), >, 0)))), mod2_part, 1, SoleModels.Interval(1,2)) + +@test mod2_part != MLJBase.partition(mod2, 0.8)[1] +@test nmemoizedvalues(mod2_part) == nmemoizedvalues(MLJBase.partition(mod2, 0.8)[1]) diff --git a/test/runtests.jl b/test/runtests.jl index a711232..a573c87 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,6 +21,7 @@ test_suites = [ "logisets/cube2logiset.jl", "logisets/dataframe2logiset.jl", "logisets/multilogisets.jl", + "logisets/MLJ.jl", ]), ("Models", ["base.jl", ]), ("Miscellaneous", ["misc.jl", "minify.jl"]), From bc24e9176f35b17033f39f25e65d185e8a7b9eb4 Mon Sep 17 00:00:00 2001 From: Federico Manzella Date: Fri, 30 Jun 2023 14:16:50 +0200 Subject: [PATCH 21/77] Add Documentation.yml --- .github/workflows/Documentation.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/Documentation.yml diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml new file mode 100644 index 0000000..af505d2 --- /dev/null +++ b/.github/workflows/Documentation.yml @@ -0,0 +1,22 @@ +name: Documentation +on: + push: + branches: + - main + tags: '*' + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@latest + with: + version: '1.9' + - name: Install dependencies + run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' + - name: Build and deploy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token + run: julia --project=docs/ docs/make.jl From 265b60c3a8af53cd10c2c5e3cda0f6b675f75f83 Mon Sep 17 00:00:00 2001 From: Federico Manzella Date: Fri, 30 Jun 2023 14:18:14 +0200 Subject: [PATCH 22/77] Update make.jl --- docs/make.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 075cde9..f2cb098 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -5,7 +5,7 @@ DocMeta.setdocmeta!(SoleModels, :DocTestSetup, :(using SoleModels); recursive=tr makedocs(; modules=[SoleModels], - authors="Eduard I. STAN, Giovanni PAGLIARINI", + authors="Giovanni Pagliarini, Eduard I. Stan", repo="https://github.com/aclai-lab/SoleModels.jl/blob/{commit}{path}#{line}", sitename="SoleModels.jl", format=Documenter.HTML(; @@ -20,4 +20,8 @@ makedocs(; deploydocs(; repo="github.com/aclai-lab/SoleModels.jl", + devbranch = "main", + target = "build", + branch = "gh-pages", + versions = ["stable" => "v^", "v#.#"], ) From f5d848af72865a108d6d9ead1a409f6b85a4adea Mon Sep 17 00:00:00 2001 From: Federico Manzella Date: Fri, 30 Jun 2023 14:21:35 +0200 Subject: [PATCH 23/77] Update Manifest.toml --- docs/Manifest.toml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index a577863..08fc238 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -539,13 +539,11 @@ version = "1.0.1" [[Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -[[SoleBase]] -deps = ["Logging", "Random"] -git-tree-sha1 = "c6c3042fd61c7b7162ddf16b69939bdace7d5e30" -repo-rev = "dev" -repo-url = "https://github.com/aclai-lab/SoleBase.jl" +[[deps.SoleBase]] +deps = ["IterTools", "Logging", "Random"] +git-tree-sha1 = "4f941f62f7864f253e9063256dcef0fab72a44f4" uuid = "4475fa32-7023-44a0-aa70-4813b230e492" -version = "0.1.0" +version = "0.9.1" [[SoleData]] deps = ["CSV", "Catch22", "DataFrames", "DataStructures", "Logging", "Random", "Reexport", "ScientificTypes", "SoleBase", "Statistics"] From fafa30782895d9774c2690632f0e4540326794e0 Mon Sep 17 00:00:00 2001 From: ferdiu Date: Fri, 30 Jun 2023 14:31:51 +0200 Subject: [PATCH 24/77] fix Project.toml Signed-off-by: ferdiu --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index 2c8af33..39a7385 100644 --- a/Project.toml +++ b/Project.toml @@ -23,7 +23,6 @@ Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" ThreadSafeDicts = "4239201d-c60e-5e0a-9702-85d713665ba7" [compat] -BenchmarkTools = "1" julia = "1" BenchmarkTools = "1" ComputedFieldTypes = "1" From a5833a929e623dedc1c94a2bf2d0d1ae63ba3acf Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 4 Jul 2023 15:29:34 +0200 Subject: [PATCH 25/77] Fix dependencies --- Project.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Project.toml b/Project.toml index a7dc0c7..ba4e8da 100644 --- a/Project.toml +++ b/Project.toml @@ -24,6 +24,9 @@ ThreadSafeDicts = "4239201d-c60e-5e0a-9702-85d713665ba7" [compat] BenchmarkTools = "1" +SoleBase = "0.9.2" +SoleData = "0.9.0" +SoleLogics = "0.2.1" julia = "1" [extras] From 773a1529905fc3e9ef1ef9cf43c49dd94585439a Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:07:37 +0200 Subject: [PATCH 26/77] Fixes. Add leafmetrics, add displaymodel parameters --- src/SoleModels.jl | 4 +- src/logisets/MLJ-interface.jl | 4 +- src/logisets/check-modes.jl | 2 +- src/logisets/conditions.jl | 6 +- .../dimensional-structures/computefeature.jl | 5 +- .../dimensional-structures/logiset.jl | 32 +-- src/logisets/dimensional-structures/main.jl | 5 +- src/logisets/features.jl | 5 +- src/logisets/logiset.jl | 26 +-- src/logisets/memosets.jl | 6 +- src/logisets/multilogiset.jl | 31 ++- src/logisets/scalar/conditions.jl | 10 +- src/logisets/scalar/dataset-bindings.jl | 25 ++- src/logisets/scalar/memosets.jl | 7 +- src/logisets/scalar/onestep-memoset.jl | 24 +-- src/logisets/scalar/var-features.jl | 6 +- src/logisets/supported-logiset.jl | 7 +- src/models/base.jl | 15 +- .../{rule-evaluation.jl => evaluation.jl} | 21 ++ src/models/print.jl | 189 ++++++++++++++---- src/models/symbolic-utils.jl | 100 ++++----- test/base.jl | 16 +- test/logisets/cube2logiset.jl | 2 +- test/logisets/dataframe2logiset.jl | 2 +- test/logisets/logisets.jl | 6 +- test/logisets/multilogisets.jl | 2 +- test/misc.jl | 28 +-- 27 files changed, 369 insertions(+), 217 deletions(-) rename src/models/{rule-evaluation.jl => evaluation.jl} (87%) diff --git a/src/SoleModels.jl b/src/SoleModels.jl index d4bbbaa..7abd637 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -50,9 +50,9 @@ export Label, bestguess include("machine-learning.jl") -export rulemetrics +export rulemetrics, leafmetrics -include("models/rule-evaluation.jl") +include("models/evaluation.jl") export minify, isminifiable diff --git a/src/logisets/MLJ-interface.jl b/src/logisets/MLJ-interface.jl index 2dc8155..aaca5c3 100644 --- a/src/logisets/MLJ-interface.jl +++ b/src/logisets/MLJ-interface.jl @@ -96,9 +96,9 @@ using MLJBase using MLJModelInterface import MLJModelInterface: selectrows, _selectrows -# From MLJModelInferface.jl/src/data_utils.jl -function MLJModelInterface.selectrows(::MLJBase.FI, ::Val{:table}, X::Union{AbstractLogiset,MultiLogiset}, r) +function MLJModelInterface.selectrows(X::Union{AbstractLogiset,MultiLogiset}, r) r = r isa Integer ? (r:r) : r + # return slicedataset(X, r; return_view = true) return Tables.subset(X, r) end diff --git a/src/logisets/check-modes.jl b/src/logisets/check-modes.jl index 2cbc2b0..becf1e8 100644 --- a/src/logisets/check-modes.jl +++ b/src/logisets/check-modes.jl @@ -92,7 +92,7 @@ end # tree(f::AnchoredFormula) = tree(f.formula) function SoleLogics.tree(f::AnchoredFormula) - error("Cannot convert object of type AnchoredFormula to a SyntaxTree.") + return error("Cannot convert object of type AnchoredFormula to a SyntaxTree.") end function syntaxstring(f::AnchoredFormula; kwargs...) diff --git a/src/logisets/conditions.jl b/src/logisets/conditions.jl index e658071..17949e9 100644 --- a/src/logisets/conditions.jl +++ b/src/logisets/conditions.jl @@ -22,7 +22,7 @@ abstract type AbstractCondition{FT<:AbstractFeature} end # Check a condition (e.g, on a world of a logiset instance) function checkcondition(c::AbstractCondition, args...; kwargs...) - error("Please, provide method checkcondition(::$(typeof(c)), " * + return error("Please, provide method checkcondition(::$(typeof(c)), " * join(map(t->"::$(t)", typeof.(args)), ", ") * "; kwargs...). " * "Note that this value must be unique.") end @@ -37,7 +37,7 @@ end # end function syntaxstring(c::AbstractCondition; kwargs...) - error("Please, provide method syntaxstring(::$(typeof(c)); kwargs...). " * + return error("Please, provide method syntaxstring(::$(typeof(c)); kwargs...). " * "Note that this value must be unique.") end @@ -55,7 +55,7 @@ function parsecondition( expression::String; kwargs... ) - error("Please, provide method parsecondition(::$(Type{C}), expression::$(typeof(expression)); kwargs...).") + return error("Please, provide method parsecondition(::$(Type{C}), expression::$(typeof(expression)); kwargs...).") end ############################################################################################ diff --git a/src/logisets/dimensional-structures/computefeature.jl b/src/logisets/dimensional-structures/computefeature.jl index 61c6742..e754d84 100644 --- a/src/logisets/dimensional-structures/computefeature.jl +++ b/src/logisets/dimensional-structures/computefeature.jl @@ -16,10 +16,11 @@ function computefeature(f::MultivariateFeature{U}, featchannel::Any)::U where {U end function computeunivariatefeature(f::UnivariateFeature{U}, varchannel::Union{T,AbstractArray{T}}) where {U,T} - (f.f(SoleBase.vectorize(varchannel);))::U + # (f.f(SoleBase.vectorize(varchannel);))::U + (f.f(varchannel))::U end function computeunivariatefeature(f::UnivariateNamedFeature, varchannel::Union{T,AbstractArray{T}}) where {T} - error("Cannot intepret UnivariateNamedFeature on any structure at all.") + return error("Cannot intepret UnivariateNamedFeature on any structure at all.") end function computeunivariatefeature(f::UnivariateValue{U}, varchannel::Union{T,AbstractArray{T}}) where {U<:Real,T} (varchannel isa U ? varchannel : first(varchannel))::U diff --git a/src/logisets/dimensional-structures/logiset.jl b/src/logisets/dimensional-structures/logiset.jl index 2bc9d68..746a021 100644 --- a/src/logisets/dimensional-structures/logiset.jl +++ b/src/logisets/dimensional-structures/logiset.jl @@ -12,11 +12,11 @@ See also abstract type AbstractUniformFullDimensionalLogiset{U,N,W<:AbstractWorld,FT<:AbstractFeature,FR<:FullDimensionalFrame{N,W}} <: AbstractLogiset{W,U,FT,FR} end function maxchannelsize(X::AbstractUniformFullDimensionalLogiset) - error("Please, provide method maxchannelsize(::$(typeof(X))).") + return error("Please, provide method maxchannelsize(::$(typeof(X))).") end function channelsize(X::AbstractUniformFullDimensionalLogiset) - error("Please, provide method channelsize(::$(typeof(X))).") + return error("Please, provide method channelsize(::$(typeof(X))).") end function dimensionality(X::AbstractUniformFullDimensionalLogiset{U,N}) where {U,N} @@ -112,7 +112,7 @@ Base.@propagate_inbounds @inline function featchannel( i_feature :: Union{Nothing,Integer} = nothing ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -127,7 +127,7 @@ Base.@propagate_inbounds @inline function featvalues!( i_feature :: Union{Nothing,Integer} = nothing, ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -152,7 +152,7 @@ Base.@propagate_inbounds @inline function featchannel( i_feature :: Union{Nothing,Integer} = nothing ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -167,7 +167,7 @@ Base.@propagate_inbounds @inline function featvalues!( i_feature :: Union{Nothing,Integer} = nothing, ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -192,7 +192,7 @@ Base.@propagate_inbounds @inline function featchannel( i_feature :: Union{Nothing,Integer} = nothing ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -207,7 +207,7 @@ Base.@propagate_inbounds @inline function featvalues!( i_feature :: Union{Nothing,Integer} = nothing, ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -234,7 +234,7 @@ end i_feature :: Union{Nothing,Integer} = nothing ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -251,7 +251,7 @@ end i_feature :: Union{Nothing,Integer} = nothing ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -268,7 +268,7 @@ end i_feature :: Union{Nothing,Integer} = nothing ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -288,7 +288,7 @@ end i_feature :: Union{Nothing,Integer} = nothing ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -306,7 +306,7 @@ end i_feature :: Union{Nothing,Integer} = nothing ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -324,7 +324,7 @@ end i_feature :: Union{Nothing,Integer} = nothing ) where {U} if isnothing(i_feature) - i_feature = findfirst(isequal(feature), features(X)) + i_feature = _findfirst(isequal(feature), features(X)) if isnothing(i_feature) error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end @@ -345,7 +345,7 @@ function allfeatvalues( X::UniformFullDimensionalLogiset, i_instance, ) - error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), f::$(typeof(f))).") + return error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), f::$(typeof(f))).") end function allfeatvalues( @@ -353,7 +353,7 @@ function allfeatvalues( i_instance, f, ) - error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), f::$(typeof(f))).") + return error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), f::$(typeof(f))).") end ############################################################################################ diff --git a/src/logisets/dimensional-structures/main.jl b/src/logisets/dimensional-structures/main.jl index 878d981..91a2f76 100644 --- a/src/logisets/dimensional-structures/main.jl +++ b/src/logisets/dimensional-structures/main.jl @@ -1,8 +1,11 @@ module DimensionalDatasets - import Base: size, show, getindex, iterate, length, push!, eltype +# TODO remove +using SoleModels: _in, _findfirst + + using BenchmarkTools using ProgressMeter using UniqueVectors diff --git a/src/logisets/features.jl b/src/logisets/features.jl index 894e5bb..13caa30 100644 --- a/src/logisets/features.jl +++ b/src/logisets/features.jl @@ -12,7 +12,7 @@ See also [`VarFeature`](@ref), [`featvaltype`](@ref), [`AbstractWorld`](@ref). abstract type AbstractFeature end function syntaxstring(f::AbstractFeature; kwargs...) - error("Please, provide method syntaxstring(::$(typeof(f)); kwargs...)." + return error("Please, provide method syntaxstring(::$(typeof(f)); kwargs...)." * " Note that this value must be unique.") end @@ -30,7 +30,8 @@ function parsefeature( expression::String; kwargs... ) - error("Please, provide method parsefeature(::$(FT), expression::$(typeof(expression)); kwargs...).") + return error("Please, provide method parsefeature(::$(FT), " * + " expression::$(typeof(expression)); kwargs...).") end ############################################################################################ diff --git a/src/logisets/logiset.jl b/src/logisets/logiset.jl index 15a8ab2..351e028 100644 --- a/src/logisets/logiset.jl +++ b/src/logisets/logiset.jl @@ -37,7 +37,7 @@ function featchannel( i_instance::Integer, feature::AbstractFeature, ) where {W<:AbstractWorld} - error("Please, provide method featchannel(::$(typeof(X)), i_instance::$(typeof(i_instance)), feature::$(typeof(feature))).") + return error("Please, provide method featchannel(::$(typeof(X)), i_instance::$(typeof(i_instance)), feature::$(typeof(feature))).") end function readfeature( @@ -46,7 +46,7 @@ function readfeature( w::W, feature::AbstractFeature, ) where {W<:AbstractWorld} - error("Please, provide method readfeature(::$(typeof(X)), featchannel::$(typeof(featchannel)), w::$(typeof(w)), feature::$(typeof(feature))).") + return error("Please, provide method readfeature(::$(typeof(X)), featchannel::$(typeof(featchannel)), w::$(typeof(w)), feature::$(typeof(feature))).") end """ @@ -68,7 +68,7 @@ function featvalue!( w::W, feature::AbstractFeature, ) where {W<:AbstractWorld} - error("Please, provide method featvalue!(::$(typeof(X)), featval::$(typeof(featval)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), feature::$(typeof(feature))).") + return error("Please, provide method featvalue!(::$(typeof(X)), featval::$(typeof(featval)), i_instance::$(typeof(i_instance)), w::$(typeof(w)), feature::$(typeof(feature))).") end function featvalues!( @@ -76,22 +76,22 @@ function featvalues!( featslice, feature::AbstractFeature, ) where {W<:AbstractWorld} - error("Please, provide method featvalues!(::$(typeof(X)), featslice::$(typeof(featslice)), feature::$(typeof(feature))).") + return error("Please, provide method featvalues!(::$(typeof(X)), featslice::$(typeof(featslice)), feature::$(typeof(feature))).") end function frame(X::AbstractLogiset, i_instance::Integer) - error("Please, provide method frame(::$(typeof(X)), i_instance::$(typeof(i_instance))).") + return error("Please, provide method frame(::$(typeof(X)), i_instance::$(typeof(i_instance))).") end function ninstances(X::AbstractLogiset) - error("Please, provide method ninstances(::$(typeof(X))).") + return error("Please, provide method ninstances(::$(typeof(X))).") end function allfeatvalues( X::AbstractLogiset, i_instance, ) - error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance))).") + return error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance))).") end function allfeatvalues( @@ -99,7 +99,7 @@ function allfeatvalues( i_instance, feature, ) - error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), feature::$(typeof(feature))).") + return error("Please, provide method allfeatvalues(::$(typeof(X)), i_instance::$(typeof(i_instance)), feature::$(typeof(feature))).") end function instances( @@ -108,15 +108,15 @@ function instances( return_view::Union{Val{true},Val{false}} = Val(false); kwargs... ) - error("Please, provide method instances(::$(typeof(X)), ::$(typeof(inds)), ::$(typeof(return_view))).") + return error("Please, provide method instances(::$(typeof(X)), ::$(typeof(inds)), ::$(typeof(return_view))).") end function concatdatasets(Xs::AbstractLogiset...) - error("Please, provide method concatdatasets(X...::$(typeof(Xs))).") + return error("Please, provide method concatdatasets(X...::$(typeof(Xs))).") end function displaystructure(X::AbstractLogiset; kwargs...)::String - error("Please, provide method displaystructure(X::$(typeof(X)); kwargs...)::String.") + return error("Please, provide method displaystructure(X::$(typeof(X)); kwargs...)::String.") end isminifiable(::AbstractLogiset) = false @@ -153,11 +153,11 @@ representatives(X::AbstractLogiset, i_instance::Integer, args...) = representati # Non mandatory function features(X::AbstractLogiset) - error("Please, provide method features(::$(typeof(X))).") + return error("Please, provide method features(::$(typeof(X))).") end function nfeatures(X::AbstractLogiset) - error("Please, provide method nfeatures(::$(typeof(X))).") + return error("Please, provide method nfeatures(::$(typeof(X))).") end ############################################################################################ diff --git a/src/logisets/memosets.jl b/src/logisets/memosets.jl index 6375ad9..5f73635 100644 --- a/src/logisets/memosets.jl +++ b/src/logisets/memosets.jl @@ -22,15 +22,15 @@ abstract type AbstractMemoset{ } <: AbstractLogiset{W,U,FT,FR} end function capacity(Xm::AbstractMemoset) - error("Please, provide method capacity(::$(typeof(Xm))).") + return error("Please, provide method capacity(::$(typeof(Xm))).") end function nmemoizedvalues(Xm::AbstractMemoset) - error("Please, provide method nmemoizedvalues(::$(typeof(Xm))).") + return error("Please, provide method nmemoizedvalues(::$(typeof(Xm))).") end function nonnothingshare(Xm::AbstractMemoset) - (isinf(capacity(Xm)) ? NaN : nmemoizedvalues(Xm)/capacity(Xm)) + return (isinf(capacity(Xm)) ? NaN : nmemoizedvalues(Xm)/capacity(Xm)) end function memoizationinfo(Xm::AbstractMemoset) diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index 338036f..bcb01f4 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -60,12 +60,12 @@ frametype(X::MultiLogiset, i_modality::Integer) = frametype(modality(X, i_modal frametypes(X::MultiLogiset) = Vector{Type{<:AbstractWorld}}(frametype.(eachmodality(X))) function instances( - X::MultiLogiset{L}, + X::MultiLogiset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false); kwargs... -) where {L<:AbstractLogiset} - MultiLogiset{L}(Vector{L}(map(modality->instances(modality, inds, return_view; kwargs...), eachmodality(X)))) +) + MultiLogiset(map(modality->instances(modality, inds, return_view; kwargs...), eachmodality(X))) end function concatdatasets(Xs::MultiLogiset...) @@ -183,11 +183,30 @@ function MultiFormula(i_modality, formula::SyntaxTree) end function SoleLogics.tree(f::MultiFormula) - error("Cannot convert object of type MultiFormula to a SyntaxTree.") + return error("Cannot convert object of type MultiFormula to a SyntaxTree.") end -function syntaxstring(f::MultiFormula; kwargs...) - join(["{$(i_modality)}($(syntaxstring(f.formulas[i_modality])))" for i_modality in sort(collect(keys(f.formulas)))], " ∧ ") +function syntaxstring( + f::MultiFormula; + hidemodality = false, + variable_names_map::Union{Nothing,AbstractDict,AbstractVector,AbstractVector{<:Union{AbstractDict,AbstractVector}}} = nothing, + kwargs... +) + map_is_multimodal = begin + if !isnothing(variable_names_map) && all(e->!(e isa Union{AbstractDict,AbstractVector}), variable_names_map) + @warn "With multimodal formulas, variable_names_map should be a vector of vectors/maps of " * + "variable names. Got $(typeof(variable_names_map)) instead. This may fail, " * + "or lead to unexpected results." + false + else + !isnothing(variable_names_map) + end + end + join([begin + _variable_names_map = map_is_multimodal ? variable_names_map[i_modality] : variable_names_map + φ = syntaxstring(f.formulas[i_modality]; variable_names_map = _variable_names_map, kwargs...) + hidemodality ? "$φ" : "{$(i_modality)}($φ)" + end for i_modality in sort(collect(keys(f.formulas)))], " $(CONJUNCTION) ") end function joinformulas(op::SoleLogics.AbstractOperator, children::NTuple{N,MultiFormula{F}}) where {N,F} diff --git a/src/logisets/scalar/conditions.jl b/src/logisets/scalar/conditions.jl index 7b03ae6..4c38cff 100644 --- a/src/logisets/scalar/conditions.jl +++ b/src/logisets/scalar/conditions.jl @@ -160,19 +160,19 @@ end function syntaxstring( m::ScalarCondition; - threshold_decimals::Union{Nothing,Integer} = nothing, + threshold_digits::Union{Nothing,Integer} = nothing, threshold_display_method::Union{Nothing,Base.Callable} = nothing, kwargs... ) - if (!isnothing(threshold_decimals) && !isnothing(threshold_display_method)) - @warn "Prioritizing threshold_display_method parameter over threshold_decimals " * + if (!isnothing(threshold_digits) && !isnothing(threshold_display_method)) + @warn "Prioritizing threshold_display_method parameter over threshold_digits " * "in syntaxstring for `ScalarCondition`." end threshold_display_method = begin if !isnothing(threshold_display_method) threshold_display_method - elseif !isnothing(threshold_decimals) - x->round(x; digits=threshold_decimals) + elseif !isnothing(threshold_digits) + x->round(x; digits=threshold_digits) else identity end diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 776b03b..7c2ade2 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -2,22 +2,22 @@ using SoleData: AbstractMultiModalDataset import SoleData: ninstances, nvariables, nmodalities, eachmodality function initlogiset(dataset, features) - error("Please, provide method initlogiset(dataset::$(typeof(dataset)), features::$(typeof(features))).") + return error("Please, provide method initlogiset(dataset::$(typeof(dataset)), features::$(typeof(features))).") end function ninstances(dataset) - error("Please, provide method ninstances(dataset::$(typeof(dataset))).") + return error("Please, provide method ninstances(dataset::$(typeof(dataset))).") end function nvariables(dataset) - error("Please, provide method nvariables(dataset::$(typeof(dataset))).") + return error("Please, provide method nvariables(dataset::$(typeof(dataset))).") end function frame(dataset, i_instance) - error("Please, provide method frame(dataset::$(typeof(dataset)), i_instance::Integer).") + return error("Please, provide method frame(dataset::$(typeof(dataset)), i_instance::Integer).") end function featvalue(dataset, i_instance, w, feature) - error("Please, provide method featvalue(dataset::$(typeof(dataset)), i_instance::Integer, w::$(typeof(w)), feature::$(typeof(feature))).") + return error("Please, provide method featvalue(dataset::$(typeof(dataset)), i_instance::Integer, w::$(typeof(w)), feature::$(typeof(feature))).") end function vareltype(dataset, i_variable) - error("Please, provide method vareltype(dataset::$(typeof(dataset)), i_variable::Integer).") + return error("Please, provide method vareltype(dataset::$(typeof(dataset)), i_variable::Integer).") end function allworlds(dataset, i_instance) @@ -29,10 +29,10 @@ function ismultimodal(dataset) false end function nmodalities(dataset) - error("Please, provide method nmodalities(dataset::$(typeof(dataset))).") + return error("Please, provide method nmodalities(dataset::$(typeof(dataset))).") end function eachmodality(dataset) - error("Please, provide method eachmodality(dataset::$(typeof(dataset))).") + return error("Please, provide method eachmodality(dataset::$(typeof(dataset))).") end function ismultimodal(dataset::AbstractMultiModalDataset) @@ -169,7 +169,7 @@ function scalarlogiset( else @warn "Could not infer feature value type for some of the specified features. " * "Please specify the feature value type upon construction. Untyped " * - "features: $(displaysyntaxvector(features_notok_fixed))" + "features: $(displaysyntaxvector(features_notok))" end end features = UniqueVector(features) @@ -229,7 +229,7 @@ const MixedCondition = Union{ function naturalconditions( dataset, mixed_conditions :: AbstractVector, - featvaltype :: Type = Real + featvaltype :: Type = DEFAULT_VARFEATVALTYPE ) nvars = nvariables(dataset) @@ -275,7 +275,9 @@ function naturalconditions( @warn "Building UnivariateFeature with non-concrete feature type: $(V)." "Please provide `featvaltype` parameter to naturalconditions." end - return (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_var, (x)->(V(cond(x))))) + # f = function (x) return V(cond(x)) end # breaks because it does not create a closure. + f = cond + return (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_var, f)) end univar_condition(i_var,::Any) = throw_n_log("Unknown mixed_feature type: $(cond), $(typeof(cond))") @@ -327,6 +329,7 @@ function naturalconditions( end end end + metaconditions end diff --git a/src/logisets/scalar/memosets.jl b/src/logisets/scalar/memosets.jl index a1bb80e..742eb35 100644 --- a/src/logisets/scalar/memosets.jl +++ b/src/logisets/scalar/memosets.jl @@ -6,6 +6,11 @@ import Base: in, findfirst Base.in(item, uv::UniqueVector) = haskey(uv.lookup, item) Base.findfirst(p::UniqueVectors.EqualTo, uv::UniqueVector) = get(uv.lookup, p.x, nothing) +_in(item, uv) = Base.in(item, uv) +_in(item, uv::UniqueVector) = haskey(uv.lookup, item) +_findfirst(p::UniqueVectors.EqualTo, uv) = Base.findfirst(p, uv) +_findfirst(p::UniqueVectors.EqualTo, uv::UniqueVector) = get(uv.lookup, p.x, nothing) + """ A full memoization structure used for checking formulas of scalar conditions on @@ -87,7 +92,7 @@ function check( w::W; kwargs... ) where {W<:AbstractWorld} - error("TODO implement chained threshold checking algorithm.") + return error("TODO implement chained threshold checking algorithm.") end function instances( diff --git a/src/logisets/scalar/onestep-memoset.jl b/src/logisets/scalar/onestep-memoset.jl index 56707bb..292a41f 100644 --- a/src/logisets/scalar/onestep-memoset.jl +++ b/src/logisets/scalar/onestep-memoset.jl @@ -95,7 +95,7 @@ abstract type AbstractScalarOneStepRelationalMemoset{W<:AbstractWorld,U,FR<:Abst i_metacond :: Integer, i_relation :: Integer ) where {W} - error("Please, provide method Base.getindex(" * + return error("Please, provide method Base.getindex(" * "Xm::$(typeof(Xm)), " * "i_instance::$(typeof(i_instance)), " * "w::$(typeof(w)), " * @@ -112,7 +112,7 @@ end i_metacond :: Integer, i_relation :: Integer, ) where {W<:AbstractWorld} - error("Please, provide method Base.setindex!(" * + return error("Please, provide method Base.setindex!(" * "Xm::$(typeof(Xm)), " * "gamma, " * "i_instance::$(typeof(i_instance)), " * @@ -134,7 +134,7 @@ abstract type AbstractScalarOneStepGlobalMemoset{W<:AbstractWorld,U} <: Abstract i_instance :: Integer, i_metacond :: Integer, ) where {W} - error("Please, provide method Base.getindex(" * + return error("Please, provide method Base.getindex(" * "Xm::$(typeof(Xm)), " * "i_instance::$(typeof(i_instance)), " * "i_metacond::$(typeof(i_metacond))" * @@ -147,7 +147,7 @@ end i_instance :: Integer, i_metacond :: Integer, ) where {W<:AbstractWorld} - error("Please, provide method Base.getindex(" * + return error("Please, provide method Base.getindex(" * "Xm::$(typeof(Xm)), " * "gamma, " * "i_instance::$(typeof(i_instance)), " * @@ -157,7 +157,7 @@ end # Access inner structure function innerstruct(Xm::Union{AbstractScalarOneStepRelationalMemoset,AbstractScalarOneStepGlobalMemoset}) - error("Please, provide method innerstruct(::$(typeof(Xm))).") + return error("Please, provide method innerstruct(::$(typeof(Xm))).") end isminifiable(::Union{AbstractScalarOneStepRelationalMemoset,AbstractScalarOneStepGlobalMemoset}) = true @@ -353,7 +353,7 @@ function grouped_metaconditions( end return map(((feature,these_metaconditions),)->begin these_metaconditions = map(_metacond->begin - i_metacond = findfirst(isequal(_metacond), metaconditions) + i_metacond = _findfirst(isequal(_metacond), metaconditions) aggregator = existential_aggregator(test_operator(_metacond)) (i_metacond, aggregator, _metacond) end, these_metaconditions) @@ -374,14 +374,14 @@ function featchannel_onestep_aggregation( )::U where {U,W<:AbstractWorld} if isnothing(i_metacond) - i_metacond = findfirst(isequal(metacond), metaconditions(Xm)) + i_metacond = _findfirst(isequal(metacond), metaconditions(Xm)) end if isnothing(i_metacond) # Find metacond with same aggregator i_metacond = findfirst((m)->feature(m) == feature(metacond) && existential_aggregator(test_operator(m)) == existential_aggregator(test_operator(metacond)), metaconditions(Xm)) if isnothing(i_metacond) - i_neg_metacond = findfirst(isequal(negation(metacond)), metaconditions(Xm)) + i_neg_metacond = _findfirst(isequal(negation(metacond)), metaconditions(Xm)) error("Could not find metacondition $(metacond) in memoset of type $(typeof(Xm)) " * "($(!isnothing(i_neg_metacond) ? "but negation was found " * "with i_metacond = $(i_neg_metacond)!" : "")).") @@ -405,7 +405,7 @@ function featchannel_onestep_aggregation( _globmemoset[i_instance, i_metacond] end else - i_relation = isnothing(i_relation) ? findfirst(isequal(rel), Xm.relations) : i_relation + i_relation = isnothing(i_relation) ? _findfirst(isequal(rel), Xm.relations) : i_relation if isnothing(i_relation) error("Could not find relation $(rel) in memoset of type $(typeof(Xm)).") end @@ -430,11 +430,11 @@ function check( rel = relation(f) metacond = metacond(f) - i_metacond = findfirst(isequal(metacond), metaconditions(Xm)) + i_metacond = _findfirst(isequal(metacond), metaconditions(Xm)) if isnothing(i_metacond) i_metacond = findfirst((m)->feature(m) == feature(metacond) && existential_aggregator(test_operator(m)) == existential_aggregator(test_operator(metacond)), metaconditions(Xm)) if isnothing(i_metacond) - i_neg_metacond = findfirst(isequal(negation(metacond)), metaconditions(Xm)) + i_neg_metacond = _findfirst(isequal(negation(metacond)), metaconditions(Xm)) error("Could not find metacondition $(metacond) in memoset of type $(typeof(Xm)) " * "($(!isnothing(i_neg_metacond) ? "but negation was found " * "with i_metacond = $(i_neg_metacond)!" : "")).") @@ -445,7 +445,7 @@ function check( if rel Base.getindex(globmemoset(Xm), i_instance, i_metacond) else - i_rel = findfirst(isequal(rel), Xm.relations) + i_rel = _findfirst(isequal(rel), Xm.relations) if isnothing(i_rel) error("Could not find relation $(rel) in memoset of type $(typeof(Xm)).") end diff --git a/src/logisets/scalar/var-features.jl b/src/logisets/scalar/var-features.jl index 631904d..4c0a6bb 100644 --- a/src/logisets/scalar/var-features.jl +++ b/src/logisets/scalar/var-features.jl @@ -60,7 +60,7 @@ Compute a feature on a featchannel (i.e., world reading) of an instance. See also [`VarFeature`](@ref). """ function computefeature(f::VarFeature{U}, featchannel; kwargs...) where {U} - error("Please, provide method computefeature(::$(typeof(f)), featchannel::$(typeof(featchannel)); kwargs...)::U.") + return error("Please, provide method computefeature(::$(typeof(f)), featchannel::$(typeof(featchannel)); kwargs...)::U.") end preserveseltype(::VarFeature) = false @@ -115,7 +115,7 @@ Compute a feature on a variable channel (i.e., world reading) of an instance. See also [`AbstractUnivariateFeature`](@ref). """ function computeunivariatefeature(f::AbstractUnivariateFeature{U}, varchannel::Any; kwargs...) where {U} - error("Please, provide method computeunivariatefeature(::$(typeof(f)), varchannel::$(typeof(varchannel)); kwargs...)::U.") + return error("Please, provide method computeunivariatefeature(::$(typeof(f)), varchannel::$(typeof(varchannel)); kwargs...)::U.") end i_variable(f::AbstractUnivariateFeature) = f.i_variable @@ -162,7 +162,7 @@ function variable_name( end function featurename(f::AbstractFeature; kwargs...) - error("Please, provide method featurename(::$(typeof(f)); kwargs...).") + return error("Please, provide method featurename(::$(typeof(f)); kwargs...).") end function syntaxstring( diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index bc699f0..509a724 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -174,6 +174,9 @@ end base(X::SupportedLogiset) = X.base supports(X::SupportedLogiset) = X.supports +# Helper +base(X::AbstractLogiset) = X + basetype(X::SupportedLogiset{W,U,FT,FR,L,N,MS}) where {W,U,FT,FR,L,N,MS} = L supporttypes(X::SupportedLogiset{W,U,FT,FR,L,N,MS}) where {W,U,FT,FR,L,N,MS} = MS @@ -184,7 +187,7 @@ nmemoizedvalues(X::SupportedLogiset) = sum(nmemoizedvalues.(supports(X))) function featchannel( - X::AbstractLogiset{W}, + X::SupportedLogiset{W}, i_instance::Integer, feature::AbstractFeature, ) where {W<:AbstractWorld} @@ -192,7 +195,7 @@ function featchannel( end function readfeature( - X::AbstractLogiset{W}, + X::SupportedLogiset{W}, featchannel::Any, w::W, feature::AbstractFeature, diff --git a/src/models/base.jl b/src/models/base.jl index 77ec47a..d0aef9c 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -21,7 +21,7 @@ See also abstract type AbstractBooleanCondition end function syntaxstring(c::AbstractBooleanCondition; kwargs...) - error("Please, provide method syntaxstring(::$(typeof(c)); kwargs...).") + return error("Please, provide method syntaxstring(::$(typeof(c)); kwargs...).") end function Base.show(io::IO, c::AbstractBooleanCondition) @@ -30,7 +30,7 @@ end # Check on a boolean condition function check(c::AbstractBooleanCondition, i::AbstractInterpretation, args...; kwargs...) - error("Please, provide method check(::$(typeof(c)), " * + return error("Please, provide method check(::$(typeof(c)), " * "i::$(typeof(i)), args...; kwargs...).") end function check( @@ -70,7 +70,7 @@ See also [`AbstractLogicalBooleanCondition`](@ref). """ function formula(c::AbstractLogicalBooleanCondition)::AbstractFormula - error("Please, provide method formula(::$(typeof(c))).") + return error("Please, provide method formula(::$(typeof(c))).") end function syntaxstring(c::AbstractLogicalBooleanCondition; kwargs...) @@ -258,7 +258,7 @@ function apply( functional_kwargs::NamedTuple = (;), kwargs..., )::outputtype(m) - error("Please, provide method apply(::$(typeof(m)), ::$(typeof(i))).") + return error("Please, provide method apply(::$(typeof(m)), ::$(typeof(i))).") end function apply( @@ -300,6 +300,7 @@ issymbolic(::AbstractModel) = false """ info(m::AbstractModel)::NamedTuple = m.info + info(m::AbstractModel, key) = m.info[key] Return the `info` structure for model `m`; this structure is used for storing additional information that does not affect the model's behavior. @@ -307,6 +308,8 @@ This structure can hold, for example, information about the model's statistical performance during the learning phase. """ info(m::AbstractModel)::NamedTuple = m.info +info(m::AbstractModel, key) = m.info[key] +info!(m::AbstractModel, key, value) = (m.info[key] = value; m) ############################################################################################ @@ -1304,7 +1307,7 @@ function nleaves(t::DecisionTree) end function height(t::DecisionTree) - submodelsheight(t) + subtreeheight(t) end ############################################################################################ @@ -1381,7 +1384,7 @@ function nleaves(f::DecisionForest) end function height(f::DecisionForest) - submodelsheight(f) + subtreeheight(f) end ############################################################################################ diff --git a/src/models/rule-evaluation.jl b/src/models/evaluation.jl similarity index 87% rename from src/models/rule-evaluation.jl rename to src/models/evaluation.jl index edb93fb..f6367e2 100644 --- a/src/models/rule-evaluation.jl +++ b/src/models/evaluation.jl @@ -1,7 +1,28 @@ using SoleModels +using MLJBase using SoleModels: LeafModel import SoleLogics: npropositions +function leafmetrics( + m::ConstantModel{L}; + digits = 2 +) where {L} + if haskey(info(m), :supporting_labels) + _gts = info(m)[:supporting_labels] + _preds = fill(outcome(m), length(_gts)) + if L <: CLabel + (; ninstances = length(_gts), confidence = round(MLJBase.accuracy(_gts, _preds); digits = digits)) + elseif L <: RLabel + (; ninstances = length(_gts), mae = round(MLJBase.mae(_gts, _preds); digits = digits)) + else + error("Could not compute leafmetrics with unknown label type: $(L).") + end + else + return (;) + end +end + + """ evaluaterule( r::Rule{O}, diff --git a/src/models/print.jl b/src/models/print.jl index ca9ce4b..0c77580 100644 --- a/src/models/print.jl +++ b/src/models/print.jl @@ -2,14 +2,37 @@ import Base: display ############################################################################################ -default_indentation_list_children = "┐" -default_indentation_any_first = "├ " # "╭✔ " -default_indentation_any_space = "│ " -default_indentation_last_first = "└ " # "╰✘ " -default_indentation_last_space = " " +# default_indentation_list_children = "┐" +# default_indentation_hspace = " " +# default_indentation_any_first = "├" # "╭✔ " +# default_indentation_any_space = "│" +# default_indentation_last_first = "└" # "╰✘ " +# default_indentation_last_space = " " + +default_indentation_list_children = "" +default_indentation_hspace = "" +default_indentation_any_first = "├" # "╭✔ " +default_indentation_any_space = "│" +default_indentation_last_first = "└" # "╰✘ " +default_indentation_last_space = " " + +# TICK = "✅" +# TICK = "✔️" +# TICK = "☑" +TICK = "✔" +# TICK = "🟩" +# CROSS = "❎" +# CROSS = "❌" +# CROSS = "☒" +# CROSS = "〤" +CROSS = "✘" + + +default_intermediate_finals_rpad = 100 default_indentation = ( default_indentation_list_children, + default_indentation_hspace, default_indentation_any_first, default_indentation_any_space, default_indentation_last_first, @@ -33,6 +56,8 @@ prints or returns a string representation of model `m`. the `info` structure for `m`; - `show_subtree_info::Bool = false`: when set to `true`, the header is printed for models in the sub-tree of `m`; +- `show_metrics::Bool = true`: when set to `true`, performance metrics at each point of the +subtree are shown, whenever they are available in the `info` structure; - `max_depth::Union{Nothing,Int} = nothing`: when it is an `Int`, models in the sub-tree with a depth higher than `max_depth` are ellipsed with "..."; - `syntaxstring_kwargs::NamedTuple = (;)`: kwargs to be passed to `syntaxstring` for @@ -47,15 +72,21 @@ function printmodel(io::IO, m::AbstractModel; kwargs...) end printmodel(m::AbstractModel; kwargs...) = printmodel(stdout, m; kwargs...) +# DEFAULT_HEADER = :brief +DEFAULT_HEADER = false + """$(doc_printdisplay_model)""" function displaymodel( m::AbstractModel; - header = :brief, + header = DEFAULT_HEADER, indentation_str = "", indentation = default_indentation, depth = 0, max_depth = nothing, show_subtree_info = false, + show_metrics = false, + show_intermediate_finals = false, + tree_mode = false, syntaxstring_kwargs = (;), ) println("Please, provide method displaymodel(::$(typeof(m)); kwargs...). " * @@ -74,6 +105,9 @@ macro _display_submodel( depth, max_depth, show_subtree_info, + show_metrics, + show_intermediate_finals, + tree_mode, syntaxstring_kwargs, kwargs ) @@ -81,21 +115,35 @@ macro _display_submodel( displaymodel($(esc(submodel)); indentation_str = $(esc(indentation_str)), indentation = $(esc(indentation)), - header = $(esc(show_subtree_info)), - show_subtree_info = $(esc(show_subtree_info)), depth = $(esc(depth))+1, max_depth = $(esc(max_depth)), + header = $(esc(show_subtree_info)), + show_subtree_info = $(esc(show_subtree_info)), + show_metrics = $(esc(show_metrics)), + show_intermediate_finals = $(esc(show_intermediate_finals)), + tree_mode = $(esc(tree_mode)), syntaxstring_kwargs = $(esc(syntaxstring_kwargs)), $(esc(kwargs))..., ) end end +function get_metrics_string( + m::ConstantModel, + digits = 2 +) + "$(leafmetrics(m; digits = digits))" +end + function displaymodel( m::ConstantModel; - header = :brief, + header = DEFAULT_HEADER, indentation_str = "", show_subtree_info = false, + show_metrics = false, + show_intermediate_finals = false, + tree_mode = false, + depth = 0, kwargs..., ) io = IOBuffer() @@ -107,15 +155,21 @@ function displaymodel( println(io, "$(indentation_str)$(_typestr)$((length(info(m)) == 0) ? "" : "\n$(indentation_str)Info: $(info(m))")") end - println(io, "$(outcome(m))") + depth == 0 && print(io, "▣") + print(io, " $(outcome(m))") + show_metrics != false && print(io, " : $(get_metrics_string(m; (show_metrics isa NamedTuple ? show_metrics : [])...))") + println(io, "") String(take!(io)) end function displaymodel( m::FunctionModel; - header = :brief, + header = DEFAULT_HEADER, indentation_str = "", show_subtree_info = false, + show_metrics = false, + show_intermediate_finals = false, + tree_mode = false, kwargs..., ) io = IOBuffer() @@ -127,24 +181,32 @@ function displaymodel( println(io, "$(indentation_str)$(_typestr)$((length(info(m)) == 0) ? "" : "\n$(indentation_str)Info: $(info(m))")") end - println(io, "$(f(m))") + depth == 0 && print(io, "▣") + print(io, "$(f(m))") + show_metrics != false && print(io, " : $(get_metrics_string(m; (show_metrics isa NamedTuple ? show_metrics : [])...))") + println(io, "") String(take!(io)) end function displaymodel( m::Rule; - header = :brief, + header = DEFAULT_HEADER, indentation_str = "", indentation = default_indentation, depth = 0, max_depth = nothing, show_subtree_info = false, + show_metrics = false, + show_intermediate_finals = false, + tree_mode = (subtreeheight(m) != 1), + arrow = "🠮", # ⮞, 🡆, 🠮, 🠲, => syntaxstring_kwargs = (;), kwargs..., ) io = IOBuffer() ( indentation_list_children, + indentation_hspace, indentation_any_first, indentation_any_space, indentation_last_first, @@ -158,18 +220,28 @@ function displaymodel( println(io, "$(indentation_str)$(_typestr)$((length(info(m)) == 0) ? "" : "\n$(indentation_str)Info: $(info(m))")") end + depth == 0 && print(io, "▣") ######################################################################################## if isnothing(max_depth) || depth < max_depth - pipe = "$(indentation_list_children)" + pipe = "$(indentation_list_children) " # println(io, "$(indentation_str*pipe)$(antecedent(m))") #println(io, "$(pipe)$(antecedent(m))") - println(io, "$(pipe)$(syntaxstring(antecedent(m); syntaxstring_kwargs...))") - pad_str = indentation_str*repeat(" ", length(pipe)-length(indentation_last_space)+1) - print(io, "$(pad_str*indentation_last_first)$("✔")") - ind_str = pad_str*indentation_last_space*repeat(" ", length("✔")-length(indentation_last_space)+2) - subm_str = @_display_submodel consequent(m) ind_str indentation depth max_depth show_subtree_info syntaxstring_kwargs kwargs - print(io, subm_str) + print(io, "$(pipe)$(syntaxstring(antecedent(m); (haskey(info(m), :syntaxstring_kwargs) ? info(m)[:syntaxstring_kwargs] : (;))..., syntaxstring_kwargs..., kwargs...))") + if tree_mode + println(io, "") + pad_str = indentation_str*repeat(indentation_hspace, length(pipe)-length(indentation_last_space)+2) + print(io, "$(pad_str*indentation_last_first)$(TICK)") + ind_str = pad_str*indentation_last_space*repeat(indentation_hspace, length(TICK)-length(indentation_last_space)+2) + subm_str = @_display_submodel consequent(m) ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs + print(io, subm_str) + else + ind_str = "" + subm_str = @_display_submodel consequent(m) ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs + print(io, " $(arrow) ") + print(io, subm_str) + end else + depth != 0 && print(io, " ") println(io, "[...]") end String(take!(io)) @@ -177,18 +249,22 @@ end function displaymodel( m::Branch; - header = :brief, + header = DEFAULT_HEADER, indentation_str = "", indentation = default_indentation, depth = 0, max_depth = nothing, show_subtree_info = false, + show_metrics = false, + show_intermediate_finals = false, + tree_mode = true, # subtreeheight(m) != 1 syntaxstring_kwargs = (;), kwargs..., ) io = IOBuffer() ( indentation_list_children, + indentation_hspace, indentation_any_first, indentation_any_space, indentation_last_first, @@ -202,22 +278,32 @@ function displaymodel( println(io, "$(indentation_str)$(_typestr)$((length(info(m)) == 0) ? "" : "\n$(indentation_str)Info: $(info(m))")") end + depth == 0 && print(io, "▣") ######################################################################################## if isnothing(max_depth) || depth < max_depth pipe = "$(indentation_list_children) " - println(io, "$(pipe)$(syntaxstring(antecedent(m); syntaxstring_kwargs...))") + line_str = "$(pipe)$(syntaxstring(antecedent(m); (haskey(info(m), :syntaxstring_kwargs) ? info(m)[:syntaxstring_kwargs] : (;))..., syntaxstring_kwargs..., kwargs...))" + if show_intermediate_finals != false && haskey(info(m), :this) + ind_str = "" + subm_str = @_display_submodel info(m)[:this] ind_str indentation (depth-1) max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs + line_str = rpad(line_str, show_intermediate_finals isa Integer ? show_intermediate_finals : default_intermediate_finals_rpad) * subm_str + print(io, line_str) + else + println(io, line_str) + end for (consequent, indentation_flag_space, indentation_flag_first, f) in [ - (posconsequent(m), indentation_any_space, indentation_any_first, "✔ "), - (negconsequent(m), indentation_last_space, indentation_last_first, "✘ ") + (posconsequent(m), indentation_any_space, indentation_any_first, TICK), + (negconsequent(m), indentation_last_space, indentation_last_first, CROSS) ] - # pad_str = indentation_str*indentation_flag_first**repeat(" ", length(pipe)-length(indentation_flag_first)) + # pad_str = indentation_str*indentation_flag_first**repeat(indentation_hspace, length(pipe)-length(indentation_flag_first)) pad_str = "$(indentation_str*indentation_flag_first)$(f)" print(io, "$(pad_str)") - ind_str = indentation_str*indentation_flag_space*repeat(" ", length(f)) - subm_str = @_display_submodel consequent ind_str indentation depth max_depth show_subtree_info syntaxstring_kwargs kwargs + ind_str = indentation_str*indentation_flag_space*repeat(indentation_hspace, length(f)) + subm_str = @_display_submodel consequent ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, subm_str) end else + depth != 0 && print(io, " ") println(io, "[...]") end String(take!(io)) @@ -226,18 +312,22 @@ end function displaymodel( m::DecisionList; - header = :brief, + header = DEFAULT_HEADER, indentation_str = "", indentation = default_indentation, depth = 0, max_depth = nothing, show_subtree_info = false, + show_metrics = false, + show_intermediate_finals = false, + tree_mode = true, syntaxstring_kwargs = (;), kwargs..., ) io = IOBuffer() ( indentation_list_children, + indentation_hspace, indentation_any_first, indentation_any_space, indentation_last_first, @@ -251,27 +341,29 @@ function displaymodel( println(io, "$(indentation_str)$(_typestr)$((length(info(m)) == 0) ? "" : "\n$(indentation_str)Info: $(info(m))")") end + depth == 0 && print(io, "▣") ######################################################################################## if isnothing(max_depth) || depth < max_depth println(io, "$(indentation_list_children)") for (i_rule, rule) in enumerate(rulebase(m)) # pipe = indentation_any_first pipe = indentation_any_first*"[$(i_rule)/$(length(rulebase(m)))]┐" - println(io, "$(indentation_str*pipe) $(syntaxstring(antecedent(rule); syntaxstring_kwargs...))") - pad_str = indentation_str*indentation_any_space*repeat(" ", length(pipe)-length(indentation_any_space)-1) + println(io, "$(indentation_str*pipe)$(syntaxstring(antecedent(rule); (haskey(info(rule), :syntaxstring_kwargs) ? info(rule)[:syntaxstring_kwargs] : (;))..., syntaxstring_kwargs..., kwargs...))") + pad_str = indentation_str*indentation_any_space*repeat(indentation_hspace, length(pipe)-length(indentation_any_space)-1) print(io, "$(pad_str*indentation_last_first)") ind_str = pad_str*indentation_last_space - subm_str = @_display_submodel consequent(rule) ind_str indentation depth max_depth show_subtree_info syntaxstring_kwargs kwargs + subm_str = @_display_submodel consequent(rule) ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, subm_str) end - pipe = indentation_last_first*"$("✘ ")" + pipe = indentation_last_first*"$(indentiation_cross)" print(io, "$(indentation_str*pipe)") - # print(io, "$(indentation_str*indentation_last_space*repeat(" ", length(pipe)-length(indentation_last_space)-1)*indentation_last_space)") - ind_str = indentation_str*indentation_last_space*repeat(" ", length(pipe)-length(indentation_last_space)-1)*indentation_last_space + # print(io, "$(indentation_str*indentation_last_space*repeat(indentation_hspace, length(pipe)-length(indentation_last_space)-1)*indentation_last_space)") + ind_str = indentation_str*indentation_last_space*repeat(indentation_hspace, length(pipe)-length(indentation_last_space)-1)*indentation_last_space # ind_str = indentation_str*indentation_last_space, - subm_str = @_display_submodel defaultconsequent(m) ind_str indentation depth max_depth show_subtree_info syntaxstring_kwargs kwargs + subm_str = @_display_submodel defaultconsequent(m) ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, subm_str) else + depth != 0 && print(io, " ") println(io, "[...]") end String(take!(io)) @@ -279,18 +371,22 @@ end function displaymodel( m::DecisionTree; - header = :brief, + header = DEFAULT_HEADER, indentation_str = "", indentation = default_indentation, depth = 0, max_depth = nothing, show_subtree_info = false, + show_metrics = false, + show_intermediate_finals = false, + tree_mode = true, syntaxstring_kwargs = (;), kwargs..., ) io = IOBuffer() ( indentation_list_children, + indentation_hspace, indentation_any_first, indentation_any_space, indentation_last_first, @@ -304,26 +400,31 @@ function displaymodel( println(io, "$(indentation_str)$(_typestr)$((length(info(m)) == 0) ? "" : "\n$(indentation_str)Info: $(info(m))")") end + ######################################################################################## - subm_str = @_display_submodel root(m) indentation_str indentation depth max_depth show_subtree_info syntaxstring_kwargs kwargs + subm_str = @_display_submodel root(m) indentation_str indentation (depth-1) max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, subm_str) String(take!(io)) end function displaymodel( m::DecisionForest; - header = :brief, + header = DEFAULT_HEADER, indentation_str = "", indentation = default_indentation, depth = 0, max_depth = nothing, show_subtree_info = false, + show_metrics = false, + show_intermediate_finals = false, + tree_mode = true, syntaxstring_kwargs = (;), kwargs..., ) io = IOBuffer() ( indentation_list_children, + indentation_hspace, indentation_any_first, indentation_any_space, indentation_last_first, @@ -337,9 +438,10 @@ function displaymodel( println(io, "$(indentation_str)$(_typestr)$((length(info(m)) == 0) ? "" : "\n$(indentation_str)Info: $(info(m))")") end + ######################################################################################## for tree in trees(m) - subm_str = @_display_submodel tree indentation_str indentation depth max_depth show_subtree_info syntaxstring_kwargs kwargs + subm_str = @_display_submodel tree indentation_str indentation (depth-1) max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, subm_str) end String(take!(io)) @@ -347,18 +449,22 @@ end function displaymodel( m::MixedSymbolicModel; - header = :brief, + header = DEFAULT_HEADER, indentation_str = "", indentation = default_indentation, depth = 0, max_depth = nothing, show_subtree_info = false, + show_metrics = false, + show_intermediate_finals = false, + tree_mode = true, syntaxstring_kwargs = (;), kwargs..., ) io = IOBuffer() ( indentation_list_children, + indentation_hspace, indentation_any_first, indentation_any_space, indentation_last_first, @@ -372,8 +478,9 @@ function displaymodel( println(io, "$(indentation_str)$(_typestr)$((length(info(m)) == 0) ? "" : "\n$(indentation_str)Info: $(info(m))")") end + ######################################################################################## - subm_str = @_display_submodel root(m) indentation_str indentation depth max_depth show_subtree_info syntaxstring_kwargs kwargs + subm_str = @_display_submodel root(m) indentation_str indentation (depth-1) max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, subm_str) String(take!(io)) end diff --git a/src/models/symbolic-utils.jl b/src/models/symbolic-utils.jl index fa632ae..2ba9c83 100644 --- a/src/models/symbolic-utils.jl +++ b/src/models/symbolic-utils.jl @@ -13,7 +13,7 @@ Note: if the model is a leaf model, then the returned list will be empty. ```julia-repl julia> using SoleLogics -julia> branch = Branch(SoleLogics.parseformula("p∧q∨r"), "YES", "NO"); +julia> branch = Branch(SoleLogics.parsebaseformula("p∧q∨r"), "YES", "NO"); julia> immediatesubmodels(branch) 2-element Vector{SoleModels.ConstantModel{String}}: @@ -23,7 +23,7 @@ YES SoleModels.ConstantModel{String} NO -julia> branch2 = Branch(SoleLogics.parseformula("s→p"), branch, 42); +julia> branch2 = Branch(SoleLogics.parsebaseformula("s→p"), branch, 42); julia> printmodel.(immediatesubmodels(branch2)); @@ -44,7 +44,7 @@ See also function immediatesubmodels( m::AbstractModel{O} )::Vector{<:{AbstractModel{<:O}}} where {O} - error("Please, provide method immediatesubmodels(::$(typeof(m))).") + return error("Please, provide method immediatesubmodels(::$(typeof(m))).") end immediatesubmodels(m::LeafModel{O}) where {O} = Vector{<:AbstractModel{<:O}}[] @@ -75,7 +75,7 @@ their immediate submodels, and so on. ```julia-repl julia> using SoleLogics -julia> branch = Branch(SoleLogics.parseformula("p∧q∨r"), "YES", "NO"); +julia> branch = Branch(SoleLogics.parsebaseformula("p∧q∨r"), "YES", "NO"); julia> submodels(branch) 2-element Vector{SoleModels.ConstantModel{String}}: @@ -86,7 +86,7 @@ YES NO -julia> branch2 = Branch(SoleLogics.parseformula("s→p"), branch, 42); +julia> branch2 = Branch(SoleLogics.parsebaseformula("s→p"), branch, 42); julia> printmodel.(submodels(branch2)); Branch @@ -132,11 +132,12 @@ leafmodels(m::AbstractModel) = [Iterators.flatten(leafmodels.(immediatesubmodels nleafmodels(m::AbstractModel) = sum(nleafmodels, immediatesubmodels(m)) -submodelsheight(m::AbstractModel) = 1 + maximum(submodelsheight, immediatesubmodels(m)) -submodelsheight(m::DecisionList) = maximum(submodelsheight, immediatesubmodels(m)) -submodelsheight(m::DecisionTree) = maximum(submodelsheight, immediatesubmodels(m)) -submodelsheight(m::DecisionForest) = maximum(submodelsheight, immediatesubmodels(m)) -submodelsheight(m::MixedSymbolicModel) = maximum(submodelsheight, immediatesubmodels(m)) +subtreeheight(m::AbstractModel) = 1 + maximum(subtreeheight, immediatesubmodels(m)) +subtreeheight(m::LeafModel) = 0 +subtreeheight(m::DecisionList) = maximum(subtreeheight, immediatesubmodels(m)) +subtreeheight(m::DecisionTree) = maximum(subtreeheight, immediatesubmodels(m)) +subtreeheight(m::DecisionForest) = maximum(subtreeheight, immediatesubmodels(m)) +subtreeheight(m::MixedSymbolicModel) = maximum(subtreeheight, immediatesubmodels(m)) ############################################################################################ ############################################################################################ @@ -200,7 +201,7 @@ listimmediaterules(m::MixedSymbolicModel) = listimmediaterules(root(m)) ############################################################################################ """ - listrules(m::AbstractModel; force_syntaxtree::Bool = false)::Vector{<:Rule} + listrules(m::AbstractModel; force_syntaxtree::Bool = false, use_shortforms::Bool = true)::Vector{<:Rule} Return a list of rules capturing the knowledge enclosed in symbolic model. The behavior of a symbolic model can be extracted and represented as a @@ -289,13 +290,8 @@ function listrules( m::Rule{O,<:LogicalTruthCondition}; force_syntaxtree::Bool = false ) where {O} - [begin - !force_syntaxtree ? m : Rule{O}( - LogicalTruthCondition(tree(formula(m))), - consequent(m), - info(m) - ) - end] + ant = force_syntaxtree ? tree(formula(m)) : formula(m) + [(force_syntaxtree ? Rule{O}(LogicalTruthCondition(ant), consequent(m), info(m)) : m)] end function listrules( @@ -320,55 +316,45 @@ end function listrules( m::Branch{O,<:LogicalTruthCondition}; + use_shortforms::Bool = true, force_syntaxtree::Bool = false, + use_leftmostlinearform::Bool = false, kwargs..., ) where {O} - pos_rules = begin - submodels = listrules(posconsequent(m); force_syntaxtree = force_syntaxtree, kwargs...) - ant = tree(formula(m)) + using_shortform = use_shortforms && haskey(info(m), :shortform) + ant = (using_shortform ? info(m, :shortform) : m) + antformula = formula(ant) - map(subm-> begin - if subm isa LeafModel - Rule(LogicalTruthCondition(ant), subm) - else - f = formula(subm) - subants = f isa LeftmostLinearForm ? children(f) : [f] - Rule( - LogicalTruthCondition( begin - lf = LeftmostConjunctiveForm([ant, subants...]) - force_syntaxtree ? tree(lf) : lf - end), - consequent(subm) - ) - end - end, submodels) - end + pos_antformula = force_syntaxtree ? tree(antformula) : antformula + neg_antformula = force_syntaxtree ? ¬tree(antformula) : ¬antformula - neg_rules = begin - submodels = listrules(negconsequent(m); force_syntaxtree = force_syntaxtree, kwargs...) - ant = ¬(tree(formula(m))) + _subrules = [ + [(pos_antformula, r) for r in listrules(posconsequent(m); use_shortforms = use_shortforms, use_leftmostlinearform = use_leftmostlinearform, force_syntaxtree = force_syntaxtree, kwargs...)]..., + [(neg_antformula, r) for r in listrules(negconsequent(m); use_shortforms = use_shortforms, use_leftmostlinearform = use_leftmostlinearform, force_syntaxtree = force_syntaxtree, kwargs...)]... + ] - map(subm-> begin - if subm isa LeafModel - Rule(LogicalTruthCondition(ant), subm) + rules = map(((antformula, subrule),)->begin + # @show info(subrule) + if subrule isa LeafModel + Rule(LogicalTruthCondition(antformula), subrule, merge(info(subrule), (; shortform = LogicalTruthCondition(antformula)))) + elseif (use_shortforms && haskey(info(subrule), :shortform)) + Rule(info(subrule)[:shortform], consequent(subrule), info(subrule)) else - f = formula(subm) - subants = f isa LeftmostLinearForm ? children(f) : [f] - Rule( - LogicalTruthCondition( begin - lf = LeftmostConjunctiveForm([ant, subants...]) + f = begin + f = formula(subrule) + if use_leftmostlinearform + subantformulas = (f isa LeftmostLinearForm ? children(f) : [f]) + lf = LeftmostConjunctiveForm([antformula, subantformulas...]) force_syntaxtree ? tree(lf) : lf - end), - consequent(subm) - ) + else + antformula ∧ f + end + end + Rule(LogicalTruthCondition(f), consequent(subrule)) end - end, submodels) - end + end, _subrules) - return [ - pos_rules..., - neg_rules..., - ] + return rules end function listrules(m::DecisionList; kwargs...) diff --git a/test/base.jl b/test/base.jl index 4723de6..df7897b 100644 --- a/test/base.jl +++ b/test/base.jl @@ -10,14 +10,14 @@ using Test io = IOBuffer() -p = SoleLogics.parseformula("p") -phi = SoleLogics.parseformula("p∧q∨r") -phi2 = SoleLogics.parseformula("q∧s→r") - -formula_p = SoleLogics.parseformula("p") -formula_q = SoleLogics.parseformula("q") -formula_r = SoleLogics.parseformula("r") -formula_s = SoleLogics.parseformula("s") +p = SoleLogics.parsebaseformula("p") +phi = SoleLogics.parsebaseformula("p∧q∨r") +phi2 = SoleLogics.parsebaseformula("q∧s→r") + +formula_p = SoleLogics.parsebaseformula("p") +formula_q = SoleLogics.parsebaseformula("q") +formula_r = SoleLogics.parsebaseformula("r") +formula_s = SoleLogics.parsebaseformula("s") # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Leaf models ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/test/logisets/cube2logiset.jl b/test/logisets/cube2logiset.jl index d6340bc..edffe1e 100644 --- a/test/logisets/cube2logiset.jl +++ b/test/logisets/cube2logiset.jl @@ -48,7 +48,7 @@ complete_supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_mem rng = Random.MersenneTwister(1) alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, generic_features), rand(rng, [>, <]), rand(rng)) for i in 1:n_instances]); # syntaxstring.(alph) -_formulas = [randformulatree(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:16:end]...]) for i in 1:20]; +_formulas = [randformula(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:16:end]...]) for i in 1:20]; # syntaxstring.(_formulas) .|> println; i_instance = 1 diff --git a/test/logisets/dataframe2logiset.jl b/test/logisets/dataframe2logiset.jl index ebad62f..7fb1f2d 100644 --- a/test/logisets/dataframe2logiset.jl +++ b/test/logisets/dataframe2logiset.jl @@ -50,7 +50,7 @@ complete_logiset = @test_nowarn scalarlogiset(dataset; use_full_memoization = fa rng = Random.MersenneTwister(1) alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, generic_features), rand(rng, [>, <]), rand(rng)) for i in 1:n_instances]); syntaxstring.(alph) -_formulas = [randformulatree(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:16:end]...]) for i in 1:20]; +_formulas = [randformula(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:16:end]...]) for i in 1:20]; syntaxstring.(_formulas) .|> println; i_instance = 1 diff --git a/test/logisets/logisets.jl b/test/logisets/logisets.jl index 23c2253..e778807 100644 --- a/test/logisets/logisets.jl +++ b/test/logisets/logisets.jl @@ -154,9 +154,9 @@ bool_supported_logiset2 = @test_nowarn SupportedLogiset(bool_logiset, [memoset]) rng = Random.MersenneTwister(1) alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]) syntaxstring.(alph) -_formulas = [randformulatree(rng, 4, alph, [NEGATION, CONJUNCTION, IMPLICATION, DIAMOND, BOX]) for i in 1:10] +_formulas = [randformula(rng, 4, alph, [NEGATION, CONJUNCTION, IMPLICATION, DIAMOND, BOX]) for i in 1:10] @test_nowarn syntaxstring.(_formulas) -@test_nowarn syntaxstring.(_formulas; threshold_decimals = 2) +@test_nowarn syntaxstring.(_formulas; threshold_digits = 2) c1 = @test_nowarn [check(φ, bool_logiset, 1, w) for φ in _formulas] c2 = @test_nowarn [check(φ, bool_logiset, 1, w; use_memo = nothing) for φ in _formulas] @@ -223,7 +223,7 @@ bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset_empty, rng = Random.MersenneTwister(1) alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]) syntaxstring.(alph) -_formulas = [randformulatree(rng, 10, alph, SoleLogics.BASE_MULTIMODAL_OPERATORS) for i in 1:20]; +_formulas = [randformula(rng, 10, alph, SoleLogics.BASE_MULTIMODAL_OPERATORS) for i in 1:20]; # Below are the times with a testset of 1000 formulas ############################################################################################ diff --git a/test/logisets/multilogisets.jl b/test/logisets/multilogisets.jl index 6e209ae..38cd87e 100644 --- a/test/logisets/multilogisets.jl +++ b/test/logisets/multilogisets.jl @@ -41,7 +41,7 @@ i_instance = 1 multiformulas = [begin _formulas_dict = Dict{Int,SoleLogics.AbstractFormula}() for (i_modality, relations) in enumerate(multirelations) - f = randformulatree(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:32:end]...]) + f = randformula(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:32:end]...]) if rand(Bool) # coin = rand(1:3) coin = rand(2:3) diff --git a/test/misc.jl b/test/misc.jl index e3b99c1..8c535dc 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -68,19 +68,19 @@ st_1 = @test_nowarn SyntaxTree(prop_1) st_100 = @test_nowarn SyntaxTree(prop_100) ################################### Formula ################################################ -p = @test_nowarn SoleLogics.parseformula("p") -p_tree = @test_nowarn SoleLogics.parseformulatree("p") +p = @test_nowarn SoleLogics.parsebaseformula("p") +p_tree = @test_nowarn SoleLogics.parsetree("p") # @test LogicalTruthCondition(p) == LogicalTruthCondition{Formula}(p) # @test LogicalTruthCondition(p_tree) == LogicalTruthCondition{SyntaxTree}(p_tree) -# phi = @test_nowarn SoleLogics.parseformula("p∧q∨r") -# phi_tree = @test_nowarn SoleLogics.parseformulatree("p∧q∨r") +# phi = @test_nowarn SoleLogics.parsebaseformula("p∧q∨r") +# phi_tree = @test_nowarn SoleLogics.parsetree("p∧q∨r") # @test LogicalTruthCondition(phi) == LogicalTruthCondition{Formula}(phi) # @test LogicalTruthCondition(phi_tree) == LogicalTruthCondition{SyntaxTree}(phi_tree) -# phi2 = @test_nowarn SoleLogics.parseformula("q∧s→r") -# phi2_tree = @test_nowarn SoleLogics.parseformulatree("q∧s→r") +# phi2 = @test_nowarn SoleLogics.parsebaseformula("q∧s→r") +# phi2_tree = @test_nowarn SoleLogics.parsetree("q∧s→r") # @test LogicalTruthCondition(phi2) == LogicalTruthCondition{Formula}(phi2) # @test LogicalTruthCondition(phi2_tree) == LogicalTruthCondition{SyntaxTree}(phi2_tree) @@ -90,20 +90,20 @@ p_tree = @test_nowarn SoleLogics.parseformulatree("p") @test LogicalTruthCondition(p) isa LogicalTruthCondition{<:Formula} @test LogicalTruthCondition(p_tree) isa LogicalTruthCondition{<:SyntaxTree} -phi = @test_nowarn SoleLogics.parseformula("p∧q∨r") -phi_tree = @test_nowarn SoleLogics.parseformulatree("p∧q∨r") +phi = @test_nowarn SoleLogics.parsebaseformula("p∧q∨r") +phi_tree = @test_nowarn SoleLogics.parsetree("p∧q∨r") @test LogicalTruthCondition(phi) isa LogicalTruthCondition{<:Formula} @test LogicalTruthCondition(phi_tree) isa LogicalTruthCondition{<:SyntaxTree} -phi2 = @test_nowarn SoleLogics.parseformula("q∧s→r") -phi2_tree = @test_nowarn SoleLogics.parseformulatree("q∧s→r") +phi2 = @test_nowarn SoleLogics.parsebaseformula("q∧s→r") +phi2_tree = @test_nowarn SoleLogics.parsetree("q∧s→r") @test LogicalTruthCondition(phi2) isa LogicalTruthCondition{<:Formula} @test LogicalTruthCondition(phi2_tree) isa LogicalTruthCondition{<:SyntaxTree} -formula_p = @test_nowarn SoleLogics.parseformula("p") -formula_q = @test_nowarn SoleLogics.parseformula("q") -formula_r = @test_nowarn SoleLogics.parseformula("r") -formula_s = @test_nowarn SoleLogics.parseformula("s") +formula_p = @test_nowarn SoleLogics.parsebaseformula("p") +formula_q = @test_nowarn SoleLogics.parsebaseformula("q") +formula_r = @test_nowarn SoleLogics.parsebaseformula("r") +formula_s = @test_nowarn SoleLogics.parsebaseformula("s") ############################### LogicalTruthCondition ###################################### cond_r = @test_nowarn LogicalTruthCondition(st_r) From 92e70088e2b42fd50c97a4e0a094d9a83ff1f0e6 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 7 Jul 2023 17:56:18 +0200 Subject: [PATCH 27/77] Fix SoleLogics dependency --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index ba4e8da..ae1b4ae 100644 --- a/Project.toml +++ b/Project.toml @@ -26,7 +26,7 @@ ThreadSafeDicts = "4239201d-c60e-5e0a-9702-85d713665ba7" BenchmarkTools = "1" SoleBase = "0.9.2" SoleData = "0.9.0" -SoleLogics = "0.2.1" +SoleLogics = "0.3.0" julia = "1" [extras] From 5459cc417bcd030ffb8af8e05a019239aba97156 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 7 Jul 2023 19:10:32 +0200 Subject: [PATCH 28/77] Fix authors --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index de08921..91f065f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "SoleModels" uuid = "4249d9c7-3290-4ddd-961c-e1d3ec2467f8" -authors = ["Eduard I. STAN", "Giovanni PAGLIARINI"] +authors = ["Michele GHIOTTI", "Giovanni PAGLIARINI", "Eduard I. STAN"] version = "0.1.0" [deps] From 0e62b06181f09adbc1b6d7d78950267cea283dcc Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 7 Jul 2023 19:28:25 +0200 Subject: [PATCH 29/77] Bump SoleData version --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 91f065f..71dd0ef 100644 --- a/Project.toml +++ b/Project.toml @@ -23,7 +23,6 @@ Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" ThreadSafeDicts = "4239201d-c60e-5e0a-9702-85d713665ba7" [compat] -julia = "1" BenchmarkTools = "1" ComputedFieldTypes = "1" DataStructures = "0.18" @@ -33,10 +32,11 @@ ProgressMeter = "1" Reexport = "1" Revise = "3" SoleBase = "0.9.2" -SoleData = "0.9.0" +SoleData = "0.9.1" SoleLogics = "0.3.0" StatsBase = "0.33" Suppressor = "0.2" +julia = "1" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From 0480504d1ef26536eae443a2fdfdfb25db04c860 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 7 Jul 2023 19:52:20 +0200 Subject: [PATCH 30/77] Fix readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7981f2f..4507c83 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ *SoleModels.jl* defines the building blocks of *symbolic* modeling and learning. It features: -- Definitions for symbolic models (decision trees/forests, rules, etc.); -- Optimized data structures, useful when learning models from datasets; +- Definitions for symbolic models (decision trees/forests, rules, branches, etc.); +- Optimized data structures, useful when learning models from machine learning datasets; - Support for mixed, neuro-symbolic computation. These definitions provide a unified base for implementing symbolic algorithms, such as: From 142ae420d48f72080d5f1f32507f7b5b7661e5cc Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:01:55 +0200 Subject: [PATCH 31/77] Fix actions and docs/Manifest.toml --- docs/Manifest.toml | 670 ++++++++++++++++++++++++++------------------- docs/Project.toml | 1 - docs/install.jl | 31 --- src/install.jl | 30 -- 4 files changed, 383 insertions(+), 349 deletions(-) delete mode 100644 docs/install.jl delete mode 100644 src/install.jl diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 08fc238..2d3c775 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -1,699 +1,795 @@ # This file is machine-generated - editing it directly is not advised -[[ANSIColoredPrinters]] +julia_version = "1.9.0" +manifest_format = "2.0" +project_hash = "505d636e5b06fb8b13e70d8052e1aec51b8ac90e" + +[[deps.ANSIColoredPrinters]] git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" version = "0.0.1" -[[Adapt]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "195c5505521008abea5aee4f96930717958eac6f" +[[deps.Adapt]] +deps = ["LinearAlgebra", "Requires"] +git-tree-sha1 = "76289dc51920fdc6e0013c872ba9551d54961c24" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "3.4.0" +version = "3.6.2" + + [deps.Adapt.extensions] + AdaptStaticArraysExt = "StaticArrays" -[[ArgTools]] + [deps.Adapt.weakdeps] + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.1" -[[ArrayInterfaceCore]] -deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "c46fb7dd1d8ca1d213ba25848a5ec4e47a1a1b08" +[[deps.ArrayInterfaceCore]] +deps = ["LinearAlgebra", "SnoopPrecompile", "SparseArrays", "SuiteSparse"] +git-tree-sha1 = "e5f08b5689b1aad068e01751889f2f615c7db36d" uuid = "30b0a656-2188-435a-8636-2ec0e6a096e2" -version = "0.1.26" +version = "0.1.29" -[[Artifacts]] +[[deps.Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -[[Base64]] +[[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -[[CSV]] -deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings"] -git-tree-sha1 = "c5fd7cd27ac4aed0acf4b73948f0110ff2a854b2" +[[deps.BenchmarkTools]] +deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] +git-tree-sha1 = "d9a9701b899b30332bbcb3e1679c41cce81fb0e8" +uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +version = "1.3.2" + +[[deps.CSV]] +deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "PrecompileTools", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings", "WorkerUtilities"] +git-tree-sha1 = "44dbf560808d49041989b8a96cae4cffbeb7966a" uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" -version = "0.10.7" +version = "0.10.11" -[[Calculus]] +[[deps.Calculus]] deps = ["LinearAlgebra"] git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad" uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" version = "0.5.1" -[[Catch22]] -deps = ["DelimitedFiles", "DimensionalData", "Documenter", "Libdl", "LinearAlgebra", "Pkg", "ProgressLogging", "Reexport", "Requires", "Statistics", "catch22_jll"] -git-tree-sha1 = "7604d4308cbf61c9a0ad4b9b05764cadeffa73db" +[[deps.Catch22]] +deps = ["DelimitedFiles", "DimensionalData", "Libdl", "LinearAlgebra", "Pkg", "ProgressLogging", "Reexport", "Requires", "Statistics", "catch22_jll"] +git-tree-sha1 = "319c5c8e66fb45a3d5a91864fdebe01ec314b9dc" uuid = "acdeb78f-3d39-4310-8fdf-6d75c17c6d5a" -version = "0.4.3" +version = "0.4.4" -[[CategoricalArrays]] +[[deps.CategoricalArrays]] deps = ["DataAPI", "Future", "Missings", "Printf", "Requires", "Statistics", "Unicode"] -git-tree-sha1 = "5084cc1a28976dd1642c9f337b28a3cb03e0f7d2" +git-tree-sha1 = "1568b28f91293458345dabba6a5ea3f183250a61" uuid = "324d7699-5711-5eae-9e2f-1d82baa6b597" -version = "0.10.7" - -[[ChainRulesCore]] -deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "e7ff6cadf743c098e08fca25c91103ee4303c9bb" -uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.15.6" - -[[ChangesOfVariables]] -deps = ["ChainRulesCore", "LinearAlgebra", "Test"] -git-tree-sha1 = "38f7a08f19d8810338d4f5085211c7dfa5d5bdd8" -uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" -version = "0.1.4" +version = "0.10.8" + + [deps.CategoricalArrays.extensions] + CategoricalArraysJSONExt = "JSON" + CategoricalArraysRecipesBaseExt = "RecipesBase" + CategoricalArraysSentinelArraysExt = "SentinelArrays" + CategoricalArraysStructTypesExt = "StructTypes" -[[CodeTracking]] + [deps.CategoricalArrays.weakdeps] + JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" + RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + SentinelArrays = "91c51154-3ec4-41a3-a24f-3f23e20d615c" + StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" + +[[deps.CodeTracking]] deps = ["InteractiveUtils", "UUIDs"] -git-tree-sha1 = "3bf60ba2fae10e10f70d53c070424e40a820dac2" +git-tree-sha1 = "d730914ef30a06732bdd9f763f6cc32e92ffbff1" uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" -version = "1.1.2" +version = "1.3.1" -[[CodecZlib]] +[[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "ded953804d019afa9a3f98981d99b33e3db7b6da" +git-tree-sha1 = "9c209fb7536406834aa938fb149964b985de6c83" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.0" +version = "0.7.1" -[[ColorTypes]] +[[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4" uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" version = "0.11.4" -[[Compat]] -deps = ["Dates", "LinearAlgebra", "UUIDs"] -git-tree-sha1 = "00a2cccc7f098ff3b66806862d275ca3db9e6e5a" +[[deps.Combinatorics]] +git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" +uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" +version = "1.0.2" + +[[deps.Compat]] +deps = ["UUIDs"] +git-tree-sha1 = "4e88377ae7ebeaf29a047aa1ee40826e0b708a5d" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.5.0" +version = "4.7.0" +weakdeps = ["Dates", "LinearAlgebra"] + + [deps.Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" -[[CompilerSupportLibraries_jll]] +[[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "0.5.2+0" +version = "1.0.2+0" -[[ConstructionBase]] +[[deps.ComputedFieldTypes]] +git-tree-sha1 = "059a8d396af73d574b679223c91ce209c0d3d809" +uuid = "459fdd68-db75-56b8-8c15-d717a790f88e" +version = "1.0.1" + +[[deps.ConstructionBase]] deps = ["LinearAlgebra"] -git-tree-sha1 = "fb21ddd70a051d882a1686a5a550990bbe371a95" +git-tree-sha1 = "738fec4d684a9a6ee9598a8bfee305b26831f28c" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.4.1" +version = "1.5.2" + + [deps.ConstructionBase.extensions] + ConstructionBaseIntervalSetsExt = "IntervalSets" + ConstructionBaseStaticArraysExt = "StaticArrays" + + [deps.ConstructionBase.weakdeps] + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" -[[Crayons]] +[[deps.Crayons]] git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" version = "4.1.1" -[[DataAPI]] -git-tree-sha1 = "e08915633fcb3ea83bf9d6126292e5bc5c739922" +[[deps.DataAPI]] +git-tree-sha1 = "8da84edb865b0b5b0100c0666a9bc9a0b71c553c" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.13.0" +version = "1.15.0" -[[DataFrames]] -deps = ["Compat", "DataAPI", "Future", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SnoopPrecompile", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] -git-tree-sha1 = "d4f69885afa5e6149d0cab3818491565cf41446d" +[[deps.DataFrames]] +deps = ["Compat", "DataAPI", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SnoopPrecompile", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] +git-tree-sha1 = "aa51303df86f8626a962fccb878430cdb0a97eee" uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" -version = "1.4.4" +version = "1.5.0" -[[DataStructures]] +[[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" +git-tree-sha1 = "cf25ccb972fec4e4817764d01c82386ae94f77b4" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.13" +version = "0.18.14" -[[DataValueInterfaces]] +[[deps.DataValueInterfaces]] git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" version = "1.0.0" -[[Dates]] +[[deps.Dates]] deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -[[DelimitedFiles]] +[[deps.DelimitedFiles]] deps = ["Mmap"] +git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" +version = "1.9.1" -[[DensityInterface]] -deps = ["InverseFunctions", "Test"] -git-tree-sha1 = "80c3e8639e3353e5d2912fb3a1916b8455e2494b" -uuid = "b429d917-457f-4dbc-8f4c-0cc954292b1d" -version = "0.4.0" - -[[Dictionaries]] +[[deps.Dictionaries]] deps = ["Indexing", "Random", "Serialization"] git-tree-sha1 = "e82c3c97b5b4ec111f3c1b55228cebc7510525a2" uuid = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" version = "0.3.25" -[[DimensionalData]] -deps = ["Adapt", "ArrayInterfaceCore", "ConstructionBase", "Dates", "Extents", "IntervalSets", "IteratorInterfaceExtensions", "LinearAlgebra", "Random", "RecipesBase", "SparseArrays", "Statistics", "TableTraits", "Tables"] -git-tree-sha1 = "d27931da7e3ec81c355e6895ba53034c03e17a7e" +[[deps.DimensionalData]] +deps = ["Adapt", "ArrayInterfaceCore", "ConstructionBase", "Dates", "Extents", "IntervalSets", "IteratorInterfaceExtensions", "LinearAlgebra", "Random", "RecipesBase", "SnoopPrecompile", "SparseArrays", "Statistics", "TableTraits", "Tables"] +git-tree-sha1 = "dda58a378971eabba69c526ca159ee9ca6715f4f" uuid = "0703355e-b756-11e9-17c0-8b28908087d0" -version = "0.23.0" +version = "0.24.12" -[[Distributed]] +[[deps.Distributed]] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -[[Distributions]] -deps = ["ChainRulesCore", "DensityInterface", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SparseArrays", "SpecialFunctions", "Statistics", "StatsBase", "StatsFuns", "Test"] -git-tree-sha1 = "a7756d098cbabec6b3ac44f369f74915e8cfd70a" +[[deps.Distributions]] +deps = ["FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SparseArrays", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns", "Test"] +git-tree-sha1 = "e76a3281de2719d7c81ed62c6ea7057380c87b1d" uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.79" +version = "0.25.98" + + [deps.Distributions.extensions] + DistributionsChainRulesCoreExt = "ChainRulesCore" + DistributionsDensityInterfaceExt = "DensityInterface" -[[DocStringExtensions]] + [deps.Distributions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" + +[[deps.DocStringExtensions]] deps = ["LibGit2"] -git-tree-sha1 = "5158c2b41018c5f7eb1470d558127ac274eca0c9" +git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.1" +version = "0.9.3" -[[Documenter]] +[[deps.Documenter]] deps = ["ANSIColoredPrinters", "Base64", "Dates", "DocStringExtensions", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"] -git-tree-sha1 = "6030186b00a38e9d0434518627426570aac2ef95" +git-tree-sha1 = "39fd748a73dce4c05a9655475e437170d8fb1b67" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "0.27.23" +version = "0.27.25" -[[Downloads]] +[[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" -[[DualNumbers]] +[[deps.DualNumbers]] deps = ["Calculus", "NaNMath", "SpecialFunctions"] git-tree-sha1 = "5837a837389fccf076445fce071c8ddaea35a566" uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74" version = "0.6.8" -[[Extents]] +[[deps.Extents]] git-tree-sha1 = "5e1e4c53fa39afe63a7d356e30452249365fba99" uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" version = "0.1.1" -[[FilePathsBase]] +[[deps.FilePathsBase]] deps = ["Compat", "Dates", "Mmap", "Printf", "Test", "UUIDs"] git-tree-sha1 = "e27c4ebe80e8699540f2d6c805cc12203b614f12" uuid = "48062228-2e41-5def-b9a4-89aafe57970f" version = "0.9.20" -[[FileWatching]] +[[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" -[[FillArrays]] +[[deps.FillArrays]] deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"] -git-tree-sha1 = "802bfc139833d2ba893dd9e62ba1767c88d708ae" +git-tree-sha1 = "2250347838b28a108d1967663cba57bfb3c02a58" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "0.13.5" +version = "1.3.0" -[[FixedPointNumbers]] +[[deps.FixedPointNumbers]] deps = ["Statistics"] git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" version = "0.8.4" -[[Formatting]] +[[deps.Formatting]] deps = ["Printf"] git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8" uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" version = "0.4.2" -[[FunctionWrappers]] +[[deps.FunctionWrappers]] git-tree-sha1 = "d62485945ce5ae9c0c48f124a84998d755bae00e" uuid = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" version = "1.1.3" -[[Future]] +[[deps.Future]] deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" -[[HypergeometricFunctions]] -deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions", "Test"] -git-tree-sha1 = "709d864e3ed6e3545230601f94e11ebc65994641" +[[deps.HypergeometricFunctions]] +deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] +git-tree-sha1 = "0ec02c648befc2f94156eaef13b0f38106212f3f" uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.11" +version = "0.3.17" -[[IOCapture]] +[[deps.IOCapture]] deps = ["Logging", "Random"] -git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a" +git-tree-sha1 = "d75853a0bdbfb1ac815478bacd89cd27b550ace6" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.2" +version = "0.2.3" -[[Indexing]] +[[deps.Indexing]] git-tree-sha1 = "ce1566720fd6b19ff3411404d4b977acd4814f9f" uuid = "313cdc1a-70c2-5d6a-ae34-0150d3930a38" version = "1.1.1" -[[InlineStrings]] +[[deps.InlineStrings]] deps = ["Parsers"] -git-tree-sha1 = "0cf92ec945125946352f3d46c96976ab972bde6f" +git-tree-sha1 = "9cc2baf75c6d09f9da536ddf58eb2f29dedaf461" uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" -version = "1.3.2" +version = "1.4.0" -[[InteractiveUtils]] +[[deps.InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -[[IntervalSets]] +[[deps.IntervalSets]] deps = ["Dates", "Random", "Statistics"] git-tree-sha1 = "16c0cc91853084cb5f58a78bd209513900206ce6" uuid = "8197267c-284f-5f27-9208-e0e47529a953" version = "0.7.4" -[[InverseFunctions]] -deps = ["Test"] -git-tree-sha1 = "49510dfcb407e572524ba94aeae2fced1f3feb0f" -uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.8" - -[[InvertedIndices]] -git-tree-sha1 = "82aec7a3dd64f4d9584659dc0b62ef7db2ef3e19" +[[deps.InvertedIndices]] +git-tree-sha1 = "0dc7b50b8d436461be01300fd8cd45aa0274b038" uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" -version = "1.2.0" +version = "1.3.0" -[[IrrationalConstants]] -git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151" +[[deps.IrrationalConstants]] +git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.1.1" +version = "0.2.2" -[[IterTools]] -git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5" +[[deps.IterTools]] +git-tree-sha1 = "4ced6667f9974fc5c5943fa5e2ef1ca43ea9e450" uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -version = "1.4.0" +version = "1.8.0" -[[IteratorInterfaceExtensions]] +[[deps.IteratorInterfaceExtensions]] git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" uuid = "82899510-4779-5014-852e-03e436cf321d" version = "1.0.0" -[[JLLWrappers]] +[[deps.JLLWrappers]] deps = ["Preferences"] git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" version = "1.4.1" -[[JSON]] +[[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.3" +version = "0.21.4" -[[JuliaInterpreter]] +[[deps.JuliaInterpreter]] deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"] -git-tree-sha1 = "847da597e4271c88bb54b8c7dfbeac44ea85ace4" +git-tree-sha1 = "6a125e6a4cb391e0b9adbd1afa9e771c2179f8ef" uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a" -version = "0.9.18" +version = "0.9.23" -[[LaTeXStrings]] +[[deps.LaTeXStrings]] git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996" uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" version = "1.3.0" -[[Lazy]] +[[deps.Lazy]] deps = ["MacroTools"] git-tree-sha1 = "1370f8202dac30758f3c345f9909b97f53d87d3f" uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" version = "0.15.1" -[[LibCURL]] +[[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" version = "0.6.3" -[[LibCURL_jll]] +[[deps.LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" version = "7.84.0+0" -[[LibGit2]] +[[deps.LibGit2]] deps = ["Base64", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -[[LibSSH2_jll]] +[[deps.LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" version = "1.10.2+0" -[[Libdl]] +[[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -[[LinearAlgebra]] -deps = ["Libdl", "libblastrampoline_jll"] +[[deps.LinearAlgebra]] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -[[LogExpFunctions]] -deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "946607f84feb96220f480e0422d3484c49c00239" +[[deps.LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "c3ce8e7420b3a6e071e0fe4745f5d4300e37b13f" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.19" +version = "0.3.24" -[[Logging]] + [deps.LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [deps.LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -[[LoweredCodeUtils]] +[[deps.LoweredCodeUtils]] deps = ["JuliaInterpreter"] -git-tree-sha1 = "dedbebe234e06e1ddad435f5c6f4b85cd8ce55f7" +git-tree-sha1 = "60168780555f3e663c536500aa790b6368adc02a" uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b" -version = "2.2.2" +version = "2.3.0" -[[MacroTools]] +[[deps.MacroTools]] deps = ["Markdown", "Random"] git-tree-sha1 = "42324d08725e200c23d4dfb549e0d5d89dede2d2" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" version = "0.5.10" -[[Markdown]] +[[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -[[MbedTLS_jll]] +[[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.0+0" +version = "2.28.2+0" -[[Missings]] +[[deps.Missings]] deps = ["DataAPI"] -git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f" +git-tree-sha1 = "f66bdc5de519e8f8ae43bdc598782d35a25b1272" uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.0.2" +version = "1.1.0" -[[Mmap]] +[[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" -[[MozillaCACerts_jll]] +[[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2022.2.1" +version = "2022.10.11" -[[NaNMath]] +[[deps.NaNMath]] deps = ["OpenLibm_jll"] -git-tree-sha1 = "a7c3d1da1189a1c2fe843a3bfa04d18d20eb3211" +git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "1.0.1" +version = "1.0.2" + +[[deps.NamedArrays]] +deps = ["Combinatorics", "DataStructures", "DelimitedFiles", "InvertedIndices", "LinearAlgebra", "Random", "Requires", "SparseArrays", "Statistics"] +git-tree-sha1 = "b84e17976a40cb2bfe3ae7edb3673a8c630d4f95" +uuid = "86f7a689-2022-50b4-a561-43c23ac3c673" +version = "0.9.8" -[[NetworkOptions]] +[[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" -[[OpenBLAS_jll]] +[[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.20+0" +version = "0.3.21+4" -[[OpenLibm_jll]] +[[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" version = "0.8.1+0" -[[OpenSpecFun_jll]] +[[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" version = "0.5.5+0" -[[OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +[[deps.OrderedCollections]] +git-tree-sha1 = "d321bf2de576bf25ec4d3e4360faca399afca282" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" +version = "1.6.0" -[[PDMats]] +[[deps.PDMats]] deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "cf494dca75a69712a72b80bc48f59dcf3dea63ec" +git-tree-sha1 = "67eae2738d63117a196f497d7db789821bce61d1" uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" -version = "0.11.16" +version = "0.11.17" -[[Parsers]] -deps = ["Dates"] -git-tree-sha1 = "3d5bf43e3e8b412656404ed9466f1dcbf7c50269" +[[deps.Parsers]] +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "4b2e829ee66d4218e0cef22c0a64ee37cf258c29" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.4.0" +version = "2.7.1" -[[Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.8.0" +version = "1.9.0" -[[PooledArrays]] +[[deps.PooledArrays]] deps = ["DataAPI", "Future"] git-tree-sha1 = "a6062fe4063cdafe78f4a0a81cfffb89721b30e7" uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" version = "1.4.2" -[[Preferences]] +[[deps.PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "9673d39decc5feece56ef3940e5dafba15ba0f81" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.1.2" + +[[deps.Preferences]] deps = ["TOML"] -git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" +git-tree-sha1 = "7eb1686b4f04b82f96ed7a4ea5890a4f0c7a09f1" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.3.0" +version = "1.4.0" -[[PrettyTables]] +[[deps.PrettyTables]] deps = ["Crayons", "Formatting", "LaTeXStrings", "Markdown", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "96f6db03ab535bdb901300f88335257b0018689d" +git-tree-sha1 = "213579618ec1f42dea7dd637a42785a608b1ea9c" uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "2.2.2" +version = "2.2.4" -[[Printf]] +[[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -[[ProgressLogging]] +[[deps.Profile]] +deps = ["Printf"] +uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" + +[[deps.ProgressLogging]] deps = ["Logging", "SHA", "UUIDs"] git-tree-sha1 = "80d919dee55b9c50e8d9e2da5eeafff3fe58b539" uuid = "33c8b6b6-d38a-422a-b730-caa89a2f386c" version = "0.1.4" -[[QuadGK]] +[[deps.ProgressMeter]] +deps = ["Distributed", "Printf"] +git-tree-sha1 = "d7a7aef8f8f2d537104f170139553b14dfe39fe9" +uuid = "92933f4c-e287-5a05-a399-4b506db050ca" +version = "1.7.2" + +[[deps.QuadGK]] deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "97aa253e65b784fd13e83774cadc95b38011d734" +git-tree-sha1 = "6ec7ac8412e83d57e313393220879ede1740f9ee" uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.6.0" +version = "2.8.2" -[[REPL]] +[[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -[[Random]] +[[deps.Random]] deps = ["SHA", "Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -[[RecipesBase]] -deps = ["SnoopPrecompile"] -git-tree-sha1 = "18c35ed630d7229c5584b945641a73ca83fb5213" +[[deps.RecipesBase]] +deps = ["PrecompileTools"] +git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.3.2" +version = "1.3.4" -[[Reexport]] +[[deps.Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" version = "1.2.2" -[[Requires]] +[[deps.Requires]] deps = ["UUIDs"] git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" -[[Revise]] +[[deps.Revise]] deps = ["CodeTracking", "Distributed", "FileWatching", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Pkg", "REPL", "Requires", "UUIDs", "Unicode"] -git-tree-sha1 = "dad726963ecea2d8a81e26286f625aee09a91b7c" +git-tree-sha1 = "1e597b93700fa4045d7189afa7c004e0584ea548" uuid = "295af30f-e4ad-537b-8983-00126c2a3abe" -version = "3.4.0" +version = "3.5.3" -[[Rmath]] +[[deps.Rmath]] deps = ["Random", "Rmath_jll"] -git-tree-sha1 = "bf3188feca147ce108c76ad82c2792c57abe7b1f" +git-tree-sha1 = "f65dcb5fa46aee0cf9ed6274ccbd597adc49aa7b" uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" -version = "0.7.0" +version = "0.7.1" -[[Rmath_jll]] +[[deps.Rmath_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "68db32dff12bb6127bac73c209881191bf0efbb7" +git-tree-sha1 = "6ed52fdd3382cf21947b15e8870ac0ddbff736da" uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" -version = "0.3.0+0" +version = "0.4.0+0" -[[SHA]] +[[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" version = "0.7.0" -[[ScientificTypes]] +[[deps.ScientificTypes]] deps = ["CategoricalArrays", "ColorTypes", "Dates", "Distributions", "PrettyTables", "Reexport", "ScientificTypesBase", "StatisticalTraits", "Tables"] git-tree-sha1 = "75ccd10ca65b939dab03b812994e571bf1e3e1da" uuid = "321657f4-b219-11e9-178b-2701a2544e81" version = "3.0.2" -[[ScientificTypesBase]] +[[deps.ScientificTypesBase]] git-tree-sha1 = "a8e18eb383b5ecf1b5e6fc237eb39255044fd92b" uuid = "30f210dd-8aff-4c5f-94ba-8e64358c1161" version = "3.0.0" -[[SentinelArrays]] +[[deps.SentinelArrays]] deps = ["Dates", "Random"] -git-tree-sha1 = "efd23b378ea5f2db53a55ae53d3133de4e080aa9" +git-tree-sha1 = "04bdff0b09c65ff3e06a05e3eb7b120223da3d39" uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.3.16" +version = "1.4.0" -[[Serialization]] +[[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -[[SnoopPrecompile]] -git-tree-sha1 = "f604441450a3c0569830946e5b33b78c928e1a85" +[[deps.SnoopPrecompile]] +deps = ["Preferences"] +git-tree-sha1 = "e760a70afdcd461cf01a575947738d359234665c" uuid = "66db9d55-30c0-4569-8b51-7e840670fc0c" -version = "1.0.1" +version = "1.0.3" -[[Sockets]] +[[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" [[deps.SoleBase]] deps = ["IterTools", "Logging", "Random"] -git-tree-sha1 = "4f941f62f7864f253e9063256dcef0fab72a44f4" +git-tree-sha1 = "d130869d52e513583ba9745bf691b862b86499a6" uuid = "4475fa32-7023-44a0-aa70-4813b230e492" -version = "0.9.1" +version = "0.9.2" -[[SoleData]] +[[deps.SoleData]] deps = ["CSV", "Catch22", "DataFrames", "DataStructures", "Logging", "Random", "Reexport", "ScientificTypes", "SoleBase", "Statistics"] -git-tree-sha1 = "338d3dd73e31b8932ce4cbe7e882ca92010a684b" -repo-rev = "dev" -repo-url = "https://github.com/aclai-lab/SoleData.jl" +git-tree-sha1 = "bf463a2f808fd4d75cef1959e0a042cc94004d3d" uuid = "123f1ae1-6307-4526-ab5b-aab3a92a2b8c" -version = "0.1.0" +version = "0.9.1" -[[SoleLogics]] -deps = ["DataStructures", "Dictionaries", "IterTools", "Lazy", "Random", "Reexport", "SoleBase"] -git-tree-sha1 = "86fd496e354c3bfbf845d6bec70a39c98347544b" -repo-rev = "algebras/giopaglia" -repo-url = "https://github.com/aclai-lab/SoleLogics.jl" +[[deps.SoleLogics]] +deps = ["DataStructures", "Dictionaries", "IterTools", "Lazy", "NamedArrays", "Random", "Reexport", "Revise", "SoleBase"] +git-tree-sha1 = "ef27f7225151535eb54f597c4375597d095daf79" uuid = "b002da8f-3cb3-4d91-bbe3-2953433912b5" -version = "0.1.1" +version = "0.3.0" -[[SoleModels]] -deps = ["FunctionWrappers", "Reexport", "Revise", "SoleBase", "SoleData", "SoleLogics", "Suppressor"] -git-tree-sha1 = "e95bb0d0dc1dbe7d914bcc342c33d86b0ed3157d" -repo-rev = "definitions/giopaglia" +[[deps.SoleModels]] +deps = ["BenchmarkTools", "ComputedFieldTypes", "DataStructures", "FillArrays", "FunctionWrappers", "LinearAlgebra", "Logging", "ProgressMeter", "Random", "Reexport", "Revise", "SoleBase", "SoleData", "SoleLogics", "StatsBase", "Suppressor", "ThreadSafeDicts"] +git-tree-sha1 = "23bda16c79ff5878812b3b4284ea8cfe8559fbd8" +repo-rev = "main" repo-url = "https://github.com/aclai-lab/SoleModels.jl" uuid = "4249d9c7-3290-4ddd-961c-e1d3ec2467f8" version = "0.1.0" -[[SortingAlgorithms]] +[[deps.SortingAlgorithms]] deps = ["DataStructures"] -git-tree-sha1 = "a4ada03f999bd01b3a25dcaa30b2d929fe537e00" +git-tree-sha1 = "c60ec5c62180f27efea3ba2908480f8055e17cee" uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.1.0" +version = "1.1.1" -[[SparseArrays]] -deps = ["LinearAlgebra", "Random"] +[[deps.SparseArrays]] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -[[SpecialFunctions]] -deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "d75bda01f8c31ebb72df80a46c88b25d1c79c56d" +[[deps.SpecialFunctions]] +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "7beb031cf8145577fbccacd94b8a8f4ce78428d3" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.1.7" +version = "2.3.0" -[[StatisticalTraits]] + [deps.SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" + + [deps.SpecialFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + +[[deps.StatisticalTraits]] deps = ["ScientificTypesBase"] git-tree-sha1 = "30b9236691858e13f167ce829490a68e1a597782" uuid = "64bff920-2084-43da-a3e6-9bb72801c0c9" version = "3.2.0" -[[Statistics]] +[[deps.Statistics]] deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.9.0" -[[StatsAPI]] +[[deps.StatsAPI]] deps = ["LinearAlgebra"] -git-tree-sha1 = "f9af7f195fb13589dd2e2d57fdb401717d2eb1f6" +git-tree-sha1 = "45a7769a04a3cf80da1c1c7c60caf932e6f4c9f7" uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.5.0" +version = "1.6.0" -[[StatsBase]] +[[deps.StatsBase]] deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] git-tree-sha1 = "d1bf48bfcc554a3761a133fe3a9bb01488e06916" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" version = "0.33.21" -[[StatsFuns]] -deps = ["ChainRulesCore", "HypergeometricFunctions", "InverseFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] -git-tree-sha1 = "89a3bfe98f5400f4ff58bb5cd1a9e46f95d08352" +[[deps.StatsFuns]] +deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] +git-tree-sha1 = "f625d686d5a88bcd2b15cd81f18f98186fdc0c9a" uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" -version = "1.1.0" +version = "1.3.0" + + [deps.StatsFuns.extensions] + StatsFunsChainRulesCoreExt = "ChainRulesCore" + StatsFunsInverseFunctionsExt = "InverseFunctions" + + [deps.StatsFuns.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" -[[StringManipulation]] +[[deps.StringManipulation]] git-tree-sha1 = "46da2434b41f41ac3594ee9816ce5541c6096123" uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" version = "0.3.0" -[[SuiteSparse]] +[[deps.SuiteSparse]] deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" -[[Suppressor]] -git-tree-sha1 = "c6ed566db2fe3931292865b966d6d140b7ef32a9" +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "Pkg", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "5.10.1+6" + +[[deps.Suppressor]] +deps = ["Logging"] +git-tree-sha1 = "37d1976ca8368f6adbe1d65a4deeeda6ee7faa31" uuid = "fd094767-a336-5f1f-9728-57cf17d0bbfb" -version = "0.2.1" +version = "0.2.4" -[[TOML]] +[[deps.TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.0" +version = "1.0.3" -[[TableTraits]] +[[deps.TableTraits]] deps = ["IteratorInterfaceExtensions"] git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" version = "1.0.1" -[[Tables]] +[[deps.Tables]] deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"] -git-tree-sha1 = "c79322d36826aa2f4fd8ecfa96ddb47b174ac78d" +git-tree-sha1 = "1544b926975372da01227b382066ab70e574a3ec" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.10.0" +version = "1.10.1" -[[Tar]] +[[deps.Tar]] deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" version = "1.10.0" -[[Test]] +[[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -[[TranscodingStreams]] +[[deps.ThreadSafeDicts]] +git-tree-sha1 = "8800982249f3657fbf176624db7d39107efb0062" +uuid = "4239201d-c60e-5e0a-9702-85d713665ba7" +version = "0.1.0" + +[[deps.TranscodingStreams]] deps = ["Random", "Test"] -git-tree-sha1 = "e4bdc63f5c6d62e80eb1c0043fcc0360d5950ff7" +git-tree-sha1 = "9a6ae7ed916312b41236fcef7e0af564ef934769" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.9.10" +version = "0.9.13" -[[UUIDs]] +[[deps.UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -[[Unicode]] +[[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -[[WeakRefStrings]] +[[deps.WeakRefStrings]] deps = ["DataAPI", "InlineStrings", "Parsers"] git-tree-sha1 = "b1be2855ed9ed8eac54e5caff2afcdb442d52c23" uuid = "ea10d353-3f73-51f8-a26c-33c1cb351aa5" version = "1.4.2" -[[Zlib_jll]] +[[deps.WorkerUtilities]] +git-tree-sha1 = "cd1659ba0d57b71a464a29e64dbc67cfe83d54e7" +uuid = "76eceee3-57b5-4d4a-8e66-0e911cebbf60" +version = "1.6.1" + +[[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.12+3" +version = "1.2.13+0" -[[catch22_jll]] +[[deps.catch22_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "7cfb827b3f62e20de3ccebaabf468ea979d098d9" uuid = "8a07c0c5-99ad-56cb-bc82-72eed1bb61ce" version = "0.4.0+0" -[[libblastrampoline_jll]] -deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.1.1+0" +version = "5.7.0+0" -[[nghttp2_jll]] +[[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" version = "1.48.0+0" -[[p7zip_jll]] +[[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" version = "17.4.0+0" diff --git a/docs/Project.toml b/docs/Project.toml index c5a16f8..09bd9bc 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,6 +1,5 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" SoleBase = "4475fa32-7023-44a0-aa70-4813b230e492" SoleData = "123f1ae1-6307-4526-ab5b-aab3a92a2b8c" SoleLogics = "b002da8f-3cb3-4d91-bbe3-2953433912b5" diff --git a/docs/install.jl b/docs/install.jl deleted file mode 100644 index c3aefce..0000000 --- a/docs/install.jl +++ /dev/null @@ -1,31 +0,0 @@ -# This file can be used to automatically resolve dependencies -# involving unregistered packages. -# To do so, simply call `install` one time for each package -# respecting the correct dependency order. - -using Pkg - -# Remove the specified package (do not abort if it is already removed) and reinstall it. -function install(package::String, url::String, rev::String) - printstyled(stdout, "\nRemoving: $package\n", color=:green) - try - Pkg.rm(package) - catch error - println(); showerror(stdout, error); println() - end - - printstyled(stdout, "\nFetching: $url at branch $rev\n", color=:green) - try - Pkg.add(url=url, rev=rev) - printstyled(stdout, "\nPackage $package instantiated correctly\n", color=:green) - catch error - println(); showerror(stdout, error); println() - end -end - -install("SoleBase", "https://github.com/aclai-lab/SoleBase.jl", "dev") -install("SoleData", "https://github.com/aclai-lab/SoleData.jl", "dev") -install("SoleLogics", "https://github.com/aclai-lab/SoleLogics.jl", "algebras/giopaglia") -install("SoleModels", "https://github.com/aclai-lab/SoleModels.jl", "definitions/giopaglia") - -Pkg.instantiate() diff --git a/src/install.jl b/src/install.jl deleted file mode 100644 index 14b4539..0000000 --- a/src/install.jl +++ /dev/null @@ -1,30 +0,0 @@ -# This file can be used to automatically resolve dependencies -# involving unregistered packages. -# To do so, simply call `install` one time for each package -# respecting the correct dependency order. - -using Pkg - -# Remove the specified package (do not abort if it is already removed) and reinstall it. -function install(package::String, url::String, rev::String) - printstyled(stdout, "\nRemoving: $package\n", color=:green) - try - Pkg.rm(package) - catch error - println(); showerror(stdout, error); println() - end - - printstyled(stdout, "\nFetching: $url at branch $rev\n", color=:green) - try - Pkg.add(url=url, rev=rev) - printstyled(stdout, "\nPackage $package instantiated correctly\n", color=:green) - catch error - println(); showerror(stdout, error); println() - end -end - -install("SoleBase", "https://github.com/aclai-lab/SoleBase.jl", "dev") -install("SoleData", "https://github.com/aclai-lab/SoleData.jl", "dev") -install("SoleLogics", "https://github.com/aclai-lab/SoleLogics.jl", "algebras/giopaglia") - -Pkg.instantiate() From ceb4a0c28a5149cd0d9d7a0b346b1d949ce30fe4 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:02:53 +0200 Subject: [PATCH 32/77] Fix actions --- .cirrus.yml | 12 +++++----- .github/workflows/CompatHelper.yml | 36 ++++++++++++++++++++++++++---- .github/workflows/ci.yml | 4 ++-- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 496d836..0312aeb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,13 +1,15 @@ freebsd_instance: - image: freebsd-12-0-release-amd64 + image_family: freebsd-13-1 task: name: FreeBSD artifacts_cache: folder: ~/.julia/artifacts env: - JULIA_VERSION: 1.0 - JULIA_VERSION: 1.6 - JULIA_VERSION: nightly + matrix: + - JULIA_VERSION: 1.8 + - JULIA_VERSION: 1 + - JULIA_VERSION: nightly + allow_failures: $JULIA_VERSION == 'nightly' install_script: - sh -c "$(fetch https://raw.githubusercontent.com/ararslan/CirrusCI.jl/master/bin/install.sh -o -)" build_script: @@ -15,4 +17,4 @@ task: test_script: - cirrusjl test coverage_script: - - cirrusjl coverage codecov coveralls + - cirrusjl coverage codecov diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml index cba9134..43dbf95 100644 --- a/.github/workflows/CompatHelper.yml +++ b/.github/workflows/CompatHelper.yml @@ -3,14 +3,42 @@ on: schedule: - cron: 0 0 * * * workflow_dispatch: +permissions: + contents: write + pull-requests: write jobs: CompatHelper: runs-on: ubuntu-latest steps: - - name: Pkg.add("CompatHelper") - run: julia -e 'using Pkg; Pkg.add("CompatHelper")' - - name: CompatHelper.main() + - name: Check if Julia is already available in the PATH + id: julia_in_path + run: which julia + continue-on-error: true + - name: Install Julia, but only if it is not already available in the PATH + uses: julia-actions/setup-julia@v1 + with: + version: '1.9' + arch: ${{ runner.arch }} + if: steps.julia_in_path.outcome != 'success' + - name: "Add the General registry via Git" + run: | + import Pkg + ENV["JULIA_PKG_SERVER"] = "" + Pkg.Registry.add("General") + shell: julia --color=yes {0} + - name: "Install CompatHelper" + run: | + import Pkg + name = "CompatHelper" + uuid = "aa819f21-2bde-4658-8897-bab36330d9b7" + version = "3" + Pkg.add(; name, uuid, version) + shell: julia --color=yes {0} + - name: "Run CompatHelper" + run: | + import CompatHelper + CompatHelper.main() + shell: julia --color=yes {0} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} - run: julia -e 'using CompatHelper; CompatHelper.main()' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 126b4ab..58b3b80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,10 +6,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - - name: Set up Julia 1.8.0 + - name: Set up Julia 1.9.0 uses: julia-actions/setup-julia@v1 with: - version: "1.8.0" + version: "1.9.0" - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 From 70f3d6e4d5336bf5c1196564e6b53d46f3fdc911 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:07:27 +0200 Subject: [PATCH 33/77] Fix README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4507c83..0fd2f43 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,10 @@ These definitions provide a unified base for implementing symbolic algorithms, s Remember: - An antecedent is any *condition* that can be checked on a machine learning *instance*, yielding a `true/false` value; -- A consequent is... anything you want, really, even another model (spoiler: a Branch with Branch consequents is a Decision Tree 😉). +- A consequent is another model, for example, a (final) constant model or branch to be applied. -More specifically, antecedents can be *logical formulas* and, in such case, the symbolic models +Within this framewokr, a decision tree is simply a branch with branch and final consequents. +NoteThat antecedents can consist of *logical formulas* and, in such case, the symbolic models are can be applied to *logical interpretations*. For more information, refer to [*SoleLogics.jl*](https://github.com/aclai-lab/SoleLogics.jl), the underlying logical layer. From 9d93998aae3d22d68876e4b20bc47045891a14bd Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:22:19 +0200 Subject: [PATCH 34/77] Fix test --- test/misc.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/misc.jl b/test/misc.jl index d8cdbc4..801a084 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -525,7 +525,7 @@ YES @test_nowarn listrules(rule_r) @test_nowarn ruleset = listrules(branch_r) -@test_broken listrules(dlmodel) +@test_nowarn listrules(dlmodel) @test listrules(r1_string) isa Vector{<:Rule} @test join(displaymodel.(listrules(r1_string); header = false)) == """ From be0d2517c54eefea27f68e14490d634f89e47c95 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:35:55 +0200 Subject: [PATCH 35/77] Fix TODOs in docstrings --- src/conditional-data/random.jl | 2 -- src/models/base.jl | 20 +++++++------ src/models/symbolic-utils.jl | 52 ---------------------------------- 3 files changed, 11 insertions(+), 63 deletions(-) diff --git a/src/conditional-data/random.jl b/src/conditional-data/random.jl index b667675..0cfedba 100644 --- a/src/conditional-data/random.jl +++ b/src/conditional-data/random.jl @@ -18,8 +18,6 @@ is limited to those with `feature`; - if `test_operator` is specified, then the set of metaconditions (feature-operator pairs) is limited to those with `test_operator`. -TODO Examples - See also [`BoundedExplicitConditionalAlphabet`](@ref), [`FeatCondition`](@ref), diff --git a/src/models/base.jl b/src/models/base.jl index 31a41d6..eb65447 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -283,13 +283,17 @@ end issymbolic(::AbstractModel)::Bool Return whether a model is symbolic or not. -A model is said to be `symbolic` when its application relies on checking formulas -of a certain logical language (see [`SoleLogics`](@ref) package) on the instance. -Symbolic models provide a form of transparent and interpretable modeling. - -Instead, a model is said to be functional when it encodes an algebraic mathematical -function (e.g., a neural network). -TODO explain listrules/cascade/rules A symbolic model is one where the computation has a *rule-base structure*. +A model is said to be `symbolic` when its functioning simply relies on checking conditions +(e.g., based on formulas of a certain logical language, see [`SoleLogics`](@ref) package) +on the instance. +Essentially, symbolic models have a *rule-based structure*, +and provide a form of transparent and interpretable computation. + +Examples of purely symbolic models are [`Rule`](@ref)s, [`Branch`](@ref), +[`DecisionList`](@ref)s and [`DecisionTree`](@ref)s. +Examples of non-symbolic models are those encoding algebraic mathematical +functions (e.g., a neural networks). Note that [`DecisionForest`](@ref)s are +not purely symbolic. See also [`apply`](@ref), @@ -397,8 +401,6 @@ in order to compute the outcome. Over efficiency concerns, it is mandatory to ma the output type `O` by wrapping the `Function` into an object of type `FunctionWrapper{O}`. -TODO @Michele explain functional_args/functional_kwargs - See also [`ConstantModel`](@ref), [`FunctionWrapper`](@ref), [`FinalModel`](@ref). """ struct FunctionModel{O} <: FinalModel{O} diff --git a/src/models/symbolic-utils.jl b/src/models/symbolic-utils.jl index f80b6f8..c6f01af 100644 --- a/src/models/symbolic-utils.jl +++ b/src/models/symbolic-utils.jl @@ -183,58 +183,6 @@ The keyword argument `force_syntaxtree`, when set to true, causes the logical an in the returned rules to be represented as `SyntaxTree`s, as opposed to other syntax structure (e.g., `LeftmostConjunctiveForm`). -# Examples -# TODO @Michi questi esempi non sono chiari: cosa è r2_string? -```julia-repl -@test listrules(r2_string) isa Vector{<:Rule} -julia> print(join(displaymodel.(listrules(rule); header = false))) -┐¬(r) -└ ✔ YES - -julia> print(join(displaymodel.(listrules(decision_list); header = false))) -┐(r ∧ s) ∧ t -└ ✔ YES -┐¬(r) -└ ✔ YES -┐⊤ -└ ✔ YES - -@test listrules(rcmodel) isa Vector{<:Rule} -julia> print(join(displaymodel.(listrules(rule_cascade); header = false))) -┐(p ∧ (q ∨ r)) ∧ ((p ∧ (q ∨ r)) ∧ (p ∧ (q ∨ r))) -└ ✔ 1 - -julia> print(join(displaymodel.(listrules(branch); header = false))) -┐r ∧ s -└ ✔ YES -┐r ∧ (¬(s)) -└ ✔ NO -┐(¬(r)) ∧ (t ∧ q) -└ ✔ YES -┐(¬(r)) ∧ (t ∧ (¬(q))) -└ ✔ NO -┐(¬(r)) ∧ (¬(t)) -└ ✔ YES - -julia> print(join(displaymodel.(listrules(decision_tree); header = false))) -┐r ∧ s -└ ✔ YES -┐r ∧ (¬(s)) -└ ✔ NO -┐(¬(r)) ∧ (t ∧ q) -└ ✔ YES -┐(¬(r)) ∧ (t ∧ (¬(q))) -└ ✔ NO -┐(¬(r)) ∧ (¬(t)) -└ ✔ YES - -julia> print(join(displaymodel.(listrules(mixed_symbolic_model); header = false))) -┐q -└ ✔ 2 -┐¬(q) -└ ✔ 1.5 -``` - See also [`listimmediaterules`](@ref), [`issymbolic`](@ref), [`FinalModel`](@ref), [`AbstractModel`](@ref). """ From d6cb5e2fc16307b2b99056369044e8219bd6ca1a Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:43:46 +0200 Subject: [PATCH 36/77] Add compat --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 71dd0ef..a7dab16 100644 --- a/Project.toml +++ b/Project.toml @@ -36,6 +36,7 @@ SoleData = "0.9.1" SoleLogics = "0.3.0" StatsBase = "0.33" Suppressor = "0.2" +ThreadSafeDicts = "0.1.0" julia = "1" [extras] From c00b4dde0ea0ccc4a2483ff7af82822b19a17693 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sat, 8 Jul 2023 04:25:38 +0200 Subject: [PATCH 37/77] Improve displaymodel, listrules. Add joinrules, readmetrics. islogiseed/ismultilogiseed. print_progress. saved little space in dimensional-logisets. Fixing multimodal. booleanconditions->antecedents. check-modes next to antecedents instead of formulas. --- src/MLJ-utils.jl | 8 +- src/SoleModels.jl | 88 +++--- src/logisets/check-modes.jl | 110 -------- src/logisets/check.jl | 4 +- .../dataset-bindings.jl | 2 +- .../dimensional-structures/logiset.jl | 29 +- .../onestep-memosets.jl | 24 +- .../representatives/Full1DFrame+IA.jl | 15 +- src/logisets/main.jl | 1 - src/logisets/multilogiset.jl | 82 +----- src/logisets/scalar/dataset-bindings.jl | 46 ++- src/logisets/supported-logiset.jl | 6 +- src/models/antecedents.jl | 157 ++++++++++ src/models/base.jl | 267 +++++------------- src/models/check-modes.jl | 73 +++++ src/models/evaluation.jl | 53 +++- src/models/multi-antecedents.jl | 105 +++++++ src/models/print.jl | 95 ++++--- src/models/symbolic-utils.jl | 174 +++++++++--- test/logisets/multilogisets.jl | 4 +- test/misc.jl | 56 ++-- 21 files changed, 806 insertions(+), 593 deletions(-) delete mode 100644 src/logisets/check-modes.jl create mode 100644 src/models/antecedents.jl create mode 100644 src/models/check-modes.jl create mode 100644 src/models/multi-antecedents.jl diff --git a/src/MLJ-utils.jl b/src/MLJ-utils.jl index 9496e6b..e4b7202 100644 --- a/src/MLJ-utils.jl +++ b/src/MLJ-utils.jl @@ -17,7 +17,13 @@ function fix_y(y) nothing end end - y = Vector{(is_classification ? String : Float64)}(y) + y = begin + if is_classification + Vector{String}(string.(y)) + else + Vector{Float64}(y) + end + end y, classes_seen end diff --git a/src/SoleModels.jl b/src/SoleModels.jl index 7abd637..5ad8b23 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -20,45 +20,17 @@ include("utils.jl") using .utils -export outcometype, outputtype - -export Rule, Branch -export check_antecedent -export antecedent, consequent -export posconsequent, negconsequent - -export DecisionList -export rulebase, defaultconsequent - -export DecisionTree -export root - -export MixedSymbolicModel, DecisionForest - -include("models/base.jl") - -export printmodel, displaymodel - -include("models/print.jl") - -export immediatesubmodels, listimmediaterules -export listrules - -include("models/symbolic-utils.jl") - -export Label, bestguess - -include("machine-learning.jl") - -export rulemetrics, leafmetrics - -include("models/evaluation.jl") - export minify, isminifiable # Minification interface for lossless data compression include("utils/minify.jl") +include("MLJ-utils.jl") + +############################################################################################ +############################################################################################ +############################################################################################ + export AbstractFeature, Feature export propositions @@ -75,8 +47,6 @@ export ExplicitLogiset, ScalarCondition export ninstances export MultiLogiset, modality, nmodalities, modalities -export MultiFormula - export UnivariateNamedFeature, UnivariateFeature @@ -109,8 +79,50 @@ using .DimensionalDatasets: IA2DRelations using .DimensionalDatasets: identityrel using .DimensionalDatasets: globalrel -const GenericDataset = Union{SoleData.AbstractDimensionalDataset,AbstractDataFrame,AbstractLogiset,MultiLogiset} +############################################################################################ +############################################################################################ +############################################################################################ -include("MLJ-utils.jl") +include("models/check-modes.jl") + +include("models/antecedents.jl") + +export MultiAntecedent + +include("models/multi-antecedents.jl") + +export outcometype, outputtype + +export Rule, Branch +export check_antecedent +export antecedent, consequent +export posconsequent, negconsequent + +export DecisionList +export rulebase, defaultconsequent + +export DecisionTree +export root + +export MixedSymbolicModel, DecisionForest + +include("models/base.jl") + +export printmodel, displaymodel + +include("models/print.jl") + +export immediatesubmodels, listimmediaterules +export listrules, joinrules + +include("models/symbolic-utils.jl") + +export Label, bestguess + +include("machine-learning.jl") + +export rulemetrics, readmetrics + +include("models/evaluation.jl") end diff --git a/src/logisets/check-modes.jl b/src/logisets/check-modes.jl deleted file mode 100644 index becf1e8..0000000 --- a/src/logisets/check-modes.jl +++ /dev/null @@ -1,110 +0,0 @@ - -############################################################################################ -# Check modes -############################################################################################ - -using SoleLogics: AbstractMultiModalFrame - -""" -Abstract type for model checking modes. - -See also -[`GlobalCheck`](@ref), -[`CenteredCheck`](@ref), -[`WorldCheck`](@ref). -""" -abstract type CheckMode end - -""" -A model checking mode where the formula to check is global and no specific -world is required. - -See also -[`CheckMode`](@ref). -""" -struct GlobalCheck <: CheckMode end; - -abstract type GroundedCheck <: CheckMode end - -""" -A model checking mode where the formula is checked on the central world; -note that the central world must be defined via - -See also -[`CheckMode`](@ref). -""" -struct CenteredCheck <: GroundedCheck end; - -function getworld(fr::AbstractMultiModalFrame{W}, checkmode::CenteredCheck) where {W<:AbstractWorld} - SoleLogics.centeredworld(fr) -end - -""" -A model checking mode where the formula is checked on the central world - -See also -[`CheckMode`](@ref), -[`CenteredCheck`](@ref). -""" -struct WorldCheck{W<:AbstractWorld} <: GroundedCheck - w::W -end - -function getworld(::AbstractMultiModalFrame{W}, checkmode::WorldCheck{W}) where {W<:AbstractWorld} - checkmode.w -end - -function check( - φ::SoleLogics.AbstractFormula, - X::AbstractLogiset, - i_instance::Integer, - checkmode::GlobalCheck, - args...; - kwargs... -) - check(φ, X, i_instance, nothing, args...; kwargs...) -end - -function check( - φ::SoleLogics.AbstractFormula, - X::AbstractLogiset, - i_instance::Integer, - checkmode::GroundedCheck, - args...; - kwargs... -) - check(φ, X, i_instance, getworld(frame(X, i_instance), checkmode), args...; kwargs...) -end - -############################################################################################ - -import SoleLogics: tree - -struct AnchoredFormula{ - F<:AbstractFormula, - C<:CheckMode, -} <: AbstractSyntaxStructure - - formula::F - - checkmode::C -end - -# tree(f::AnchoredFormula) = tree(f.formula) -function SoleLogics.tree(f::AnchoredFormula) - return error("Cannot convert object of type AnchoredFormula to a SyntaxTree.") -end - -function syntaxstring(f::AnchoredFormula; kwargs...) - "@$(f.checkmode)($(syntaxstring(f.formula)))" -end - -function check( - φ::AnchoredFormula, - X::AbstractLogiset, - i_instance::Integer, - args...; - kwargs... -) - check(φ.formula, X, i_instance, φ.checkmode, args...; kwargs...) -end diff --git a/src/logisets/check.jl b/src/logisets/check.jl index 19eea11..2aba448 100644 --- a/src/logisets/check.jl +++ b/src/logisets/check.jl @@ -17,7 +17,7 @@ function check( φ::SoleLogics.SyntaxTree, X::AbstractLogiset{W,U}, i_instance::Integer, - w::Union{Nothing,<:AbstractWorld,AbstractVector{<:AbstractWorld}} = nothing; + w::Union{Nothing,<:AbstractWorld} = nothing; use_memo::Union{Nothing,AbstractMemoset{<:AbstractWorld},AbstractVector{<:AbstractDict{<:FT,<:WorldSet}}} = nothing, perform_normalization::Bool = true, memo_max_height::Union{Nothing,Int} = nothing, @@ -26,8 +26,6 @@ function check( if isnothing(w) w = nothing - elseif w isa AbstractVector - w = w[i_instance] end @assert SoleLogics.isglobal(φ) || !isnothing(w) "Please, specify a world in order " * "to check non-global formula: $(syntaxstring(φ))." diff --git a/src/logisets/dimensional-structures/dataset-bindings.jl b/src/logisets/dimensional-structures/dataset-bindings.jl index 4c9204c..99c1f23 100644 --- a/src/logisets/dimensional-structures/dataset-bindings.jl +++ b/src/logisets/dimensional-structures/dataset-bindings.jl @@ -37,7 +37,7 @@ function initlogiset( U = Union{featvaltype.(features)...} featstruct = Array{U,length(_maxchannelsize)*2+2}( undef, - vcat([[s,s+1] for s in _maxchannelsize]...)..., + vcat([[s, s] for s in _maxchannelsize]...)..., _ninstances, length(features) ) diff --git a/src/logisets/dimensional-structures/logiset.jl b/src/logisets/dimensional-structures/logiset.jl index 746a021..66df606 100644 --- a/src/logisets/dimensional-structures/logiset.jl +++ b/src/logisets/dimensional-structures/logiset.jl @@ -181,7 +181,7 @@ function readfeature( w::Interval, f::AbstractFeature ) where {U} - featchannel[w.x, w.y] + featchannel[w.x, w.y-1] end @@ -221,7 +221,7 @@ function readfeature( w::Interval2D, f::AbstractFeature ) where {U} - featchannel[w.x.x, w.x.y, w.y.x, w.y.y] + featchannel[w.x.x, w.x.y-1, w.y.x, w.y.y-1] end ############################################################################################ @@ -257,7 +257,7 @@ end end end - X.featstruct[w.x, w.y, i_instance, i_feature] + X.featstruct[w.x, w.y-1, i_instance, i_feature] end @inline function featvalue( @@ -273,8 +273,7 @@ end error("Could not find feature $(feature) in memoset of type $(typeof(X)).") end end - - X.featstruct[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] + X.featstruct[w.x.x, w.x.y-1, w.y.x, w.y.y-1, i_instance, i_feature] end ############################################################################################ @@ -312,7 +311,7 @@ end end end - X.featstruct[w.x, w.y, i_instance, i_feature] = featval + X.featstruct[w.x, w.y-1, i_instance, i_feature] = featval end @inline function featvalue!( @@ -330,7 +329,7 @@ end end end - X.featstruct[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] = featval + X.featstruct[w.x.x, w.x.y-1, w.y.x, w.y.y-1, i_instance, i_feature] = featval end ############################################################################################ @@ -453,15 +452,15 @@ function capacity(X::UniformFullDimensionalLogiset{U,<:Interval}) where {U} prod([ ninstances(X), nfeatures(X), - div(size(X, 1)*(size(X, 2)),2), + div(size(X, 1)*(size(X, 2)+1),2), ]) end function capacity(X::UniformFullDimensionalLogiset{U,<:Interval2D}) where {U} prod([ ninstances(X), nfeatures(X), - div(size(X, 1)*(size(X, 2)),2), - div(size(X, 3)*(size(X, 4)),2), + div(size(X, 1)*(size(X, 2)+1),2), + div(size(X, 3)*(size(X, 4)+1),2), ]) end @@ -471,13 +470,13 @@ function hasnans(X::UniformFullDimensionalLogiset{U,OneWorld}) where {U} any(_isnan.(X.featstruct)) end function hasnans(X::UniformFullDimensionalLogiset{U,<:Interval}) where {U} - any([hasnans(X.featstruct[x,y,:,:]) - for x in 1:size(X, 1) for y in (x+1):size(X, 2)]) + any([hasnans(X.featstruct[x,y-1,:,:]) + for x in 1:size(X, 1) for y in (x+1):(size(X, 2)+1)]) end function hasnans(X::UniformFullDimensionalLogiset{U,<:Interval2D}) where {U} - any([hasnans(X.featstruct[xx,xy,yx,yy,:,:]) - for xx in 1:size(X, 1) for xy in (xx+1):size(X, 2) - for yx in 1:size(X, 3) for yy in (yx+1):size(X, 4)]) + any([hasnans(X.featstruct[xx,xy-1,yx,yy-1,:,:]) + for xx in 1:size(X, 1) for xy in (xx+1):(size(X, 2)+1) + for yx in 1:size(X, 3) for yy in (yx+1):(size(X, 4)+1)]) end ############################################################################################ diff --git a/src/logisets/dimensional-structures/onestep-memosets.jl b/src/logisets/dimensional-structures/onestep-memosets.jl index 4840106..3129260 100644 --- a/src/logisets/dimensional-structures/onestep-memosets.jl +++ b/src/logisets/dimensional-structures/onestep-memosets.jl @@ -133,7 +133,7 @@ function capacity(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interva ninstances(Xm), nmetaconditions(Xm), nrelations(Xm), - div(size(Xm, 1)*(size(Xm, 2)),2), + div(size(Xm, 1)*(size(Xm, 1)+1),2), ]) end function capacity(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interval2D}) where {U} @@ -141,8 +141,8 @@ function capacity(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interva ninstances(Xm), nmetaconditions(Xm), nrelations(Xm), - div(size(Xm, 1)*(size(Xm, 2)),2), - div(size(Xm, 3)*(size(Xm, 4)),2), + div(size(Xm, 1)*(size(Xm, 1)+1),2), + div(size(Xm, 3)*(size(Xm, 3)+1),2), ]) end @@ -164,7 +164,7 @@ end i_metacond :: Integer, i_relation :: Integer ) where {U,W<:Interval} - Xm.d[w.x, w.y, i_instance, i_metacond, i_relation] + Xm.d[w.x, w.y-1, i_instance, i_metacond, i_relation] end @inline function Base.getindex( Xm :: UniformFullDimensionalOneStepRelationalMemoset{U,W}, @@ -173,7 +173,7 @@ end i_metacond :: Integer, i_relation :: Integer ) where {U,W<:Interval2D} - Xm.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_metacond, i_relation] + Xm.d[w.x.x, w.x.y-1, w.y.x, w.y.y-1, i_instance, i_metacond, i_relation] end ############################################################################################ @@ -197,7 +197,7 @@ Base.@propagate_inbounds @inline function Base.setindex!( i_metacond::Integer, i_relation::Integer, ) where {U} - Xm.d[w.x, w.y, i_instance, i_metacond, i_relation] = gamma + Xm.d[w.x, w.y-1, i_instance, i_metacond, i_relation] = gamma end Base.@propagate_inbounds @inline function Base.setindex!( @@ -208,7 +208,7 @@ Base.@propagate_inbounds @inline function Base.setindex!( i_metacond::Integer, i_relation::Integer, ) where {U} - Xm.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_metacond, i_relation] = gamma + Xm.d[w.x.x, w.x.y-1, w.y.x, w.y.y-1, i_instance, i_metacond, i_relation] = gamma end ############################################################################################ @@ -217,13 +217,13 @@ function hasnans(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,OneWorld}) any(_isnan.(Xm.d)) end function hasnans(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interval}) where {U} - any([hasnans(Xm.d[x,y,:,:,:]) - for x in 1:size(Xm, 1) for y in (x+1):size(Xm, 2)]) + any([hasnans(Xm.d[x,y-1,:,:,:]) + for x in 1:size(Xm, 1) for y in (x+1):(size(Xm, 2)+1)]) end function hasnans(Xm::UniformFullDimensionalOneStepRelationalMemoset{U,<:Interval2D}) where {U} - any([hasnans(Xm.d[xx,xy,yx,yy,:,:,:]) - for xx in 1:size(Xm, 1) for xy in (xx+1):size(Xm, 2) - for yx in 1:size(Xm, 3) for yy in (yx+1):size(Xm, 4)]) + any([hasnans(Xm.d[xx,xy-1,yx,yy-1,:,:,:]) + for xx in 1:size(Xm, 1) for xy in (xx+1):(size(Xm, 2)+1) + for yx in 1:size(Xm, 3) for yy in (yx+1):(size(Xm, 4)+1)]) end ############################################################################################ diff --git a/src/logisets/dimensional-structures/representatives/Full1DFrame+IA.jl b/src/logisets/dimensional-structures/representatives/Full1DFrame+IA.jl index d242ac8..f804420 100644 --- a/src/logisets/dimensional-structures/representatives/Full1DFrame+IA.jl +++ b/src/logisets/dimensional-structures/representatives/Full1DFrame+IA.jl @@ -136,13 +136,8 @@ representatives(fr::Full1DFrame, w::Interval{Int}, ::_IA_E, ::UnivariateMax, :: # |IA_I ? | # '-----------------------------' # TODO write the correct `representatives` methods, instead of these fallbacks: -representatives(fr::Full1DFrame, w::Interval, r::_IA_AorO, f::AbstractFeature, a::Aggregator) = - Iterators.flatten([representatives(fr, w, r, f, a) for r in IA72IARelations(IA_AorO)]) -representatives(fr::Full1DFrame, w::Interval, r::_IA_AiorOi, f::AbstractFeature, a::Aggregator) = - Iterators.flatten([representatives(fr, w, r, f, a) for r in IA72IARelations(IA_AiorOi)]) -representatives(fr::Full1DFrame, w::Interval, r::_IA_DorBorE, f::AbstractFeature, a::Aggregator) = - Iterators.flatten([representatives(fr, w, r, f, a) for r in IA72IARelations(IA_DorBorE)]) -representatives(fr::Full1DFrame, w::Interval, r::_IA_DiorBiorEi, f::AbstractFeature, a::Aggregator) = - Iterators.flatten([representatives(fr, w, r, f, a) for r in IA72IARelations(IA_DiorBiorEi)]) -representatives(fr::Full1DFrame, w::Interval, r::_IA_I, f::AbstractFeature, a::Aggregator) = - Iterators.flatten([representatives(fr, w, r, f, a) for r in IA72IARelations(IA_I)]) +representatives(fr::Full1DFrame, w::Interval, r::_IA_AorO, f::AbstractFeature, a::Aggregator) = Iterators.flatten([representatives(fr, w, r, f, a) for r in IA72IARelations(IA_AorO)]) +representatives(fr::Full1DFrame, w::Interval, r::_IA_AiorOi, f::AbstractFeature, a::Aggregator) = Iterators.flatten([representatives(fr, w, r, f, a) for r in IA72IARelations(IA_AiorOi)]) +representatives(fr::Full1DFrame, w::Interval, r::_IA_DorBorE, f::AbstractFeature, a::Aggregator) = Iterators.flatten([representatives(fr, w, r, f, a) for r in IA72IARelations(IA_DorBorE)]) +representatives(fr::Full1DFrame, w::Interval, r::_IA_DiorBiorEi, f::AbstractFeature, a::Aggregator) = Iterators.flatten([representatives(fr, w, r, f, a) for r in IA72IARelations(IA_DiorBiorEi)]) +representatives(fr::Full1DFrame, w::Interval, r::_IA_I, f::AbstractFeature, a::Aggregator) = Iterators.flatten([representatives(fr, w, r, f, a) for r in IA72IARelations(IA_I)]) diff --git a/src/logisets/main.jl b/src/logisets/main.jl index 41257ae..d8b9abb 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -45,7 +45,6 @@ include("multilogiset.jl") export check, AnchoredFormula # Model checking algorithms for logisets and multilogisets -include("check-modes.jl") include("check.jl") export nfeatures diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index bcb01f4..66220fb 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -1,3 +1,5 @@ +using SoleLogics: AbstractKripkeStructure, AbstractInterpretationSet, AbstractFrame +using SoleLogics: TruthValue import SoleData: modality, nmodalities, eachmodality """ @@ -13,7 +15,7 @@ See also [`AbstractLogiset`](@ref), [`minify`](@ref). """ -struct MultiLogiset{L<:AbstractLogiset} +struct MultiLogiset{L<:AbstractLogiset} <: AbstractInterpretationSet{AbstractKripkeStructure{W where W<:AbstractWorld,C where C<:AbstractCondition{_F where _F<:AbstractFeature},T where T<:TruthValue,FR where FR<:AbstractFrame{W where W<:SoleLogics.AbstractWorld}}} modalities :: Vector{L} @@ -93,7 +95,7 @@ function displaystructure(X::MultiLogiset; indent_str = "", include_ninstances = else out *= "$(indent_str)├" end - out *= "[$(i_modality)] " + out *= "{$i_modality} " # \t\t\t$(humansize(mod))\t(worldtype: $(worldtype(mod)))" out *= displaystructure(mod; indent_str = indent_str * (i_modality == nmodalities(X) ? " " : "│ "), include_ninstances = false) push!(pieces, out) @@ -163,75 +165,6 @@ end ############################################################################################ -using SoleLogics: AbstractFormula, AbstractSyntaxStructure, AbstractOperator -import SoleLogics: syntaxstring, joinformulas - -import SoleLogics: tree - -""" -A logical formula that can be checked on a `MultiLogiset`, associating -a set of subformulas to each modality -""" -struct MultiFormula{ - F<:AbstractFormula, -} <: AbstractSyntaxStructure - formulas::Dict{Int,F} -end - -function MultiFormula(i_modality, formula::SyntaxTree) - MultiFormula(Dict{Int,SyntaxTree}(i_modality => formula)) -end - -function SoleLogics.tree(f::MultiFormula) - return error("Cannot convert object of type MultiFormula to a SyntaxTree.") -end - -function syntaxstring( - f::MultiFormula; - hidemodality = false, - variable_names_map::Union{Nothing,AbstractDict,AbstractVector,AbstractVector{<:Union{AbstractDict,AbstractVector}}} = nothing, - kwargs... -) - map_is_multimodal = begin - if !isnothing(variable_names_map) && all(e->!(e isa Union{AbstractDict,AbstractVector}), variable_names_map) - @warn "With multimodal formulas, variable_names_map should be a vector of vectors/maps of " * - "variable names. Got $(typeof(variable_names_map)) instead. This may fail, " * - "or lead to unexpected results." - false - else - !isnothing(variable_names_map) - end - end - join([begin - _variable_names_map = map_is_multimodal ? variable_names_map[i_modality] : variable_names_map - φ = syntaxstring(f.formulas[i_modality]; variable_names_map = _variable_names_map, kwargs...) - hidemodality ? "$φ" : "{$(i_modality)}($φ)" - end for i_modality in sort(collect(keys(f.formulas)))], " $(CONJUNCTION) ") -end - -function joinformulas(op::SoleLogics.AbstractOperator, children::NTuple{N,MultiFormula{F}}) where {N,F} - formulas = Dict{Int,F}() - i_modalities = unique(vcat(collect.(keys.([ch.formulas for ch in children]))...)) - for i_modality in i_modalities - chs = filter(ch->haskey(ch.formulas, i_modality), children) - fs = map(ch->ch.formulas[i_modality], chs) - formulas[i_modality] = joinformulas(op, fs) - end - return MultiFormula(formulas) -end - -function check( - φ::MultiFormula, - X::MultiLogiset, - i_instance::Integer, - args...; - kwargs..., -) - # TODO in the fuzzy case: use collatetruth(fuzzy algebra, ∧, ...) - all([check(f, X, i_modality, i_instance, args...; kwargs...) - for (i_modality, f) in φ.formulas]) -end - function check( φ::SoleLogics.AbstractFormula, X::MultiLogiset, @@ -242,10 +175,3 @@ function check( ) check(φ, modality(X, i_modality), i_instance, args...; kwargs...) end - - -# # TODO join MultiFormula leads to a SyntaxTree with MultiFormula children -# function joinformulas(op::AbstractOperator, children::NTuple{N,MultiFormula{F}}) where {N,F} -# end - -# TODO MultiFormula parser diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 7c2ade2..dc892c6 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -1,6 +1,10 @@ +using ProgressMeter using SoleData: AbstractMultiModalDataset import SoleData: ninstances, nvariables, nmodalities, eachmodality +function islogiseed(dataset) + return error("Please, provide method islogiseed(dataset::$(typeof(dataset))).") +end function initlogiset(dataset, features) return error("Please, provide method initlogiset(dataset::$(typeof(dataset)), features::$(typeof(features))).") end @@ -25,7 +29,7 @@ function allworlds(dataset, i_instance) end # Multimodal dataset interface -function ismultimodal(dataset) +function ismultilogiseed(dataset) false end function nmodalities(dataset) @@ -35,11 +39,14 @@ function eachmodality(dataset) return error("Please, provide method eachmodality(dataset::$(typeof(dataset))).") end -function ismultimodal(dataset::AbstractMultiModalDataset) +function ismultilogiseed(dataset::MultiLogiset) + true +end +function ismultilogiseed(dataset::AbstractMultiModalDataset) true end -function ismultimodal(dataset::Union{AbstractVector,Tuple}) +function ismultilogiseed(dataset::Union{AbstractVector,Tuple}) true end function nmodalities(dataset::Union{AbstractVector,Tuple}) @@ -69,7 +76,7 @@ If `dataset` represents a multimodal dataset, the following methods should be de while its modalities (iterated via `eachmodality`) should provide the methods above: ```julia - ismultimodal(dataset) + ismultilogiseed(dataset) nmodalities(dataset) eachmodality(dataset) ``` @@ -90,10 +97,11 @@ function scalarlogiset( use_onestep_memoization :: Union{Bool,Type{<:AbstractOneStepMemoset}} = !isnothing(conditions) && !isnothing(relations), onestep_precompute_globmemoset :: Bool = (use_onestep_memoization != false), onestep_precompute_relmemoset :: Bool = false, + print_progress :: Bool = false, ) some_features_were_specified = !isnothing(features) - if ismultimodal(dataset) + if ismultilogiseed(dataset) kwargs = (; use_full_memoization = use_full_memoization, @@ -146,9 +154,9 @@ function scalarlogiset( is_propositional_dataset = all(i_instance->nworlds(frame(dataset, i_instance)) == 1, 1:ninstances(dataset)) features = begin if is_propositional_dataset - [UnivariateValue(i_var) for i_var in 1:nvariables(dataset)] + [UnivariateValue{vareltype(dataset, i_var)}(i_var) for i_var in 1:nvariables(dataset)] else - cat([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:nvariables(dataset)]...) + vcat([[UnivariateMax{vareltype(dataset, i_var)}(i_var), UnivariateMin{vareltype(dataset, i_var)}(i_var)] for i_var in 1:nvariables(dataset)]...) end end end @@ -193,7 +201,9 @@ function scalarlogiset( _ninstances = ninstances(dataset) # Compute features - # p = Progress(_ninstances, 1, "Computing EMD...") + if print_progress + p = Progress(_ninstances, 1, "Computing logiset...") + end @inbounds Threads.@threads for i_instance in 1:_ninstances for w in allworlds(dataset, i_instance) for (i_feature,feature) in enum_features @@ -201,7 +211,9 @@ function scalarlogiset( featvalue!(X, featval, i_instance, w, feature, i_feature) end end - # next!(p) + if print_progress + next!(p) + end end if !use_full_memoization && !use_onestep_memoization @@ -375,7 +387,7 @@ function naturalgrouping( end columnnames = names(X) - percol_framess = [unique((i_instance)->(_frame(X[:,col], i_instance)), 1:ninstances(X)) for col in columnnames] + percol_framess = [unique(map((i_instance)->(_frame(X[:,col], i_instance)), 1:ninstances(X))) for col in columnnames] # Must have common frame across instances _uniform_columns = (length.(percol_framess) .== 1) @@ -409,7 +421,19 @@ function naturalgrouping( percol_frames = getindex.(percol_framess, 1) var_grouping = begin - unique_frames = sort(unique(percol_frames)) + unique_frames = sort(unique(percol_frames); lt = (x,y)->begin + if hasmethod(dimensionality, (typeof(x),)) && hasmethod(dimensionality, (typeof(y),)) + if dimensionality(x) == dimensionality(y) + isless(SoleData.channelsize(x), SoleData.channelsize(y)) + else + isless(dimensionality(x), dimensionality(y)) + end + elseif hasmethod(dimensionality, (typeof(x),)) + true + else + false + end + end) percol_modality = [findfirst((ucs)->(ucs==cs), unique_frames) for cs in percol_frames] diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index 509a724..b48a791 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -130,6 +130,9 @@ struct SupportedLogiset{ @assert !xor(isnothing(conditions), isnothing(relations)) "Please, provide " * "both conditions and relations in order to use a one-step memoset." + conditions = unique(conditions) + relations = unique(relations) + if use_onestep_memoization != false @assert !isnothing(conditions) && !isnothing(relations) "Please, provide " * "both conditions and relations in order to use a one-step memoset." @@ -208,8 +211,9 @@ function featvalue( i_instance::Integer, w::W, f::AbstractFeature, + args... ) where {W<:AbstractWorld} - featvalue(base(X), i_instance, w, f) + featvalue(base(X), i_instance, w, f, args...) end frame(X::SupportedLogiset, i_instance::Integer) = frame(base(X), i_instance) diff --git a/src/models/antecedents.jl b/src/models/antecedents.jl new file mode 100644 index 0000000..a3d6bf3 --- /dev/null +++ b/src/models/antecedents.jl @@ -0,0 +1,157 @@ +import SoleLogics: check, syntaxstring +using SoleData: slicedataset + +""" + abstract type AbstractAntecedent <: AbstractFormula end + +A (boolean) antecedent, that is, +a condition based on a formula of a given logic, that is +to be checked on a logical interpretation, +evaluating to a boolean truth value (`true`/`false`). + +See also +[`TrueAntecedent`](@ref), +[`TruthAntecedent`](@ref), +[`check`](@ref), +[`formula`](@ref), +[`syntaxstring`](@ref). +""" +abstract type AbstractAntecedent <: AbstractFormula end + +function SoleLogics.tree(f::AbstractAntecedent) + return error("Cannot convert object of type $(typeof(f)) to a SyntaxTree.") +end + +function syntaxstring(a::AbstractAntecedent; kwargs...) + return error("Please, provide method syntaxstring(::$(typeof(a)); kwargs...).") +end + +function Base.show(io::IO, a::AbstractAntecedent) + print(io, "$(typeof(a))($(syntaxstring(a)))") +end + +# Check on a boolean antecedent +function check(a::AbstractAntecedent, i::AbstractInterpretation, args...; kwargs...) + return error("Please, provide method check(::$(typeof(a)), " * + "i::$(typeof(i)), args...; kwargs...).") +end +function check( + a::AbstractAntecedent, + d::AbstractInterpretationSet, + args...; + kwargs... +) + map( + i_instance->check(a, slicedataset(d, [i_instance]; return_view = true), args...; kwargs...)[1], + 1:ninstances(d) + ) +end + +""" + formula(a::AbstractAntecedent)::AbstractFormula + +Return the logical formula (see [`SoleLogics`](@ref) package) of a given +logical antecedent. + +See also +[`syntaxstring`](@ref), +[`AbstractAntecedent`](@ref). +""" +function formula(a::AbstractAntecedent)::AbstractFormula + return error("Please, provide method formula(::$(typeof(a))).") +end + +""" + struct TrueAntecedent <: AbstractAntecedent end + +A true condition is the boolean condition that always yields `true`. + +See also +[`TruthAntecedent`](@ref), +[`AbstractAntecedent`](@ref). +""" +struct TrueAntecedent <: AbstractAntecedent end + +tree(::TrueAntecedent) = SyntaxTree(⊤) +check(::TrueAntecedent, i::AbstractInterpretation, args...; kwargs...) = true +check(::TrueAntecedent, d::AbstractInterpretationSet, args...; kwargs...) = + fill(true, ninstances(d)) + +""" + struct TruthAntecedent{F<:AbstractFormula} <: AbstractAntecedent + formula::F + checkmode::C, + end + +An antecedent representing a condition that, on a given logical interpretation, +checking a logical formula evaluates to the `top` of the logic's algebra. + +The evaluation can be done with respect to a checkmode (e.g., evaluate the formula +on a specific world). + +See also +[`formula`](@ref), +[`CheckMode`](@ref), +[`AbstractAntecedent`](@ref). +""" +struct TruthAntecedent{ + F<:AbstractFormula, + C<:CheckMode, +} <: AbstractAntecedent + + formula::F + + checkmode::C + + function TruthAntecedent{F,C}( + formula::F, + checkmode::C, + ) where {F<:AbstractFormula,C<:CheckMode} + new{F,C}(formula) + end + + function TruthAntecedent{F}( + formula::F, + checkmode::C = GlobalCheck(), + ) where {F<:AbstractFormula,C<:CheckMode} + TruthAntecedent{F,C}(formula) + end + + function TruthAntecedent( + formula::F, + checkmode::C = GlobalCheck(), + ) where {F<:AbstractFormula,C<:CheckMode} + TruthAntecedent{F}(formula) + end +end + +function syntaxstring(a::TruthAntecedent; kwargs...) + "@$(syntaxstring(a.checkmode))($(syntaxstring(a.formula)))" +end +function syntaxstring(a::TruthAntecedent{F,C}; kwargs...) where {F,C<:GlobalCheck} + syntaxstring(a.formula) +end + + +formula(a::TruthAntecedent) = a.formula +tree(a::TruthAntecedent) = tree(a.formula) +checkmode(a::TruthAntecedent) = a.checkmode + +function check(a::TruthAntecedent, i::AbstractInterpretation, args...; kwargs...) + istop(check(checkmode(φ), formula(a), i, args...; kwargs...)) +end +function check( + a::TruthAntecedent, + d::AbstractInterpretationSet, + args...; + kwargs..., +) + map(istop, check(checkmode(φ), formula(a), d, args...; kwargs...)) +end + +############################################################################################ + +# Helpers +convert(::Type{AbstractAntecedent}, f::AbstractFormula) = TruthAntecedent(f) +convert(::Type{AbstractAntecedent}, tok::AbstractSyntaxToken) = TruthAntecedent(SyntaxTree(tok)) +convert(::Type{AbstractAntecedent}, ::typeof(⊤)) = TrueAntecedent() diff --git a/src/models/base.jl b/src/models/base.jl index d0aef9c..9cb4ebe 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -6,142 +6,6 @@ using SoleLogics: LeftmostLinearForm, LeftmostConjunctiveForm, LeftmostDisjuncti # Util typename(::Type{T}) where T = eval(nameof(T)) -""" - abstract type AbstractBooleanCondition end - -A boolean condition is a condition that evaluates to a boolean truth value (`true`/`false`), -when checked on a logical interpretation. - -See also -[`TrueCondition`](@ref), -[`LogicalTruthCondition`](@ref), -[`check`](@ref), -[`syntaxstring`](@ref). -""" -abstract type AbstractBooleanCondition end - -function syntaxstring(c::AbstractBooleanCondition; kwargs...) - return error("Please, provide method syntaxstring(::$(typeof(c)); kwargs...).") -end - -function Base.show(io::IO, c::AbstractBooleanCondition) - print(io, "$(typeof(c))($(syntaxstring(c)))") -end - -# Check on a boolean condition -function check(c::AbstractBooleanCondition, i::AbstractInterpretation, args...; kwargs...) - return error("Please, provide method check(::$(typeof(c)), " * - "i::$(typeof(i)), args...; kwargs...).") -end -function check( - c::AbstractBooleanCondition, - d::AbstractInterpretationSet, - args...; - kwargs... -) - map( - i_instance->check(c, slicedataset(d, [i_instance]; return_view = true), args...; kwargs...)[1], - 1:ninstances(d) - ) -end - -""" - abstract type AbstractLogicalBooleanCondition <: AbstractBooleanCondition end - -A boolean condition based on a formula of a given logic, that is -to be checked on a logical interpretation. - -See also -[`formula`](@ref), -[`syntaxstring`](@ref), -[`check`](@ref), -[`AbstractBooleanCondition`](@ref). -""" -abstract type AbstractLogicalBooleanCondition <: AbstractBooleanCondition end - -""" - formula(c::AbstractLogicalBooleanCondition)::AbstractFormula - -Return the logical formula (see [`SoleLogics`](@ref) package) of a given -logical boolean condition. - -See also -[`syntaxstring`](@ref), -[`AbstractLogicalBooleanCondition`](@ref). -""" -function formula(c::AbstractLogicalBooleanCondition)::AbstractFormula - return error("Please, provide method formula(::$(typeof(c))).") -end - -function syntaxstring(c::AbstractLogicalBooleanCondition; kwargs...) - syntaxstring(formula(c); kwargs...) -end - -""" - struct TrueCondition <: AbstractLogicalBooleanCondition end - -A true condition is the boolean condition that always yields `true`. - -See also -[`LogicalTruthCondition`](@ref), -[`AbstractLogicalBooleanCondition`](@ref). -""" -struct TrueCondition <: AbstractLogicalBooleanCondition end - -formula(::TrueCondition) = SyntaxTree(⊤) -check(::TrueCondition, i::AbstractInterpretation, args...; kwargs...) = true -check(::TrueCondition, d::AbstractInterpretationSet, args...; kwargs...) = - fill(true, ninstances(d)) - -""" - struct LogicalTruthCondition{F<:AbstractFormula} <: AbstractLogicalBooleanCondition - formula::F - end - -A boolean condition that, on a given logical interpretation, -a logical formula evaluates to the `top` of the logic's algebra. - -See also -[`formula`](@ref), -[`AbstractLogicalBooleanCondition`](@ref). -""" -struct LogicalTruthCondition{F<:AbstractFormula} <: AbstractLogicalBooleanCondition - formula::F - - function LogicalTruthCondition{F}( - formula::F - ) where {F<:AbstractFormula} - new{F}(formula) - end - - function LogicalTruthCondition( - formula::F - ) where {F<:AbstractFormula} - LogicalTruthCondition{F}(formula) - end -end - -formula(c::LogicalTruthCondition) = c.formula - -function check(c::LogicalTruthCondition, i::AbstractInterpretation, args...; kwargs...) - istop(check(formula(c), i, args...; kwargs...)) -end -function check( - c::LogicalTruthCondition, - d::AbstractInterpretationSet, - args...; - kwargs..., -) - map(istop, check(formula(c), d, args...; kwargs...)) -end - -############################################################################################ - -# Helpers -convert(::Type{AbstractBooleanCondition}, f::AbstractFormula) = LogicalTruthCondition(f) -convert(::Type{AbstractBooleanCondition}, tok::AbstractSyntaxToken) = LogicalTruthCondition(SyntaxTree(tok)) -convert(::Type{AbstractBooleanCondition}, ::typeof(⊤)) = TrueCondition() - ############################################################################################ """ @@ -225,7 +89,7 @@ end m::AbstractModel, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), + check_kwargs::NamedTuple = (;), functional_args::Tuple = (), functional_kwargs::NamedTuple = (;), kwargs... @@ -301,6 +165,8 @@ issymbolic(::AbstractModel) = false """ info(m::AbstractModel)::NamedTuple = m.info info(m::AbstractModel, key) = m.info[key] + info(m::AbstractModel, key, defaultval) + info!(m::AbstractModel, key, val) Return the `info` structure for model `m`; this structure is used for storing additional information that does not affect the model's behavior. @@ -309,6 +175,7 @@ about the model's statistical performance during the learning phase. """ info(m::AbstractModel)::NamedTuple = m.info info(m::AbstractModel, key) = m.info[key] +info(m::AbstractModel, key, defaultval) = Base.get(m.info, key, defaultval) info!(m::AbstractModel, key, value) = (m.info[key] = value; m) @@ -605,7 +472,7 @@ in order to obtain an outcome. """ struct Rule{ O, - C<:AbstractBooleanCondition, + C<:AbstractAntecedent, FM<:AbstractModel } <: ConstrainedModel{O,FM} antecedent::C @@ -625,13 +492,13 @@ Note that `FM` refers to the Feasible Models (`FM`) allowed in the model's sub-t See also [`antecedent`](@ref), [`consequent`](@ref), -[`AbstractBooleanCondition`](@ref), +[`AbstractAntecedent`](@ref), [`ConstrainedModel`](@ref), [`AbstractModel`](@ref). """ struct Rule{ O, - C<:AbstractBooleanCondition, + C<:AbstractAntecedent, FM<:AbstractModel } <: ConstrainedModel{O,FM} antecedent::C @@ -639,11 +506,11 @@ struct Rule{ info::NamedTuple function Rule{O}( - antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractBooleanCondition}, + antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractAntecedent}, consequent::Any, info::NamedTuple = (;), ) where {O} - antecedent = convert(AbstractBooleanCondition, antecedent) + antecedent = convert(AbstractAntecedent, antecedent) C = typeof(antecedent) consequent = wrap(consequent, AbstractModel{O}) FM = typeintersect(propagate_feasiblemodels(consequent), AbstractModel{<:O}) @@ -652,7 +519,7 @@ struct Rule{ end function Rule( - antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractBooleanCondition}, + antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractAntecedent}, consequent::Any, info::NamedTuple = (;), ) @@ -665,7 +532,7 @@ struct Rule{ consequent::Any, info::NamedTuple = (;), ) - antecedent = TrueCondition() + antecedent = TrueAntecedent() consequent = wrap(consequent) O = outcometype(consequent) Rule{O}(antecedent, consequent, info) @@ -673,7 +540,7 @@ struct Rule{ end """ - antecedent(m::Union{Rule,Branch})::AbstractBooleanCondition + antecedent(m::Union{Rule,Branch})::AbstractAntecedent Return the antecedent of a rule/branch; that is, the condition to be evaluated upon applying the model. @@ -698,8 +565,8 @@ See also """ consequent(m::Rule) = m.consequent -conditiontype(::Type{M}) where {M<:Rule{O,C}} where {O,C} = C -conditiontype(m::Rule) = conditiontype(typeof(m)) +antecedenttype(::Type{M}) where {M<:Rule{O,C}} where {O,C} = C +antecedenttype(m::Rule) = antecedenttype(typeof(m)) issymbolic(::Rule) = true @@ -750,7 +617,7 @@ function apply( d::AbstractInterpretationSet, i_instance::Integer; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [Dict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), + check_kwargs::NamedTuple = (;), kwargs... ) if check_antecedent(m, d, i_instance, check_args...; check_kwargs...) == true @@ -765,21 +632,21 @@ function apply( end # Helper -function formula(m::Rule{O,<:Union{LogicalTruthCondition,TrueCondition}}) where {O} +function formula(m::Rule{O,<:Union{TruthAntecedent,TrueAntecedent}}) where {O} formula(antecedent(m)) end # Helpers -function conjuncts(m::Rule{O,<:LogicalTruthCondition{<:LeftmostConjunctiveForm}}) where {O} +function conjuncts(m::Rule{O,<:TruthAntecedent{<:LeftmostConjunctiveForm}}) where {O} conjuncts(formula(m)) end -function nconjuncts(m::Rule{O,<:LogicalTruthCondition{<:LeftmostConjunctiveForm}}) where {O} +function nconjuncts(m::Rule{O,<:TruthAntecedent{<:LeftmostConjunctiveForm}}) where {O} nconjuncts(formula(m)) end -function disjuncts(m::Rule{O,<:LogicalTruthCondition{<:LeftmostDisjunctiveForm}}) where {O} +function disjuncts(m::Rule{O,<:TruthAntecedent{<:LeftmostDisjunctiveForm}}) where {O} disjuncts(formula(m)) end -function ndisjuncts(m::Rule{O,<:LogicalTruthCondition{<:LeftmostDisjunctiveForm}}) where {O} +function ndisjuncts(m::Rule{O,<:TruthAntecedent{<:LeftmostDisjunctiveForm}}) where {O} ndisjuncts(formula(m)) end @@ -787,12 +654,13 @@ end function Base.getindex( m::Rule{O,C}, idxs::AbstractVector{<:Integer}, -) where {O,SS<:LeftmostLinearForm,C<:LogicalTruthCondition{SS}} +) where {O,SS<:LeftmostLinearForm,C<:TruthAntecedent{SS}} + a = antecedent(m) Rule{O,C}( - LogicalTruthCondition{SS}(begin - ants = children(formula(m)) + TruthAntecedent{SS}(begin + ants = children(formula(a)) SS(ants[idxs]) - end), + end, checkmode(a)), consequent(m) ) end @@ -803,7 +671,7 @@ end """ struct Branch{ O, - C<:AbstractBooleanCondition, + C<:AbstractAntecedent, FM<:AbstractModel } <: ConstrainedModel{O,FM} antecedent::C @@ -817,7 +685,7 @@ the semantics: IF (antecedent) THEN (consequent_1) ELSE (consequent_2) END -where the antecedent is boolean condition to be tested and the consequents are the feasible +where the antecedent is a (boolean) condition to be tested and the consequents are the feasible local outcomes of the block. Note that `FM` refers to the Feasible Models (`FM`) allowed in the model's sub-tree. @@ -826,13 +694,13 @@ See also [`antecedent`](@ref), [`posconsequent`](@ref), [`negconsequent`](@ref), -[`AbstractBooleanCondition`](@ref), +[`AbstractAntecedent`](@ref), [`Rule`](@ref), [`ConstrainedModel`](@ref), [`AbstractModel`](@ref). """ struct Branch{ O, - C<:AbstractBooleanCondition, + C<:AbstractAntecedent, FM<:AbstractModel } <: ConstrainedModel{O,FM} antecedent::C @@ -841,12 +709,12 @@ struct Branch{ info::NamedTuple function Branch( - antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractBooleanCondition}, + antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractAntecedent}, posconsequent::Any, negconsequent::Any, info::NamedTuple = (;), ) - antecedent = convert(AbstractBooleanCondition, antecedent) + antecedent = convert(AbstractAntecedent, antecedent) C = typeof(antecedent) posconsequent = wrap(posconsequent) negconsequent = wrap(negconsequent) @@ -858,7 +726,7 @@ struct Branch{ end function Branch( - antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractBooleanCondition}, + antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractAntecedent}, (posconsequent, negconsequent)::Tuple{Any,Any}, info::NamedTuple = (;), ) @@ -893,8 +761,8 @@ See also """ negconsequent(m::Branch) = m.negconsequent -conditiontype(::Type{M}) where {M<:Branch{O,C}} where {O,C} = C -conditiontype(m::Branch) = conditiontype(typeof(m)) +antecedenttype(::Type{M}) where {M<:Branch{O,C}} where {O,C} = C +antecedenttype(m::Branch) = antecedenttype(typeof(m)) issymbolic(::Branch) = true @@ -931,11 +799,11 @@ function apply( end function apply( - m::Branch{O,<:LogicalTruthCondition}, + m::Branch{O,<:TruthAntecedent}, d::AbstractInterpretationSet, i_instance::Integer; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [Dict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), + check_kwargs::NamedTuple = (;), kwargs... ) where {O} if check_antecedent(m, d, i_instance, check_args...; check_kwargs...) == true @@ -954,10 +822,10 @@ function apply( end function apply( - m::Branch{O,<:LogicalTruthCondition}, + m::Branch{O,<:TruthAntecedent}, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), + check_kwargs::NamedTuple = (;), kwargs... ) where {O} cs = check_antecedent(m, d, check_args...; check_kwargs...) @@ -986,15 +854,24 @@ function apply( end # Helper -function formula(m::Branch{O,<:Union{LogicalTruthCondition,TrueCondition}}) where {O} +function formula(m::Branch{O,<:Union{TruthAntecedent,TrueAntecedent}}) where {O} formula(antecedent(m)) end +# Helper function Base.getindex( - m::Branch{O,<:LogicalTruthCondition{<:LeftmostLinearForm}}, - args... -) where {O} - return Base.getindex(formula(m), args...) + m::Branch{O,C}, + idxs::AbstractVector{<:Integer}, +) where {O,SS<:LeftmostLinearForm,C<:TruthAntecedent{SS}} + a = antecedent(m) + Branch{O,C}( + TruthAntecedent{SS}(begin + ants = children(formula(a)) + SS(ants[idxs]) + end, checkmode(a)), + posconsequent(m), + negconsequent(m) + ) end ############################################################################################ @@ -1002,7 +879,7 @@ end """ struct DecisionList{ O, - C<:AbstractBooleanCondition, + C<:AbstractAntecedent, FM<:AbstractModel } <: ConstrainedModel{O,FM} rulebase::Vector{Rule{_O,_C,_FM} where {_O<:O,_C<:C,_FM<:FM}} @@ -1035,7 +912,7 @@ See also """ struct DecisionList{ O, - C<:AbstractBooleanCondition, + C<:AbstractAntecedent, FM<:AbstractModel } <: ConstrainedModel{O,FM} rulebase::Vector{Rule{_O,_C,_FM} where {_O<:O,_C<:C,_FM<:FM}} @@ -1049,7 +926,7 @@ struct DecisionList{ ) defaultconsequent = wrap(defaultconsequent) O = Union{outcometype(defaultconsequent),outcometype.(rulebase)...} - C = Union{conditiontype.(rulebase)...} + C = Union{antecedenttype.(rulebase)...} FM = typeintersect(Union{propagate_feasiblemodels(defaultconsequent),propagate_feasiblemodels.(rulebase)...}, AbstractModel{<:O}) # FM = typeintersect(Union{propagate_feasiblemodels(defaultconsequent),propagate_feasiblemodels.(rulebase)...}, AbstractModel{O}) # FM = Union{propagate_feasiblemodels(defaultconsequent),propagate_feasiblemodels.(rulebase)...} @@ -1062,8 +939,8 @@ end rulebase(m::DecisionList) = m.rulebase defaultconsequent(m::DecisionList) = m.defaultconsequent -conditiontype(::Type{M}) where {M<:DecisionList{O,C}} where {O,C} = C -conditiontype(m::DecisionList) = conditiontype(typeof(m)) +antecedenttype(::Type{M}) where {M<:DecisionList{O,C}} where {O,C} = C +antecedenttype(m::DecisionList) = antecedenttype(typeof(m)) issymbolic(::DecisionList) = true @@ -1087,7 +964,7 @@ function apply( m::DecisionList{O}, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), + check_kwargs::NamedTuple = (;), ) where {O} nsamp = ninstances(d) pred = Vector{O}(undef, nsamp) @@ -1120,7 +997,7 @@ function apply!( m::DecisionList{O}, d::AbstractInterpretationSet; check_args::Tuple = (), - check_kwargs::NamedTuple = (; use_memo = [ThreadSafeDict{SyntaxTree,WorldSet{worldtype(d)}}() for i in 1:ninstances(d)]), + check_kwargs::NamedTuple = (;), compute_metrics::Union{Symbol,Bool} = false, ) where {O} nsamp = ninstances(d) @@ -1221,7 +1098,7 @@ sub-tree of `Branch` and `LeafModel`: struct DecisionTree{ O, - C<:AbstractBooleanCondition, + C<:AbstractAntecedent, FFM<:LeafModel } <: ConstrainedModel{O,Union{<:Branch{<:O,<:C},<:FFM}} root::M where {M<:Union{FFM,Branch}} @@ -1236,7 +1113,7 @@ See also [`ConstrainedModel`](@ref), [`MixedSymbolicModel`](@ref), [`DecisionLis """ struct DecisionTree{ O, - C<:AbstractBooleanCondition, + C<:AbstractAntecedent, FFM<:LeafModel } <: ConstrainedModel{O,Union{<:Branch{<:O,<:C}, <:FFM}} root::M where {M<:Union{FFM,Branch}} @@ -1245,8 +1122,8 @@ struct DecisionTree{ function DecisionTree( root::Union{FFM,Branch{O,C,Union{Branch{<:O,C2},FFM}}}, info::NamedTuple = (;), - ) where {O,C<:AbstractBooleanCondition,C2<:C,FFM<:LeafModel{<:O}} - new{O,root isa LeafModel ? AbstractBooleanCondition : C,FFM}(root, info) + ) where {O,C<:AbstractAntecedent,C2<:C,FFM<:LeafModel{<:O}} + new{O,root isa LeafModel ? AbstractAntecedent : C,FFM}(root, info) end function DecisionTree( @@ -1256,7 +1133,7 @@ struct DecisionTree{ root = wrap(root) M = typeof(root) O = outcometype(root) - C = (root isa LeafModel ? AbstractBooleanCondition : conditiontype(M)) + C = (root isa LeafModel ? AbstractAntecedent : antecedenttype(M)) # FM = typeintersect(Union{M,feasiblemodelstype(M)}, AbstractModel{<:O}) FM = typeintersect(Union{propagate_feasiblemodels(M)}, AbstractModel{<:O}) FFM = typeintersect(FM, LeafModel{<:O}) @@ -1272,9 +1149,9 @@ end root(m::DecisionTree) = m.root -conditiontype(::Type{M}) where {M<:DecisionTree{O,C}} where {O,C} = C -conditiontype(::Type{M}) where {M<:DecisionTree{O,C,FFM}} where {O,C,FFM} = C -conditiontype(m::DecisionTree) = conditiontype(typeof(m)) +antecedenttype(::Type{M}) where {M<:DecisionTree{O,C}} where {O,C} = C +antecedenttype(::Type{M}) where {M<:DecisionTree{O,C,FFM}} where {O,C,FFM} = C +antecedenttype(m::DecisionTree) = antecedenttype(typeof(m)) issymbolic(::DecisionTree) = true @@ -1317,7 +1194,7 @@ A `Decision Forest` is a symbolic model that wraps an ensemble of models struct DecisionForest{ O, - C<:AbstractBooleanCondition, + C<:AbstractAntecedent, FFM<:LeafModel } <: ConstrainedModel{O,Union{<:Branch{<:O,<:C},<:FFM}} trees::Vector{<:DecisionTree} @@ -1330,7 +1207,7 @@ See also [`ConstrainedModel`](@ref), [`MixedSymbolicModel`](@ref), [`DecisionLis """ struct DecisionForest{ O, - C<:AbstractBooleanCondition, + C<:AbstractAntecedent, FFM<:LeafModel } <: ConstrainedModel{O,Union{<:Branch{<:O,<:C},<:FFM}} trees::Vector{<:DecisionTree} @@ -1342,7 +1219,7 @@ struct DecisionForest{ ) @assert length(trees) > 0 "Cannot instantiate forest with no trees!" O = Union{outcometype.(trees)...} - C = Union{conditiontype.(trees)...} + C = Union{antecedenttype.(trees)...} FM = typeintersect(Union{propagate_feasiblemodels.(trees)...}, AbstractModel{<:O}) FFM = typeintersect(FM, LeafModel{<:O}) check_model_constraints.(DecisionForest{O}, typeof.(trees), FM, O) @@ -1352,8 +1229,8 @@ end trees(forest::DecisionForest) = forest.trees -conditiontype(::Type{M}) where {M<:DecisionForest{O,C}} where {O,C} = C -conditiontype(m::DecisionForest) = conditiontype(typeof(m)) +antecedenttype(::Type{M}) where {M<:DecisionForest{O,C}} where {O,C} = C +antecedenttype(m::DecisionForest) = antecedenttype(typeof(m)) issymbolic(::DecisionForest) = false diff --git a/src/models/check-modes.jl b/src/models/check-modes.jl new file mode 100644 index 0000000..af9abdb --- /dev/null +++ b/src/models/check-modes.jl @@ -0,0 +1,73 @@ +import SoleLogics: syntaxstring + +using SoleLogics: AbstractMultiModalFrame + +""" +Abstract type for model checking modes. + +See also +[`GlobalCheck`](@ref), +[`CenteredCheck`](@ref), +[`WorldCheck`](@ref). +""" +abstract type CheckMode end + +function check( + checkmode::CheckMode, + φ::SoleLogics.AbstractFormula, + X, + i_instance::Integer, + args...; + kwargs... +) + check(φ, X, i_instance, getworld(frame(X, i_instance), checkmode), args...; kwargs...) +end + +""" +A model checking mode where the formula to check is global and no specific +world is required. + +See also +[`CheckMode`](@ref). +""" +struct GlobalCheck <: CheckMode end; + +function getworld(fr::AbstractMultiModalFrame{W}, checkmode::GlobalCheck) where {W<:AbstractWorld} + nothing +end + +syntaxstring(::GlobalCheck) = "global" + +abstract type GroundedCheck <: CheckMode end + +""" +A model checking mode where the formula is checked on the central world; +(whenever the notion of "central" makes sense). + +See also +[`CheckMode`](@ref). +""" +struct CenteredCheck <: GroundedCheck end; + +syntaxstring(::CenteredCheck) = "center" + +function getworld(fr::AbstractMultiModalFrame{W}, checkmode::CenteredCheck) where {W<:AbstractWorld} + SoleLogics.centeredworld(fr) +end + +""" +A model checking mode where the formula is checked on a specific world `w`. + +See also +[`CheckMode`](@ref), +[`centeredworld`](@ref), +[`CenteredCheck`](@ref). +""" +struct WorldCheck{W<:AbstractWorld} <: GroundedCheck + w::W +end +syntaxstring(checkmode::WorldCheck) = "$(getworld(checkmode))" + +function getworld(::AbstractMultiModalFrame{W}, checkmode::WorldCheck{W}) where {W<:AbstractWorld} + checkmode.w +end diff --git a/src/models/evaluation.jl b/src/models/evaluation.jl index f6367e2..0de4c23 100644 --- a/src/models/evaluation.jl +++ b/src/models/evaluation.jl @@ -3,20 +3,45 @@ using MLJBase using SoleModels: LeafModel import SoleLogics: npropositions -function leafmetrics( - m::ConstantModel{L}; - digits = 2 -) where {L} - if haskey(info(m), :supporting_labels) - _gts = info(m)[:supporting_labels] - _preds = fill(outcome(m), length(_gts)) +""" + readmetrics(m::AbstractModel; kwargs...) + +Return a NamedTuple with some performance metrics for the given symbolic model. +Performance metrics can be computed when the `info` structure of the model: + - :supporting_labels + - :supporting_predictions + +""" +function readmetrics(m::LeafModel{L}; digits = 2) where {L<:Label} + merge(if haskey(info(m), :supporting_labels) && haskey(info(m), :supporting_predictions) + _gts = info(m).supporting_labels + _preds = _gts = info(m).supporting_predictions if L <: CLabel (; ninstances = length(_gts), confidence = round(MLJBase.accuracy(_gts, _preds); digits = digits)) elseif L <: RLabel (; ninstances = length(_gts), mae = round(MLJBase.mae(_gts, _preds); digits = digits)) else - error("Could not compute leafmetrics with unknown label type: $(L).") + error("Could not compute readmetrics with unknown label type: $(L).") end + elseif haskey(info(m), :supporting_labels) + return (; ninstances = length(info(m).supporting_labels)) + elseif haskey(info(consequent(m)), :supporting_labels) + return (; ninstances = length(info(m).supporting_labels)) + else + return (;) + end, (; coverage = 1.0)) +end + +function readmetrics(m::Rule; kwargs...) + if haskey(info(m), :supporting_labels) && haskey(info(consequent(m)), :supporting_labels) + _gts = info(m).supporting_labels + _gts_leaf = info(consequent(m)).supporting_labels + coverage = length(_gts_leaf)/length(_gts) + merge(readmetrics(consequent(m); kwargs...), (; coverage = coverage)) + elseif haskey(info(m), :supporting_labels) + return (; ninstances = length(info(m).supporting_labels)) + elseif haskey(info(consequent(m)), :supporting_labels) + return (; ninstances = length(info(m).supporting_labels)) else return (;) end @@ -81,19 +106,19 @@ function evaluaterule( end # """ -# npropositions(rule::Rule{O,<:TrueCondition}) where {O} -# npropositions(rule::Rule{O,<:LogicalTruthCondition}) where {O} +# npropositions(rule::Rule{O,<:TrueAntecedent}) where {O} +# npropositions(rule::Rule{O,<:TruthAntecedent}) where {O} # See also # [`Rule`](@ref), -# [`TrueCondition`](@ref), -# [`LogicalTruthCondition`](@ref), +# [`TrueAntecedent`](@ref), +# [`TruthAntecedent`](@ref), # [`antecedent`](@ref), # [`formula`](@ref), # [`n_propositions`](@ref). # """ -# npropositions(rule::Rule{O,<:TrueCondition}) where {O} = 1 -# npropositions(rule::Rule{O,<:LogicalTruthCondition}) where {O} = npropositions(antecedent(rule)) +# npropositions(rule::Rule{O,<:TrueAntecedent}) where {O} = 1 +# npropositions(rule::Rule{O,<:TruthAntecedent}) where {O} = npropositions(antecedent(rule)) """ rulemetrics( diff --git a/src/models/multi-antecedents.jl b/src/models/multi-antecedents.jl new file mode 100644 index 0000000..ef58fcf --- /dev/null +++ b/src/models/multi-antecedents.jl @@ -0,0 +1,105 @@ + +using SoleLogics: AbstractFormula, AbstractSyntaxStructure, AbstractOperator +import SoleLogics: syntaxstring, joinformulas + +import SoleLogics: tree +import SoleLogics: normalize + +""" + struct MultiAntecedent{F<:AbstractFormula} <: AbstractSyntaxStructure + modants::Dict{Int,F} + end + +A symbolic antecedent that can be checked on a `MultiLogiset`, associating +antecedents to modalities. +""" +struct MultiAntecedent{F<:AbstractFormula} <: AbstractSyntaxStructure + modants::Dict{Int,F} +end + +modants(f::MultiAntecedent) = f.modants + +function MultiAntecedent(i_modality::Integer, modant::AbstractFormula) + MultiAntecedent(Dict{Int,F}(i_modality => modant)) +end + +function syntaxstring( + f::MultiAntecedent; + hidemodality = false, + variable_names_map::Union{Nothing,AbstractDict,AbstractVector,AbstractVector{<:Union{AbstractDict,AbstractVector}}} = nothing, + kwargs... +) + map_is_multimodal = begin + if !isnothing(variable_names_map) && all(e->!(e isa Union{AbstractDict,AbstractVector}), variable_names_map) + @warn "With multimodal formulas, variable_names_map should be a vector of vectors/maps of " * + "variable names. Got $(typeof(variable_names_map)) instead. This may fail, " * + "or lead to unexpected results." + false + else + !isnothing(variable_names_map) + end + end + join([begin + _variable_names_map = map_is_multimodal ? variable_names_map[i_modality] : variable_names_map + φ = syntaxstring(modants(f)[i_modality]; variable_names_map = _variable_names_map, kwargs...) + hidemodality ? "$φ" : "{$(i_modality)}($φ)" + end for i_modality in sort(collect(keys(modants(f))))], " $(CONJUNCTION) ") +end + +function joinformulas(op::typeof(∧), children::NTuple{N,MultiAntecedent{F}}) where {N,F} + new_formulas = Dict{Int,F}() + i_modalities = unique(vcat(collect.(keys.([modants(ch) for ch in children]))...)) + for i_modality in i_modalities + chs = filter(ch->haskey(modants(ch), i_modality), children) + fs = map(ch->modants(ch)[i_modality], chs) + new_formulas[i_modality] = (length(fs) == 1 ? first(fs) : joinformulas(op, fs)) + end + return MultiAntecedent(new_formulas) +end + +# function joinformulas(op::typeof(¬), children::NTuple{N,MultiAntecedent{F}}) where {N,F} +# if length(children) > 1 +# error("Cannot negate $(length(children)) MultiAntecedent's.") +# end +# f = first(children) +# ks = keys(modants(f)) +# if length(ks) != 1 +# error("Cannot negate a $(length(ks))-MultiAntecedent.") +# end +# i_modality = first(ks) +# MultiAntecedent(i_modality, ¬(modants(f)[i_modality])) +# end +function joinformulas(op::AbstractOperator, children::NTuple{N,MultiAntecedent{F}}) where {N,F} + if !all(c->length(modants(c)) == 1, children) + error("Cannot join $(length(children)) MultiAntecedent's by means of $(op). " * + "$(children)\n" * + "$(map(c->length(modants(c)), children)).") + end + ks = map(c->first(keys(modants(c))), children) + if !allequal(ks) + error("Cannot join $(length(children)) MultiAntecedent's by means of $(op)." * + "Found different modalities: $(unique(ks)).") + end + i_modality = first(ks) + MultiAntecedent(i_modality, joinformulas(op, map(c->modants(c)[i_modality], children))) +end + +function normalize(φ::MultiAntecedent{F}; kwargs...) where {F<:AbstractFormula} + MultiAntecedent(Dict{Int,F}([i_modality => SoleLogics.normalize(f; kwargs...) for (i_modality,f) in pairs(modants(φ))])) +end + +function check( + φ::MultiAntecedent, + X::MultiLogiset, + i_instance::Integer, + args...; + kwargs..., +) + # TODO in the fuzzy case: use collatetruth(fuzzy algebra, ∧, ...) + all([check(f, X, i_modality, i_instance, args...; kwargs...) + for (i_modality, f) in modants(φ)]) +end + +# # TODO join MultiAntecedent leads to a SyntaxTree with MultiAntecedent children +# function joinformulas(op::AbstractOperator, children::NTuple{N,MultiAntecedent{F}}) where {N,F} +# end diff --git a/src/models/print.jl b/src/models/print.jl index 0c77580..1c57406 100644 --- a/src/models/print.jl +++ b/src/models/print.jl @@ -56,7 +56,7 @@ prints or returns a string representation of model `m`. the `info` structure for `m`; - `show_subtree_info::Bool = false`: when set to `true`, the header is printed for models in the sub-tree of `m`; -- `show_metrics::Bool = true`: when set to `true`, performance metrics at each point of the +- `show_metrics::Bool = false`: when set to `true`, performance metrics at each point of the subtree are shown, whenever they are available in the `info` structure; - `max_depth::Union{Nothing,Int} = nothing`: when it is an `Int`, models in the sub-tree with a depth higher than `max_depth` are ellipsed with "..."; @@ -75,28 +75,6 @@ printmodel(m::AbstractModel; kwargs...) = printmodel(stdout, m; kwargs...) # DEFAULT_HEADER = :brief DEFAULT_HEADER = false -"""$(doc_printdisplay_model)""" -function displaymodel( - m::AbstractModel; - header = DEFAULT_HEADER, - indentation_str = "", - indentation = default_indentation, - depth = 0, - max_depth = nothing, - show_subtree_info = false, - show_metrics = false, - show_intermediate_finals = false, - tree_mode = false, - syntaxstring_kwargs = (;), -) - println("Please, provide method displaymodel(::$(typeof(m)); kwargs...). " * - "See help for displaymodel.") -end - -############################################################################################ -############################################################################################ -############################################################################################ - # Utility macro for recursively displaying submodels macro _display_submodel( submodel, @@ -112,7 +90,7 @@ macro _display_submodel( kwargs ) quote - displaymodel($(esc(submodel)); + _displaymodel($(esc(submodel)); indentation_str = $(esc(indentation_str)), indentation = $(esc(indentation)), depth = $(esc(depth))+1, @@ -128,14 +106,44 @@ macro _display_submodel( end end +"""$(doc_printdisplay_model)""" +function displaymodel( + m::AbstractModel; + header = DEFAULT_HEADER, + indentation_str = "", + indentation = default_indentation, + depth = 0, + max_depth = nothing, + show_subtree_info = false, + show_metrics = false, + show_intermediate_finals = false, + tree_mode = false, + syntaxstring_kwargs = (;), + kwargs... +) + @_display_submodel m indentation_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs +end + +function _displaymodel( + m::AbstractModel; + kwargs..., +) + println("Please, provide method _displaymodel(::$(typeof(m)); kwargs...). " * + "See help for displaymodel.") +end + +############################################################################################ +############################################################################################ +############################################################################################ + function get_metrics_string( - m::ConstantModel, + m::AbstractModel; digits = 2 ) - "$(leafmetrics(m; digits = digits))" + "$(readmetrics(m; digits = digits))" end -function displaymodel( +function _displaymodel( m::ConstantModel; header = DEFAULT_HEADER, indentation_str = "", @@ -162,7 +170,7 @@ function displaymodel( String(take!(io)) end -function displaymodel( +function _displaymodel( m::FunctionModel; header = DEFAULT_HEADER, indentation_str = "", @@ -188,7 +196,7 @@ function displaymodel( String(take!(io)) end -function displaymodel( +function _displaymodel( m::Rule; header = DEFAULT_HEADER, indentation_str = "", @@ -199,8 +207,8 @@ function displaymodel( show_metrics = false, show_intermediate_finals = false, tree_mode = (subtreeheight(m) != 1), - arrow = "🠮", # ⮞, 🡆, 🠮, 🠲, => syntaxstring_kwargs = (;), + arrow = "🠮", # ⮞, 🡆, 🠮, 🠲, => kwargs..., ) io = IOBuffer() @@ -226,8 +234,13 @@ function displaymodel( pipe = "$(indentation_list_children) " # println(io, "$(indentation_str*pipe)$(antecedent(m))") #println(io, "$(pipe)$(antecedent(m))") - print(io, "$(pipe)$(syntaxstring(antecedent(m); (haskey(info(m), :syntaxstring_kwargs) ? info(m)[:syntaxstring_kwargs] : (;))..., syntaxstring_kwargs..., kwargs...))") + if show_intermediate_finals != false && haskey(info(m), :this) + @warn "One intermediate final was hidden. TODO expand code!" + end + ant_str = syntaxstring(antecedent(m); (haskey(info(m), :syntaxstring_kwargs) ? info(m).syntaxstring_kwargs : (;))..., syntaxstring_kwargs..., kwargs...) if tree_mode + show_metrics != false && print(io, "$(pipe)$(get_metrics_string(m; (show_metrics isa NamedTuple ? show_metrics : [])...))") + print(io, "$(pipe)$(ant_str)") println(io, "") pad_str = indentation_str*repeat(indentation_hspace, length(pipe)-length(indentation_last_space)+2) print(io, "$(pad_str*indentation_last_first)$(TICK)") @@ -235,6 +248,8 @@ function displaymodel( subm_str = @_display_submodel consequent(m) ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, subm_str) else + print(io, "$(pipe)$(ant_str)") + show_metrics != false && print(io, " : $(get_metrics_string(m; (show_metrics isa NamedTuple ? show_metrics : [])...))") ind_str = "" subm_str = @_display_submodel consequent(m) ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, " $(arrow) ") @@ -247,7 +262,7 @@ function displaymodel( String(take!(io)) end -function displaymodel( +function _displaymodel( m::Branch; header = DEFAULT_HEADER, indentation_str = "", @@ -282,10 +297,10 @@ function displaymodel( ######################################################################################## if isnothing(max_depth) || depth < max_depth pipe = "$(indentation_list_children) " - line_str = "$(pipe)$(syntaxstring(antecedent(m); (haskey(info(m), :syntaxstring_kwargs) ? info(m)[:syntaxstring_kwargs] : (;))..., syntaxstring_kwargs..., kwargs...))" + line_str = "$(pipe)$(syntaxstring(antecedent(m); (haskey(info(m), :syntaxstring_kwargs) ? info(m).syntaxstring_kwargs : (;))..., syntaxstring_kwargs..., kwargs...))" if show_intermediate_finals != false && haskey(info(m), :this) ind_str = "" - subm_str = @_display_submodel info(m)[:this] ind_str indentation (depth-1) max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs + subm_str = @_display_submodel info(m).this ind_str indentation (depth-1) max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs line_str = rpad(line_str, show_intermediate_finals isa Integer ? show_intermediate_finals : default_intermediate_finals_rpad) * subm_str print(io, line_str) else @@ -310,7 +325,7 @@ function displaymodel( end -function displaymodel( +function _displaymodel( m::DecisionList; header = DEFAULT_HEADER, indentation_str = "", @@ -348,14 +363,14 @@ function displaymodel( for (i_rule, rule) in enumerate(rulebase(m)) # pipe = indentation_any_first pipe = indentation_any_first*"[$(i_rule)/$(length(rulebase(m)))]┐" - println(io, "$(indentation_str*pipe)$(syntaxstring(antecedent(rule); (haskey(info(rule), :syntaxstring_kwargs) ? info(rule)[:syntaxstring_kwargs] : (;))..., syntaxstring_kwargs..., kwargs...))") + println(io, "$(indentation_str*pipe)$(syntaxstring(antecedent(rule); (haskey(info(rule), :syntaxstring_kwargs) ? info(rule).syntaxstring_kwargs : (;))..., syntaxstring_kwargs..., kwargs...))") pad_str = indentation_str*indentation_any_space*repeat(indentation_hspace, length(pipe)-length(indentation_any_space)-1) print(io, "$(pad_str*indentation_last_first)") ind_str = pad_str*indentation_last_space subm_str = @_display_submodel consequent(rule) ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, subm_str) end - pipe = indentation_last_first*"$(indentiation_cross)" + pipe = indentation_last_first*"$(indentation_cross)" print(io, "$(indentation_str*pipe)") # print(io, "$(indentation_str*indentation_last_space*repeat(indentation_hspace, length(pipe)-length(indentation_last_space)-1)*indentation_last_space)") ind_str = indentation_str*indentation_last_space*repeat(indentation_hspace, length(pipe)-length(indentation_last_space)-1)*indentation_last_space @@ -369,7 +384,7 @@ function displaymodel( String(take!(io)) end -function displaymodel( +function _displaymodel( m::DecisionTree; header = DEFAULT_HEADER, indentation_str = "", @@ -407,7 +422,7 @@ function displaymodel( String(take!(io)) end -function displaymodel( +function _displaymodel( m::DecisionForest; header = DEFAULT_HEADER, indentation_str = "", @@ -447,7 +462,7 @@ function displaymodel( String(take!(io)) end -function displaymodel( +function _displaymodel( m::MixedSymbolicModel; header = DEFAULT_HEADER, indentation_str = "", diff --git a/src/models/symbolic-utils.jl b/src/models/symbolic-utils.jl index 2ba9c83..c80494c 100644 --- a/src/models/symbolic-utils.jl +++ b/src/models/symbolic-utils.jl @@ -148,7 +148,7 @@ advanceformula(f::AbstractFormula, assumed_formula::Union{Nothing,AbstractFormul isnothing(assumed_formula) ? f : ∧(assumed_formula, f) advanceformula(r::Rule, assumed_formula::Union{Nothing,AbstractFormula}) = - Rule(LogicalTruthCondition(advanceformula(formula(r), assumed_formula)), consequent(r), info(r)) + Rule(TruthAntecedent(advanceformula(formula(r), assumed_formula)), consequent(r), info(r)) ############################################################################################ ############################################################################################ @@ -170,7 +170,7 @@ listimmediaterules(m::AbstractModel{O} where {O})::Rule{<:O} = end end) -listimmediaterules(m::LeafModel) = [Rule(TrueCondition, m)] +listimmediaterules(m::LeafModel) = [Rule(TrueAntecedent, m)] listimmediaterules(m::Rule) = [m] @@ -187,7 +187,7 @@ function listimmediaterules(m::DecisionList{O,C,FM}) where {O,C,FM} push!(normalized_rules, rule) assumed_formula = advanceformula(SoleLogics.NEGATION(formula(rule)), assumed_formula) end - default_antecedent = isnothing(assumed_formula) ? TrueCondition : LogicalTruthCondition(assumed_formula) + default_antecedent = isnothing(assumed_formula) ? TrueAntecedent : TruthAntecedent(assumed_formula) push!(normalized_rules, Rule(default_antecedent, defaultconsequent(m))) normalized_rules end @@ -201,10 +201,15 @@ listimmediaterules(m::MixedSymbolicModel) = listimmediaterules(root(m)) ############################################################################################ """ - listrules(m::AbstractModel; force_syntaxtree::Bool = false, use_shortforms::Bool = true)::Vector{<:Rule} + listrules( + m::AbstractModel; + force_syntaxtree::Bool = false, + use_shortforms::Bool = true, + use_leftmostlinearform::Bool = false, + )::Vector{<:Rule} Return a list of rules capturing the knowledge enclosed in symbolic model. -The behavior of a symbolic model can be extracted and represented as a +The behavior of any symbolic model can be extracted and represented as a set of mutually exclusive (and jointly exaustive, if the model is closed) rules, which can be useful for many purposes. @@ -280,32 +285,32 @@ end listrules(m::LeafModel; kwargs...) = [m] function listrules( - m::Rule{O,<:TrueCondition}; + m::Rule{O,<:TrueAntecedent}; kwargs..., ) where {O} [m] end function listrules( - m::Rule{O,<:LogicalTruthCondition}; + m::Rule{O,<:TruthAntecedent}; force_syntaxtree::Bool = false ) where {O} ant = force_syntaxtree ? tree(formula(m)) : formula(m) - [(force_syntaxtree ? Rule{O}(LogicalTruthCondition(ant), consequent(m), info(m)) : m)] + [(force_syntaxtree ? Rule{O}(TruthAntecedent(ant), consequent(m), info(m)) : m)] end function listrules( - m::Branch{O,<:TrueCondition}; + m::Branch{O,<:TrueAntecedent}; kwargs..., ) where {O} pos_rules = begin submodels = listrules(posconsequent(m); kwargs...) - submodels isa Vector{<:LeafModel} ? [Rule{O,TrueCondition}(fm) for fm in submodels] : submodels + submodels isa Vector{<:LeafModel} ? [Rule{O,TrueAntecedent}(fm) for fm in submodels] : submodels end neg_rules = begin submodels = listrules(negconsequent(m); kwargs...) - submodels isa Vector{<:LeafModel} ? [Rule{O,TrueCondition}(fm) for fm in submodels] : submodels + submodels isa Vector{<:LeafModel} ? [Rule{O,TrueAntecedent}(fm) for fm in submodels] : submodels end return [ @@ -315,42 +320,81 @@ function listrules( end function listrules( - m::Branch{O,<:LogicalTruthCondition}; + m::Branch{O,<:TruthAntecedent}; use_shortforms::Bool = true, force_syntaxtree::Bool = false, use_leftmostlinearform::Bool = false, kwargs..., ) where {O} - using_shortform = use_shortforms && haskey(info(m), :shortform) - ant = (using_shortform ? info(m, :shortform) : m) - antformula = formula(ant) - - pos_antformula = force_syntaxtree ? tree(antformula) : antformula - neg_antformula = force_syntaxtree ? ¬tree(antformula) : ¬antformula _subrules = [ - [(pos_antformula, r) for r in listrules(posconsequent(m); use_shortforms = use_shortforms, use_leftmostlinearform = use_leftmostlinearform, force_syntaxtree = force_syntaxtree, kwargs...)]..., - [(neg_antformula, r) for r in listrules(negconsequent(m); use_shortforms = use_shortforms, use_leftmostlinearform = use_leftmostlinearform, force_syntaxtree = force_syntaxtree, kwargs...)]... + [(true, r) for r in listrules(posconsequent(m); use_shortforms = use_shortforms, use_leftmostlinearform = use_leftmostlinearform, force_syntaxtree = force_syntaxtree, kwargs...)]..., + [(false, r) for r in listrules(negconsequent(m); use_shortforms = use_shortforms, use_leftmostlinearform = use_leftmostlinearform, force_syntaxtree = force_syntaxtree, kwargs...)]... ] - rules = map(((antformula, subrule),)->begin + rules = map(((flag, subrule),)->begin # @show info(subrule) + known_infokeys = [:supporting_labels, :supporting_predictions, :shortform, :this, :multipathformula] + ks = setdiff(keys(info(m)), known_infokeys) + if length(ks) > 0 + @warn "Dropping info keys: $(join(repr.(ks), ", "))" + end + + i = (;) + if haskey(info(m), :supporting_labels) + i = merge((;), (; + supporting_labels = info(m).supporting_labels, + )) + end + if haskey(info(m), :supporting_predictions) + i = merge((;), (; + supporting_predictions = info(m).supporting_predictions, + )) + end + + antformula, using_shortform = begin + if (use_shortforms && haskey(info(subrule), :shortform)) + formula(info(subrule)[:shortform]), true + else + (flag ? formula(antecedent(m)) : ¬formula(antecedent(m))), false + end + end + antformula = force_syntaxtree ? tree(antformula) : antformula + # @show using_shortform + # @show antformula + # @show typeof(subrule) + if subrule isa LeafModel - Rule(LogicalTruthCondition(antformula), subrule, merge(info(subrule), (; shortform = LogicalTruthCondition(antformula)))) - elseif (use_shortforms && haskey(info(subrule), :shortform)) - Rule(info(subrule)[:shortform], consequent(subrule), info(subrule)) - else - f = begin - f = formula(subrule) - if use_leftmostlinearform - subantformulas = (f isa LeftmostLinearForm ? children(f) : [f]) - lf = LeftmostConjunctiveForm([antformula, subantformulas...]) - force_syntaxtree ? tree(lf) : lf + ant = TruthAntecedent(SoleLogics.normalize(antformula; allow_proposition_flipping = true)) + subi = (;) + # if use_shortforms + # subi = merge((;), (; + # shortform = ant + # )) + # end + Rule(ant, subrule, merge(info(subrule), subi, i)) + elseif subrule isa Rule + ant = begin + if using_shortform + TruthAntecedent(antformula) else - antformula ∧ f + # Combine antecedents + f = begin + f = formula(subrule) + if use_leftmostlinearform + subantformulas = (f isa LeftmostLinearForm ? children(f) : [f]) + lf = LeftmostConjunctiveForm([antformula, subantformulas...]) + force_syntaxtree ? tree(lf) : lf + else + antformula ∧ f + end + end + TruthAntecedent(SoleLogics.normalize(f; allow_proposition_flipping = true)) end end - Rule(LogicalTruthCondition(f), consequent(subrule)) + Rule(ant, consequent(subrule), merge(info(subrule), i)) + else + error("Unexpected rule type: $(typeof(subrule)).") end end, _subrules) @@ -368,3 +412,67 @@ listrules(m::MixedSymbolicModel; kwargs...) = listrules(root(m); kwargs...) ############################################################################################ ############################################################################################ ############################################################################################ + + +function joinrules( + rules::AbstractVector{ + <:Rule{<:Any,<:SoleModels.AbstractAntecedent,<:SoleModels.ConstantModel} + }, + silent = false +) + alloutcomes = unique(outcome.(consequent.(rules))) + # @show info.(rules) + # @show info.(consequent.(rules)) + return [begin + these_rules = filter(r->outcome(consequent(r)) == _outcome, rules) + leafinfo, ruleinfo = begin + if !silent + known_infokeys = [:supporting_labels, :supporting_predictions, :shortform, :this, :multipathformula] + for i in [info.(these_rules)..., info.(consequent.(these_rules))...] + ks = setdiff(keys(i), known_infokeys) + if length(ks) > 0 + @warn "Dropping info keys: $(join(repr.(ks), ", "))" + end + end + end + leafinfo = begin + leafinfo = (;) + if any([haskey(info(c), :supporting_labels) for c in consequent.(these_rules)]) + leafinfo = merge(leafinfo, (; + supporting_labels = vcat([info(c, :supporting_labels) for c in consequent.(these_rules) if haskey(info(c), :supporting_labels)]...) + )) + end + if any([haskey(info(c), :supporting_predictions) for c in consequent.(these_rules)]) + leafinfo = merge(leafinfo, (; + supporting_predictions = vcat([info(c, :supporting_predictions) for c in consequent.(these_rules) if haskey(info(c), :supporting_predictions)]...) + )) + end + leafinfo + end + ruleinfo = begin + ruleinfo = (;) + if any([haskey(info(r), :supporting_labels) for r in these_rules]) + ruleinfo = merge(ruleinfo, (; + supporting_labels = vcat([info(r, :supporting_labels) for r in these_rules if haskey(info(r), :supporting_labels)]...) + )) + end + if any([haskey(info(r), :supporting_predictions) for r in these_rules]) + ruleinfo = merge(ruleinfo, (; + supporting_predictions = vcat([info(r, :supporting_predictions) for r in these_rules if haskey(info(r), :supporting_predictions)]...) + )) + end + if any([haskey(info(r), :shortform) for r in these_rules]) + ruleinfo = merge(ruleinfo, (; + shortform = LeftmostDisjunctiveForm(vcat([formula(info(r, :shortform)) for r in these_rules if haskey(info(r), :shortform)]...)) + )) + end + ruleinfo + end + leafinfo, ruleinfo + end + formulas = formula.(antecedent.(these_rules)) + newant = LeftmostDisjunctiveForm(formulas) + newcons = ConstantModel(_outcome, leafinfo) + Rule(newant, newcons, ruleinfo) + end for _outcome in alloutcomes] +end diff --git a/test/logisets/multilogisets.jl b/test/logisets/multilogisets.jl index 38cd87e..b5ed2da 100644 --- a/test/logisets/multilogisets.jl +++ b/test/logisets/multilogisets.jl @@ -38,7 +38,7 @@ alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, generic_features), i_instance = 1 -multiformulas = [begin +@test_broken multiformulas = [begin _formulas_dict = Dict{Int,SoleLogics.AbstractFormula}() for (i_modality, relations) in enumerate(multirelations) f = randformula(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:32:end]...]) @@ -57,7 +57,7 @@ multiformulas = [begin _formulas_dict[i_modality] = f end end - MultiFormula(_formulas_dict) + MultiAntecedent(_formulas_dict) end for i in 1:200]; # syntaxstring.(multiformulas) .|> println; diff --git a/test/misc.jl b/test/misc.jl index 8c535dc..957e4b5 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -7,7 +7,7 @@ using SoleLogics using SoleModels using SoleModels: AbstractModel using SoleModels: ConstantModel, LeafModel -using SoleModels: LogicalTruthCondition, TrueCondition +using SoleModels: TruthAntecedent, TrueAntecedent using SoleModels: listrules, formula, displaymodel, submodels io = IOBuffer() @@ -71,60 +71,60 @@ st_100 = @test_nowarn SyntaxTree(prop_100) p = @test_nowarn SoleLogics.parsebaseformula("p") p_tree = @test_nowarn SoleLogics.parsetree("p") -# @test LogicalTruthCondition(p) == LogicalTruthCondition{Formula}(p) -# @test LogicalTruthCondition(p_tree) == LogicalTruthCondition{SyntaxTree}(p_tree) +# @test TruthAntecedent(p) == TruthAntecedent{Formula}(p) +# @test TruthAntecedent(p_tree) == TruthAntecedent{SyntaxTree}(p_tree) # phi = @test_nowarn SoleLogics.parsebaseformula("p∧q∨r") # phi_tree = @test_nowarn SoleLogics.parsetree("p∧q∨r") -# @test LogicalTruthCondition(phi) == LogicalTruthCondition{Formula}(phi) -# @test LogicalTruthCondition(phi_tree) == LogicalTruthCondition{SyntaxTree}(phi_tree) +# @test TruthAntecedent(phi) == TruthAntecedent{Formula}(phi) +# @test TruthAntecedent(phi_tree) == TruthAntecedent{SyntaxTree}(phi_tree) # phi2 = @test_nowarn SoleLogics.parsebaseformula("q∧s→r") # phi2_tree = @test_nowarn SoleLogics.parsetree("q∧s→r") -# @test LogicalTruthCondition(phi2) == LogicalTruthCondition{Formula}(phi2) -# @test LogicalTruthCondition(phi2_tree) == LogicalTruthCondition{SyntaxTree}(phi2_tree) +# @test TruthAntecedent(phi2) == TruthAntecedent{Formula}(phi2) +# @test TruthAntecedent(phi2_tree) == TruthAntecedent{SyntaxTree}(phi2_tree) -@test LogicalTruthCondition(p) != LogicalTruthCondition{Formula}(p) -@test LogicalTruthCondition(p_tree) != LogicalTruthCondition{SyntaxTree}(p_tree) +@test TruthAntecedent(p) != TruthAntecedent{Formula}(p) +@test TruthAntecedent(p_tree) != TruthAntecedent{SyntaxTree}(p_tree) -@test LogicalTruthCondition(p) isa LogicalTruthCondition{<:Formula} -@test LogicalTruthCondition(p_tree) isa LogicalTruthCondition{<:SyntaxTree} +@test TruthAntecedent(p) isa TruthAntecedent{<:Formula} +@test TruthAntecedent(p_tree) isa TruthAntecedent{<:SyntaxTree} phi = @test_nowarn SoleLogics.parsebaseformula("p∧q∨r") phi_tree = @test_nowarn SoleLogics.parsetree("p∧q∨r") -@test LogicalTruthCondition(phi) isa LogicalTruthCondition{<:Formula} -@test LogicalTruthCondition(phi_tree) isa LogicalTruthCondition{<:SyntaxTree} +@test TruthAntecedent(phi) isa TruthAntecedent{<:Formula} +@test TruthAntecedent(phi_tree) isa TruthAntecedent{<:SyntaxTree} phi2 = @test_nowarn SoleLogics.parsebaseformula("q∧s→r") phi2_tree = @test_nowarn SoleLogics.parsetree("q∧s→r") -@test LogicalTruthCondition(phi2) isa LogicalTruthCondition{<:Formula} -@test LogicalTruthCondition(phi2_tree) isa LogicalTruthCondition{<:SyntaxTree} +@test TruthAntecedent(phi2) isa TruthAntecedent{<:Formula} +@test TruthAntecedent(phi2_tree) isa TruthAntecedent{<:SyntaxTree} formula_p = @test_nowarn SoleLogics.parsebaseformula("p") formula_q = @test_nowarn SoleLogics.parsebaseformula("q") formula_r = @test_nowarn SoleLogics.parsebaseformula("r") formula_s = @test_nowarn SoleLogics.parsebaseformula("s") -############################### LogicalTruthCondition ###################################### -cond_r = @test_nowarn LogicalTruthCondition(st_r) -cond_s = @test_nowarn LogicalTruthCondition(st_s) -cond_t = @test_nowarn LogicalTruthCondition(st_t) -cond_q = @test_nowarn LogicalTruthCondition(st_q) +############################### TruthAntecedent ###################################### +cond_r = @test_nowarn TruthAntecedent(st_r) +cond_s = @test_nowarn TruthAntecedent(st_s) +cond_t = @test_nowarn TruthAntecedent(st_t) +cond_q = @test_nowarn TruthAntecedent(st_q) -cond_not_r = @test_nowarn LogicalTruthCondition(¬(formula(cond_r))) -cond_not_s = @test_nowarn LogicalTruthCondition(¬(formula(cond_s))) +cond_not_r = @test_nowarn TruthAntecedent(¬(formula(cond_r))) +cond_not_s = @test_nowarn TruthAntecedent(¬(formula(cond_s))) -cond_1 = @test_nowarn LogicalTruthCondition(st_1) -cond_100 = @test_nowarn LogicalTruthCondition(st_100) +cond_1 = @test_nowarn TruthAntecedent(st_1) +cond_100 = @test_nowarn TruthAntecedent(st_100) ##################################### Rule ################################################# -r1_string = @test_nowarn Rule(LogicalTruthCondition(∧(∧(prop_r,prop_s),prop_t)),outcome_string) -r2_string = @test_nowarn Rule(LogicalTruthCondition(¬(prop_r)),outcome_string) +r1_string = @test_nowarn Rule(TruthAntecedent(∧(∧(prop_r,prop_s),prop_t)),outcome_string) +r2_string = @test_nowarn Rule(TruthAntecedent(¬(prop_r)),outcome_string) r_true_string = @test_nowarn Rule(outcome_string) r_true_number = @test_nowarn Rule(cmodel_number) -r1_r2_string = @test_nowarn Rule(LogicalTruthCondition(∧(∧(prop_r,prop_s),prop_t)), r2_string) +r1_r2_string = @test_nowarn Rule(TruthAntecedent(∧(∧(prop_r,prop_s),prop_t)), r2_string) rmodel_number = @test_nowarn Rule(phi, cmodel_number) rmodel_integer = @test_nowarn Rule(phi, cmodel_integer) @@ -245,7 +245,7 @@ branch_r0 = @test_nowarn Branch(formula_r, (branch_s, "yes")) branch_r = @test_nowarn Branch(formula_r, (branch_r0, "yes")) branch_r = @test_nowarn Branch(formula_r, (branch_r, "yes")) -branch_true = @test_nowarn Branch(TrueCondition(), (branch_r, "yes")) +branch_true = @test_nowarn Branch(TrueAntecedent(), (branch_r, "yes")) @test typeof(branch_r0) == typeof(branch_r) From b1e7fc4b935e40308c2ac30feb8df51c20c401a0 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 9 Jul 2023 05:11:43 +0200 Subject: [PATCH 38/77] remove antecedents layer!! Antecedents ARE formulas. check_antecedent->antecedenttops. remove check-modes, antecedents/multi-antecedents --- README.md | 2 +- src/SoleModels.jl | 10 +- src/logisets/check.jl | 22 ++- src/logisets/features.jl | 15 +- src/logisets/main.jl | 2 + src/logisets/multilogiset.jl | 116 ++++++++++++ src/logisets/scalar/onestep-memoset.jl | 5 +- src/logisets/scalar/test-operators.jl | 2 +- src/logisets/supported-logiset.jl | 6 +- src/logisets/templated-formulas.jl | 4 +- src/models/antecedents.jl | 157 ---------------- src/models/base.jl | 241 +++++++++++-------------- src/models/check-modes.jl | 73 -------- src/models/evaluation.jl | 25 +-- src/models/multi-antecedents.jl | 105 ----------- src/models/print.jl | 6 +- src/models/symbolic-utils.jl | 79 ++++---- test/logisets/cube2logiset.jl | 39 ++-- test/logisets/logisets.jl | 90 --------- test/logisets/memosets.jl | 110 +++++++++++ test/logisets/multilogisets.jl | 13 +- test/misc.jl | 114 +++++------- test/parse.jl | 4 +- test/runtests.jl | 3 +- 24 files changed, 510 insertions(+), 733 deletions(-) delete mode 100644 src/models/antecedents.jl delete mode 100644 src/models/check-modes.jl delete mode 100644 src/models/multi-antecedents.jl create mode 100644 test/logisets/memosets.jl diff --git a/README.md b/README.md index 0f189b8..2902559 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ These definitions provide a unified base for implementing symbolic algorithms, s - Branches: structures with `IF antecedent THEN pos_consequent ELSE neg_consequent END` semantics. Remember: -- An antecedent is any *condition* that can be checked on a machine learning *instance*, yielding a `true/false` value; +- An antecedent is a logical formula that can be checked on a logical interpretation (that is, an *instance* of a symbolic learning dataset), yielding a truth value (e.g., `true/false`); - A consequent is... anything you want, really, even another model (spoiler: a Branch with Branch consequents is a Decision Tree 😉). More specifically, antecedents can be *logical formulas* and, in such case, the symbolic models diff --git a/src/SoleModels.jl b/src/SoleModels.jl index 5ad8b23..a6225da 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -83,18 +83,10 @@ using .DimensionalDatasets: globalrel ############################################################################################ ############################################################################################ -include("models/check-modes.jl") - -include("models/antecedents.jl") - -export MultiAntecedent - -include("models/multi-antecedents.jl") - export outcometype, outputtype export Rule, Branch -export check_antecedent +export antecedenttops export antecedent, consequent export posconsequent, negconsequent diff --git a/src/logisets/check.jl b/src/logisets/check.jl index 2aba448..241be10 100644 --- a/src/logisets/check.jl +++ b/src/logisets/check.jl @@ -13,6 +13,20 @@ function check( check(tree(φ), X, i_instance, args...; kwargs...) end +function check( + φ::SoleLogics.SyntaxTree{ + Union{ + DiamondRelationalOperator{typeof(SoleLogics.tocenterrel)}, + BoxRelationalOperator{typeof(SoleLogics.tocenterrel)}, + } + }, + X::AbstractLogiset, + i_instance::Integer; + kwargs... +) + check(first(children(φ)), X, i_instance, SoleLogics.centralworld(frame(X, i_instance)); kwargs...) +end + function check( φ::SoleLogics.SyntaxTree, X::AbstractLogiset{W,U}, @@ -25,10 +39,12 @@ function check( ) where {W<:AbstractWorld,U,FT<:SoleLogics.AbstractFormula} if isnothing(w) - w = nothing + if nworlds(frame(X, i_instance)) == 1 + w = first(allworlds(frame(X, i_instance))) + end end - @assert SoleLogics.isglobal(φ) || !isnothing(w) "Please, specify a world in order " * - "to check non-global formula: $(syntaxstring(φ))." + @assert SoleLogics.isgrounded(φ) || !isnothing(w) "Please, specify a world in order " * + "to check non-grounded formula: $(syntaxstring(φ))." setformula(memo_structure::AbstractDict{<:AbstractFormula}, φ::AbstractFormula, val) = memo_structure[SoleLogics.tree(φ)] = val readformula(memo_structure::AbstractDict{<:AbstractFormula}, φ::AbstractFormula) = memo_structure[SoleLogics.tree(φ)] diff --git a/src/logisets/features.jl b/src/logisets/features.jl index 13caa30..98ffe57 100644 --- a/src/logisets/features.jl +++ b/src/logisets/features.jl @@ -36,6 +36,8 @@ end ############################################################################################ +import SoleLogics: atomtype + """ struct Feature{A} <: AbstractFeature atom::A @@ -50,14 +52,21 @@ struct Feature{A} <: AbstractFeature atom::A end +atomtype(::Type{<:Feature{A}}) where {A} = A +atomtype(::Feature{A}) where {A} = A + syntaxstring(f::Feature; kwargs...) = string(f.atom) function parsefeature( - ::Type{Feature}, + ::Type{F}, expression::String; kwargs... -) - Feature(expression) +) where {F<:Feature} + if F == Feature + F(expression) + else + F(parse(atomtype(F), expression)) + end end ############################################################################################ diff --git a/src/logisets/main.jl b/src/logisets/main.jl index d8b9abb..91894f6 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -39,6 +39,8 @@ include("supported-logiset.jl") export MultiLogiset, eachmodality, modality, nmodalities +export MultiFormula, modforms + # Multiframe version of logisets, for representing multimodal datasets include("multilogiset.jl") diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index 66220fb..28fbb98 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -175,3 +175,119 @@ function check( ) check(φ, modality(X, i_modality), i_instance, args...; kwargs...) end + +############################################################################################ + +using SoleLogics: AbstractFormula, AbstractSyntaxStructure, AbstractOperator +import SoleLogics: syntaxstring, joinformulas + +import SoleLogics: tree +import SoleLogics: normalize + +""" + struct MultiFormula{F<:AbstractFormula} <: AbstractSyntaxStructure + modforms::Dict{Int,F} + end + +A symbolic antecedent that can be checked on a `MultiLogiset`, associating +antecedents to modalities. +""" +struct MultiFormula{F<:AbstractFormula} <: AbstractSyntaxStructure + modforms::Dict{Int,F} +end + +subformulatype(::Type{<:M}) where {F,M<:MultiFormula{F}} = F +subformulatype(::MultiFormula{F}) where {F} = F + +function SoleLogics.tree(f::MultiFormula) + return error("Cannot convert object of type $(typeof(f)) to a SyntaxTree.") +end + +modforms(f::MultiFormula) = f.modforms + +function MultiFormula(i_modality::Integer, modant::AbstractFormula) + F = eval(nameof(typeof(modant))) + MultiFormula(Dict{Int,F}(i_modality => modant)) +end + +function syntaxstring( + f::MultiFormula; + hidemodality = false, + variable_names_map::Union{Nothing,AbstractDict,AbstractVector,AbstractVector{<:Union{AbstractDict,AbstractVector}}} = nothing, + kwargs... +) + map_is_multimodal = begin + if !isnothing(variable_names_map) && all(e->!(e isa Union{AbstractDict,AbstractVector}), variable_names_map) + @warn "With multimodal formulas, variable_names_map should be a vector of vectors/maps of " * + "variable names. Got $(typeof(variable_names_map)) instead. This may fail, " * + "or lead to unexpected results." + false + else + !isnothing(variable_names_map) + end + end + join([begin + _variable_names_map = map_is_multimodal ? variable_names_map[i_modality] : variable_names_map + φ = syntaxstring(modforms(f)[i_modality]; variable_names_map = _variable_names_map, kwargs...) + hidemodality ? "$φ" : "{$(i_modality)}($φ)" + end for i_modality in sort(collect(keys(modforms(f))))], " $(CONJUNCTION) ") +end + +function joinformulas(op::typeof(∧), children::NTuple{N,MultiFormula}) where {N} + F = eval(nameof(SoleBase._typejoin(subformulatype.(children)...))) + new_formulas = Dict{Int,F}() + i_modalities = unique(vcat(collect.(keys.([modforms(ch) for ch in children]))...)) + for i_modality in i_modalities + chs = filter(ch->haskey(modforms(ch), i_modality), children) + fs = map(ch->modforms(ch)[i_modality], chs) + new_formulas[i_modality] = (length(fs) == 1 ? first(fs) : joinformulas(op, fs)) + end + return MultiFormula(new_formulas) +end + +# function joinformulas(op::typeof(¬), children::NTuple{N,MultiFormula{F}}) where {N,F} +# if length(children) > 1 +# error("Cannot negate $(length(children)) MultiFormula's.") +# end +# f = first(children) +# ks = keys(modforms(f)) +# if length(ks) != 1 +# error("Cannot negate a $(length(ks))-MultiFormula.") +# end +# i_modality = first(ks) +# MultiFormula(i_modality, ¬(modforms(f)[i_modality])) +# end +function joinformulas(op::AbstractOperator, children::NTuple{N,MultiFormula}) where {N} + if !all(c->length(modforms(c)) == 1, children) + error("Cannot join $(length(children)) MultiFormula's by means of $(op). " * + "$(children)\n" * + "$(map(c->length(modforms(c)), children)).") + end + ks = map(c->first(keys(modforms(c))), children) + if !allequal(ks) + error("Cannot join $(length(children)) MultiFormula's by means of $(op)." * + "Found different modalities: $(unique(ks)).") + end + i_modality = first(ks) + MultiFormula(i_modality, joinformulas(op, map(c->modforms(c)[i_modality], children))) +end + +function SoleLogics.normalize(φ::MultiFormula{F}; kwargs...) where {F<:AbstractFormula} + MultiFormula(Dict{Int,F}([i_modality => SoleLogics.normalize(f; kwargs...) for (i_modality,f) in pairs(modforms(φ))])) +end + +function check( + φ::MultiFormula, + X::MultiLogiset, + i_instance::Integer, + args...; + kwargs..., +) + # TODO in the fuzzy case: use collatetruth(fuzzy algebra, ∧, ...) + all([check(f, X, i_modality, i_instance, args...; kwargs...) + for (i_modality, f) in modforms(φ)]) +end + +# # TODO join MultiFormula leads to a SyntaxTree with MultiFormula children +# function joinformulas(op::AbstractOperator, children::NTuple{N,MultiFormula{F}}) where {N,F} +# end diff --git a/src/logisets/scalar/onestep-memoset.jl b/src/logisets/scalar/onestep-memoset.jl index 292a41f..3232383 100644 --- a/src/logisets/scalar/onestep-memoset.jl +++ b/src/logisets/scalar/onestep-memoset.jl @@ -255,8 +255,7 @@ struct ScalarOneStepMemoset{ relations = filter(l->l≠globalrel, relations) if worldtype == OneWorld || all(i_instance->nworlds(frame(X, i_instance)) == 1, 1:ninstances(X)) @warn "ScalarOneStepMemoset: " * - "Found globalrel in relations but single-world case is" * - "identified." + "Found globalrel in relations in a single-world case." false else true @@ -477,7 +476,7 @@ function concatdatasets(Xms::ScalarOneStepMemoset{U}...) where {U} "$(@show relations.(Xms))" ScalarOneStepMemoset{U}( concatdatasets(relmemoset.(Xms)...), - concatdatasets(globmemoset.(Xms)...), + (any(isnothing.(globmemoset.(Xms))) ? nothing : concatdatasets(globmemoset.(Xms)...)), metaconditions(first(Xms)), relations(first(Xms)), ) diff --git a/src/logisets/scalar/test-operators.jl b/src/logisets/scalar/test-operators.jl index a94c3a9..1184e67 100644 --- a/src/logisets/scalar/test-operators.jl +++ b/src/logisets/scalar/test-operators.jl @@ -20,7 +20,7 @@ the (binary) test operator function. operator::TestOperator, featval::T1, threshold::T2 -) where {T1, T2} +) where {T1,T2} operator(featval, threshold) end diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index b48a791..fb27db0 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -130,8 +130,8 @@ struct SupportedLogiset{ @assert !xor(isnothing(conditions), isnothing(relations)) "Please, provide " * "both conditions and relations in order to use a one-step memoset." - conditions = unique(conditions) - relations = unique(relations) + isnothing(conditions) || (conditions = unique(conditions)) + isnothing(relations) || (relations = unique(relations)) if use_onestep_memoization != false @assert !isnothing(conditions) && !isnothing(relations) "Please, provide " * @@ -157,7 +157,7 @@ struct SupportedLogiset{ push!(supports, full_memoset_type(base, conditions)) end - @assert length(supports) > 0 "Cannot instantiate SupportedLogiset with no supports " * + @assert length(supports) > 0 "Cannot instantiate SupportedLogiset with no supports. " * "Please, specify use_full_memoization = true and/or " * "use_onestep_memoization = true." diff --git a/src/logisets/templated-formulas.jl b/src/logisets/templated-formulas.jl index 972ddbc..7a4fcca 100644 --- a/src/logisets/templated-formulas.jl +++ b/src/logisets/templated-formulas.jl @@ -7,14 +7,14 @@ Abstract type simple formulas of given templates. abstract type AbstractTemplatedFormula <: SoleLogics.AbstractFormula end """ -Templated formula for ⊤. +Templated formula for ⊤, which always checks top. """ struct TopFormula <: AbstractTemplatedFormula end tree(::TopFormula) = SyntaxTree(⊤) negation(::TopFormula) = BotFormula() """ -Templated formula for ⊥. +Templated formula for ⊥, which always checks bottom. """ struct BotFormula <: AbstractTemplatedFormula end tree(::BotFormula) = SyntaxTree(⊥) diff --git a/src/models/antecedents.jl b/src/models/antecedents.jl deleted file mode 100644 index a3d6bf3..0000000 --- a/src/models/antecedents.jl +++ /dev/null @@ -1,157 +0,0 @@ -import SoleLogics: check, syntaxstring -using SoleData: slicedataset - -""" - abstract type AbstractAntecedent <: AbstractFormula end - -A (boolean) antecedent, that is, -a condition based on a formula of a given logic, that is -to be checked on a logical interpretation, -evaluating to a boolean truth value (`true`/`false`). - -See also -[`TrueAntecedent`](@ref), -[`TruthAntecedent`](@ref), -[`check`](@ref), -[`formula`](@ref), -[`syntaxstring`](@ref). -""" -abstract type AbstractAntecedent <: AbstractFormula end - -function SoleLogics.tree(f::AbstractAntecedent) - return error("Cannot convert object of type $(typeof(f)) to a SyntaxTree.") -end - -function syntaxstring(a::AbstractAntecedent; kwargs...) - return error("Please, provide method syntaxstring(::$(typeof(a)); kwargs...).") -end - -function Base.show(io::IO, a::AbstractAntecedent) - print(io, "$(typeof(a))($(syntaxstring(a)))") -end - -# Check on a boolean antecedent -function check(a::AbstractAntecedent, i::AbstractInterpretation, args...; kwargs...) - return error("Please, provide method check(::$(typeof(a)), " * - "i::$(typeof(i)), args...; kwargs...).") -end -function check( - a::AbstractAntecedent, - d::AbstractInterpretationSet, - args...; - kwargs... -) - map( - i_instance->check(a, slicedataset(d, [i_instance]; return_view = true), args...; kwargs...)[1], - 1:ninstances(d) - ) -end - -""" - formula(a::AbstractAntecedent)::AbstractFormula - -Return the logical formula (see [`SoleLogics`](@ref) package) of a given -logical antecedent. - -See also -[`syntaxstring`](@ref), -[`AbstractAntecedent`](@ref). -""" -function formula(a::AbstractAntecedent)::AbstractFormula - return error("Please, provide method formula(::$(typeof(a))).") -end - -""" - struct TrueAntecedent <: AbstractAntecedent end - -A true condition is the boolean condition that always yields `true`. - -See also -[`TruthAntecedent`](@ref), -[`AbstractAntecedent`](@ref). -""" -struct TrueAntecedent <: AbstractAntecedent end - -tree(::TrueAntecedent) = SyntaxTree(⊤) -check(::TrueAntecedent, i::AbstractInterpretation, args...; kwargs...) = true -check(::TrueAntecedent, d::AbstractInterpretationSet, args...; kwargs...) = - fill(true, ninstances(d)) - -""" - struct TruthAntecedent{F<:AbstractFormula} <: AbstractAntecedent - formula::F - checkmode::C, - end - -An antecedent representing a condition that, on a given logical interpretation, -checking a logical formula evaluates to the `top` of the logic's algebra. - -The evaluation can be done with respect to a checkmode (e.g., evaluate the formula -on a specific world). - -See also -[`formula`](@ref), -[`CheckMode`](@ref), -[`AbstractAntecedent`](@ref). -""" -struct TruthAntecedent{ - F<:AbstractFormula, - C<:CheckMode, -} <: AbstractAntecedent - - formula::F - - checkmode::C - - function TruthAntecedent{F,C}( - formula::F, - checkmode::C, - ) where {F<:AbstractFormula,C<:CheckMode} - new{F,C}(formula) - end - - function TruthAntecedent{F}( - formula::F, - checkmode::C = GlobalCheck(), - ) where {F<:AbstractFormula,C<:CheckMode} - TruthAntecedent{F,C}(formula) - end - - function TruthAntecedent( - formula::F, - checkmode::C = GlobalCheck(), - ) where {F<:AbstractFormula,C<:CheckMode} - TruthAntecedent{F}(formula) - end -end - -function syntaxstring(a::TruthAntecedent; kwargs...) - "@$(syntaxstring(a.checkmode))($(syntaxstring(a.formula)))" -end -function syntaxstring(a::TruthAntecedent{F,C}; kwargs...) where {F,C<:GlobalCheck} - syntaxstring(a.formula) -end - - -formula(a::TruthAntecedent) = a.formula -tree(a::TruthAntecedent) = tree(a.formula) -checkmode(a::TruthAntecedent) = a.checkmode - -function check(a::TruthAntecedent, i::AbstractInterpretation, args...; kwargs...) - istop(check(checkmode(φ), formula(a), i, args...; kwargs...)) -end -function check( - a::TruthAntecedent, - d::AbstractInterpretationSet, - args...; - kwargs..., -) - map(istop, check(checkmode(φ), formula(a), d, args...; kwargs...)) -end - -############################################################################################ - -# Helpers -convert(::Type{AbstractAntecedent}, f::AbstractFormula) = TruthAntecedent(f) -convert(::Type{AbstractAntecedent}, tok::AbstractSyntaxToken) = TruthAntecedent(SyntaxTree(tok)) -convert(::Type{AbstractAntecedent}, ::typeof(⊤)) = TrueAntecedent() diff --git a/src/models/base.jl b/src/models/base.jl index 9cb4ebe..10d9f2f 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -1,7 +1,10 @@ import Base: convert, length, getindex, isopen -import SoleLogics: check, syntaxstring + using SoleData: slicedataset + +import SoleLogics: check, syntaxstring using SoleLogics: LeftmostLinearForm, LeftmostConjunctiveForm, LeftmostDisjunctiveForm +using SoleModels: TopFormula # Util typename(::Type{T}) where T = eval(nameof(T)) @@ -424,7 +427,6 @@ its `outcometype` `O`. See also [`feasiblemodelstype`](@ref), [`ConstrainedModel`](@ref). """ - propagate_feasiblemodels(M::Type{<:AbstractModel}) = Union{typename(M){outcometype(M)}, feasiblemodelstype(M)} propagate_feasiblemodels(m::AbstractModel) = propagate_feasiblemodels(typeof(m)) @@ -472,10 +474,10 @@ in order to obtain an outcome. """ struct Rule{ O, - C<:AbstractAntecedent, + A<:AbstractFormula, FM<:AbstractModel } <: ConstrainedModel{O,FM} - antecedent::C + antecedent::A consequent::FM info::NamedTuple end @@ -485,41 +487,41 @@ the semantics: IF (antecedent) THEN (consequent) END -where the antecedent is a condition to be tested and the consequent is the local outcome of the block. +where the antecedent is a formula to be checked, +and the consequent is the local outcome of the block. Note that `FM` refers to the Feasible Models (`FM`) allowed in the model's sub-tree. See also [`antecedent`](@ref), [`consequent`](@ref), -[`AbstractAntecedent`](@ref), +[`AbstractFormula`](@ref), [`ConstrainedModel`](@ref), [`AbstractModel`](@ref). """ struct Rule{ O, - C<:AbstractAntecedent, + A<:AbstractFormula, FM<:AbstractModel } <: ConstrainedModel{O,FM} - antecedent::C + antecedent::A consequent::FM info::NamedTuple function Rule{O}( - antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractAntecedent}, + antecedent::Union{AbstractSyntaxToken,AbstractFormula}, consequent::Any, info::NamedTuple = (;), ) where {O} - antecedent = convert(AbstractAntecedent, antecedent) - C = typeof(antecedent) + A = typeof(antecedent) consequent = wrap(consequent, AbstractModel{O}) FM = typeintersect(propagate_feasiblemodels(consequent), AbstractModel{<:O}) check_model_constraints(Rule{O}, typeof(consequent), FM, O) - new{O,C,FM}(antecedent, consequent, info) + new{O,A,FM}(antecedent, consequent, info) end function Rule( - antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractAntecedent}, + antecedent::Union{AbstractSyntaxToken,AbstractFormula}, consequent::Any, info::NamedTuple = (;), ) @@ -532,7 +534,7 @@ struct Rule{ consequent::Any, info::NamedTuple = (;), ) - antecedent = TrueAntecedent() + antecedent = TopFormula() consequent = wrap(consequent) O = outcometype(consequent) Rule{O}(antecedent, consequent, info) @@ -540,15 +542,15 @@ struct Rule{ end """ - antecedent(m::Union{Rule,Branch})::AbstractAntecedent + antecedent(m::Union{Rule,Branch})::AbstractFormula -Return the antecedent of a rule/branch; -that is, the condition to be evaluated upon applying the model. +Return the antecedent of a rule/branch, +that is, the formula to be checked upon applying the model. See also [`apply`](@ref), [`consequent`](@ref), -[`check_antecedent`](@ref), +[`antecedenttops`](@ref), [`Rule`](@ref), [`Branch`](@ref). """ @@ -565,13 +567,13 @@ See also """ consequent(m::Rule) = m.consequent -antecedenttype(::Type{M}) where {M<:Rule{O,C}} where {O,C} = C +antecedenttype(::Type{M}) where {M<:Rule{O,A}} where {O,A} = A antecedenttype(m::Rule) = antecedenttype(typeof(m)) issymbolic(::Rule) = true """ - function check_antecedent( + function antecedenttops( m::Union{Rule,Branch}, args...; kwargs... @@ -579,20 +581,14 @@ issymbolic(::Rule) = true check(antecedent(m), id, args...; kwargs...) end -Simply checks the antecedent of a rule on an instance or dataset. +Simply check the antecedent of a rule on an instance or dataset. See also [`antecedent`](@ref), [`Rule`](@ref), [`Branch`](@ref). """ -function check_antecedent( - m::Rule, - args...; - kwargs... -) - check(antecedent(m), args...; kwargs...) -end +function antecedenttops end function apply( m::Rule, @@ -601,7 +597,7 @@ function apply( check_kwargs::NamedTuple = (;), kwargs... ) - if check_antecedent(m, i, check_args...; check_kwargs...) + if antecedenttops(m, i, check_args...; check_kwargs...) apply(consequent(m), i; check_args = check_args, check_kwargs = check_kwargs, @@ -620,7 +616,7 @@ function apply( check_kwargs::NamedTuple = (;), kwargs... ) - if check_antecedent(m, d, i_instance, check_args...; check_kwargs...) == true + if antecedenttops(m, d, i_instance, check_args...; check_kwargs...) apply(consequent(m), d, i_instance; check_args = check_args, check_kwargs = check_kwargs, @@ -631,38 +627,27 @@ function apply( end end -# Helper -function formula(m::Rule{O,<:Union{TruthAntecedent,TrueAntecedent}}) where {O} - formula(antecedent(m)) -end - # Helpers -function conjuncts(m::Rule{O,<:TruthAntecedent{<:LeftmostConjunctiveForm}}) where {O} - conjuncts(formula(m)) +function conjuncts(m::Rule{O,<:LeftmostConjunctiveForm}) where {O} + conjuncts(antecedent(m)) end -function nconjuncts(m::Rule{O,<:TruthAntecedent{<:LeftmostConjunctiveForm}}) where {O} - nconjuncts(formula(m)) +function nconjuncts(m::Rule{O,<:LeftmostConjunctiveForm}) where {O} + nconjuncts(antecedent(m)) end -function disjuncts(m::Rule{O,<:TruthAntecedent{<:LeftmostDisjunctiveForm}}) where {O} - disjuncts(formula(m)) +function disjuncts(m::Rule{O,<:LeftmostDisjunctiveForm}) where {O} + disjuncts(antecedent(m)) end -function ndisjuncts(m::Rule{O,<:TruthAntecedent{<:LeftmostDisjunctiveForm}}) where {O} - ndisjuncts(formula(m)) +function ndisjuncts(m::Rule{O,<:LeftmostDisjunctiveForm}) where {O} + ndisjuncts(antecedent(m)) end # Helper function Base.getindex( - m::Rule{O,C}, + m::Rule{O,A}, idxs::AbstractVector{<:Integer}, -) where {O,SS<:LeftmostLinearForm,C<:TruthAntecedent{SS}} +) where {O,A<:LeftmostLinearForm} a = antecedent(m) - Rule{O,C}( - TruthAntecedent{SS}(begin - ants = children(formula(a)) - SS(ants[idxs]) - end, checkmode(a)), - consequent(m) - ) + Rule{O}(A(children(a)[idxs]), consequent(m)) end @@ -671,10 +656,10 @@ end """ struct Branch{ O, - C<:AbstractAntecedent, + A<:AbstractFormula, FM<:AbstractModel } <: ConstrainedModel{O,FM} - antecedent::C + antecedent::A posconsequent::FM negconsequent::FM info::NamedTuple @@ -683,10 +668,12 @@ end A `Branch` is one of the fundamental building blocks of symbolic modeling, and has the semantics: - IF (antecedent) THEN (consequent_1) ELSE (consequent_2) END + IF (antecedent) THEN (positive consequent) ELSE (negative consequent) END + +where the antecedent is a formula to be checked and the consequents are the feasible +local outcomes of the block. If checking the antecedent evaluates to the top of the algebra, +then the positive consequent is applied; otherwise, the negative consequenti is applied. -where the antecedent is a (boolean) condition to be tested and the consequents are the feasible -local outcomes of the block. Note that `FM` refers to the Feasible Models (`FM`) allowed in the model's sub-tree. @@ -694,39 +681,39 @@ See also [`antecedent`](@ref), [`posconsequent`](@ref), [`negconsequent`](@ref), -[`AbstractAntecedent`](@ref), +[`istop`](@ref), +[`AbstractFormula`](@ref), [`Rule`](@ref), [`ConstrainedModel`](@ref), [`AbstractModel`](@ref). """ struct Branch{ O, - C<:AbstractAntecedent, + A<:AbstractFormula, FM<:AbstractModel } <: ConstrainedModel{O,FM} - antecedent::C + antecedent::A posconsequent::FM negconsequent::FM info::NamedTuple function Branch( - antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractAntecedent}, + antecedent::Union{AbstractSyntaxToken,AbstractFormula}, posconsequent::Any, negconsequent::Any, info::NamedTuple = (;), ) - antecedent = convert(AbstractAntecedent, antecedent) - C = typeof(antecedent) + A = typeof(antecedent) posconsequent = wrap(posconsequent) negconsequent = wrap(negconsequent) O = Union{outcometype(posconsequent),outcometype(negconsequent)} FM = typeintersect(Union{propagate_feasiblemodels(posconsequent),propagate_feasiblemodels(negconsequent)}, AbstractModel{<:O}) check_model_constraints(Branch{O}, typeof(posconsequent), FM, O) check_model_constraints(Branch{O}, typeof(negconsequent), FM, O) - new{O,C,FM}(antecedent, posconsequent, negconsequent, info) + new{O,A,FM}(antecedent, posconsequent, negconsequent, info) end function Branch( - antecedent::Union{AbstractSyntaxToken,AbstractFormula,AbstractAntecedent}, + antecedent::Union{AbstractSyntaxToken,AbstractFormula}, (posconsequent, negconsequent)::Tuple{Any,Any}, info::NamedTuple = (;), ) @@ -761,21 +748,13 @@ See also """ negconsequent(m::Branch) = m.negconsequent -antecedenttype(::Type{M}) where {M<:Branch{O,C}} where {O,C} = C +antecedenttype(::Type{M}) where {M<:Branch{O,A}} where {O,A} = A antecedenttype(m::Branch) = antecedenttype(typeof(m)) issymbolic(::Branch) = true isopen(m::Branch) = isopen(posconsequent(m)) || isopen(negconsequent(m)) -function check_antecedent( - m::Branch, - args...; - kwargs... -) - check(antecedent(m), args...; kwargs...) -end - function apply( m::Branch, i::AbstractInterpretation; @@ -783,7 +762,7 @@ function apply( check_kwargs::NamedTuple = (;), kwargs... ) - if check_antecedent(m, i, check_args...; check_kwargs...) + if antecedenttops(m, i, check_args...; check_kwargs...) apply(posconsequent(m), i; check_args = check_args, check_kwargs = check_kwargs, @@ -799,14 +778,14 @@ function apply( end function apply( - m::Branch{O,<:TruthAntecedent}, + m::Branch, d::AbstractInterpretationSet, i_instance::Integer; check_args::Tuple = (), check_kwargs::NamedTuple = (;), kwargs... -) where {O} - if check_antecedent(m, d, i_instance, check_args...; check_kwargs...) == true +) + if antecedenttops(m, d, i_instance, check_args...; check_kwargs...) apply(posconsequent(m), d, i_instance; check_args = check_args, check_kwargs = check_kwargs, @@ -822,13 +801,13 @@ function apply( end function apply( - m::Branch{O,<:TruthAntecedent}, + m::Branch, d::AbstractInterpretationSet; check_args::Tuple = (), check_kwargs::NamedTuple = (;), kwargs... -) where {O} - cs = check_antecedent(m, d, check_args...; check_kwargs...) +) + cs = antecedenttops(m, d, check_args...; check_kwargs...) cpos = findall((c)->c==true, cs) cneg = findall((c)->c==false, cs) out = Array{outputtype(m)}(undef,length(cs)) @@ -853,36 +832,36 @@ function apply( out end -# Helper -function formula(m::Branch{O,<:Union{TruthAntecedent,TrueAntecedent}}) where {O} - formula(antecedent(m)) -end - # Helper function Base.getindex( - m::Branch{O,C}, + m::Branch{O,A}, idxs::AbstractVector{<:Integer}, -) where {O,SS<:LeftmostLinearForm,C<:TruthAntecedent{SS}} +) where {O,A<:LeftmostLinearForm} a = antecedent(m) - Branch{O,C}( - TruthAntecedent{SS}(begin - ants = children(formula(a)) - SS(ants[idxs]) - end, checkmode(a)), - posconsequent(m), - negconsequent(m) - ) + Branch{O}(A(children(a)[idxs]), posconsequent(m), negconsequent(m)) end +############################################################################################ +############################################################################################ + +antecedenttops(m::Union{Rule,Branch}, i::AbstractInterpretation, args...; kwargs...) = istop(check(antecedent(m), i, args...; kwargs...)) +antecedenttops(m::Union{Rule,Branch}, d::AbstractInterpretationSet, i_instance::Integer, args...; kwargs...) = istop(check(antecedent(m), d, i_instance, args...; kwargs...)) +antecedenttops(m::Union{Rule,Branch}, d::AbstractInterpretationSet, args...; kwargs...) = istop.(check(antecedent(m), d, args...; kwargs...)) + +antecedenttops(::Union{Rule{O,<:TopFormula},Branch{O,<:TopFormula}}, i::AbstractInterpretation, args...; kwargs...) where {O} = true +antecedenttops(::Union{Rule{O,<:TopFormula},Branch{O,<:TopFormula}}, d::AbstractInterpretationSet, i_instance::Integer, args...; kwargs...) where {O} = true +antecedenttops(::Union{Rule{O,<:TopFormula},Branch{O,<:TopFormula}}, d::AbstractInterpretationSet, args...; kwargs...) where {O} = fill(true, ninstances(d)) + +############################################################################################ ############################################################################################ """ struct DecisionList{ O, - C<:AbstractAntecedent, + A<:AbstractFormula, FM<:AbstractModel } <: ConstrainedModel{O,FM} - rulebase::Vector{Rule{_O,_C,_FM} where {_O<:O,_C<:C,_FM<:FM}} + rulebase::Vector{Rule{_O,_C,_FM} where {_O<:O,_C<:A,_FM<:FM}} defaultconsequent::FM info::NamedTuple end @@ -896,7 +875,7 @@ has the semantics of an IF-ELSEIF-ELSE block: ELSEIF (antecedent_n) THEN (consequent_n) ELSE (consequent_default) END -where the antecedents are conditions to be tested and the consequents are the feasible +where the antecedents are formulas to be, and the consequents are the feasible local outcomes of the block. Using the classical semantics, the antecedents are evaluated in order, and a consequent is returned as soon as a valid antecedent is found, @@ -912,10 +891,10 @@ See also """ struct DecisionList{ O, - C<:AbstractAntecedent, + A<:AbstractFormula, FM<:AbstractModel } <: ConstrainedModel{O,FM} - rulebase::Vector{Rule{_O,_C,_FM} where {_O<:O,_C<:C,_FM<:FM}} + rulebase::Vector{Rule{_O,_C,_FM} where {_O<:O,_C<:A,_FM<:FM}} defaultconsequent::FM info::NamedTuple @@ -926,20 +905,20 @@ struct DecisionList{ ) defaultconsequent = wrap(defaultconsequent) O = Union{outcometype(defaultconsequent),outcometype.(rulebase)...} - C = Union{antecedenttype.(rulebase)...} + A = Union{antecedenttype.(rulebase)...} FM = typeintersect(Union{propagate_feasiblemodels(defaultconsequent),propagate_feasiblemodels.(rulebase)...}, AbstractModel{<:O}) # FM = typeintersect(Union{propagate_feasiblemodels(defaultconsequent),propagate_feasiblemodels.(rulebase)...}, AbstractModel{O}) # FM = Union{propagate_feasiblemodels(defaultconsequent),propagate_feasiblemodels.(rulebase)...} check_model_constraints.(DecisionList{O}, typeof.(rulebase), FM, O) check_model_constraints(DecisionList{O}, typeof(defaultconsequent), FM, O) - new{O,C,FM}(rulebase, defaultconsequent, info) + new{O,A,FM}(rulebase, defaultconsequent, info) end end rulebase(m::DecisionList) = m.rulebase defaultconsequent(m::DecisionList) = m.defaultconsequent -antecedenttype(::Type{M}) where {M<:DecisionList{O,C}} where {O,C} = C +antecedenttype(::Type{M}) where {M<:DecisionList{O,A}} where {O,A} = A antecedenttype(m::DecisionList) = antecedenttype(typeof(m)) issymbolic(::DecisionList) = true @@ -953,7 +932,7 @@ function apply( check_kwargs::NamedTuple = (;), ) for rule in rulebase(m) - if check(m, i, check_args...; check_kwargs...) + if antecedenttops(rule, i, check_args...; check_kwargs...) return consequent(rule) end end @@ -976,8 +955,7 @@ function apply( uncovered_d = slicedataset(d, uncovered_idxs; return_view = true) idxs_sat = findall( - # check_antecedent(rule, d, check_args...; check_kwargs...) .== true - check_antecedent(rule, uncovered_d, check_args...; check_kwargs...) .== true + antecedenttops(rule, uncovered_d, check_args...; check_kwargs...) ) idxs_sat = uncovered_idxs[idxs_sat] uncovered_idxs = setdiff(uncovered_idxs, idxs_sat) @@ -1012,8 +990,7 @@ function apply!( uncovered_d = slicedataset(d, uncovered_idxs; return_view = true) idxs_sat = findall( - # check_antecedent(rule, d, check_args...; check_kwargs...) .== true - check_antecedent(rule, uncovered_d, check_args...; check_kwargs...) .== true + antecedenttops(rule, uncovered_d, check_args...; check_kwargs...) ) idxs_sat = uncovered_idxs[idxs_sat] uncovered_idxs = setdiff(uncovered_idxs, idxs_sat) @@ -1090,7 +1067,7 @@ IF-THEN-ELSE blocks: END END -where the antecedents are conditions to be tested and the consequents are the feasible +where the antecedents are formulas to be, and the consequents are the feasible local outcomes of the block. In practice, a `DecisionTree` simply wraps a constrained @@ -1098,9 +1075,9 @@ sub-tree of `Branch` and `LeafModel`: struct DecisionTree{ O, - C<:AbstractAntecedent, + A<:AbstractFormula, FFM<:LeafModel - } <: ConstrainedModel{O,Union{<:Branch{<:O,<:C},<:FFM}} + } <: ConstrainedModel{O,Union{<:Branch{<:O,<:A},<:FFM}} root::M where {M<:Union{FFM,Branch}} info::NamedTuple end @@ -1113,17 +1090,17 @@ See also [`ConstrainedModel`](@ref), [`MixedSymbolicModel`](@ref), [`DecisionLis """ struct DecisionTree{ O, - C<:AbstractAntecedent, + A<:AbstractFormula, FFM<:LeafModel -} <: ConstrainedModel{O,Union{<:Branch{<:O,<:C}, <:FFM}} +} <: ConstrainedModel{O,Union{<:Branch{<:O,<:A},<:FFM}} root::M where {M<:Union{FFM,Branch}} info::NamedTuple function DecisionTree( - root::Union{FFM,Branch{O,C,Union{Branch{<:O,C2},FFM}}}, + root::Union{FFM,Branch{O,A,Union{Branch{<:O,A2},FFM}}}, info::NamedTuple = (;), - ) where {O,C<:AbstractAntecedent,C2<:C,FFM<:LeafModel{<:O}} - new{O,root isa LeafModel ? AbstractAntecedent : C,FFM}(root, info) + ) where {O,A<:AbstractFormula,A2<:A,FFM<:LeafModel{<:O}} + new{O,root isa LeafModel ? AbstractFormula : A,FFM}(root, info) end function DecisionTree( @@ -1133,24 +1110,24 @@ struct DecisionTree{ root = wrap(root) M = typeof(root) O = outcometype(root) - C = (root isa LeafModel ? AbstractAntecedent : antecedenttype(M)) + A = (root isa LeafModel ? AbstractFormula : antecedenttype(M)) # FM = typeintersect(Union{M,feasiblemodelstype(M)}, AbstractModel{<:O}) FM = typeintersect(Union{propagate_feasiblemodels(M)}, AbstractModel{<:O}) FFM = typeintersect(FM, LeafModel{<:O}) - @assert M <: Union{<:FFM,<:Branch{<:O,<:C,<:Union{Branch,FFM}}} "" * - "Cannot instantiate DecisionTree{$(O),$(C),$(FFM)}(...) with root of " * + @assert M <: Union{<:FFM,<:Branch{<:O,<:A,<:Union{Branch,FFM}}} "" * + "Cannot instantiate DecisionTree{$(O),$(A),$(FFM)}(...) with root of " * "type $(typeof(root)). Note that the should be either a LeafModel or a " * "bounded Branch. " * - "$(M) <: $(Union{LeafModel,Branch{<:O,<:C,<:Union{Branch,FFM}}}) should hold." + "$(M) <: $(Union{LeafModel,Branch{<:O,<:A,<:Union{Branch,FFM}}}) should hold." check_model_constraints(DecisionTree{O}, typeof(root), FM, O) - new{O,C,FFM}(root, info) + new{O,A,FFM}(root, info) end end root(m::DecisionTree) = m.root -antecedenttype(::Type{M}) where {M<:DecisionTree{O,C}} where {O,C} = C -antecedenttype(::Type{M}) where {M<:DecisionTree{O,C,FFM}} where {O,C,FFM} = C +antecedenttype(::Type{M}) where {M<:DecisionTree{O,A}} where {O,A} = A +antecedenttype(::Type{M}) where {M<:DecisionTree{O,A,FFM}} where {O,A,FFM} = A antecedenttype(m::DecisionTree) = antecedenttype(typeof(m)) issymbolic(::DecisionTree) = true @@ -1194,9 +1171,9 @@ A `Decision Forest` is a symbolic model that wraps an ensemble of models struct DecisionForest{ O, - C<:AbstractAntecedent, + A<:AbstractFormula, FFM<:LeafModel - } <: ConstrainedModel{O,Union{<:Branch{<:O,<:C},<:FFM}} + } <: ConstrainedModel{O,Union{<:Branch{<:O,<:A},<:FFM}} trees::Vector{<:DecisionTree} info::NamedTuple end @@ -1207,9 +1184,9 @@ See also [`ConstrainedModel`](@ref), [`MixedSymbolicModel`](@ref), [`DecisionLis """ struct DecisionForest{ O, - C<:AbstractAntecedent, + A<:AbstractFormula, FFM<:LeafModel -} <: ConstrainedModel{O,Union{<:Branch{<:O,<:C},<:FFM}} +} <: ConstrainedModel{O,Union{<:Branch{<:O,<:A},<:FFM}} trees::Vector{<:DecisionTree} info::NamedTuple @@ -1219,17 +1196,17 @@ struct DecisionForest{ ) @assert length(trees) > 0 "Cannot instantiate forest with no trees!" O = Union{outcometype.(trees)...} - C = Union{antecedenttype.(trees)...} + A = Union{antecedenttype.(trees)...} FM = typeintersect(Union{propagate_feasiblemodels.(trees)...}, AbstractModel{<:O}) FFM = typeintersect(FM, LeafModel{<:O}) check_model_constraints.(DecisionForest{O}, typeof.(trees), FM, O) - new{O,C,FFM}(trees, info) + new{O,A,FFM}(trees, info) end end trees(forest::DecisionForest) = forest.trees -antecedenttype(::Type{M}) where {M<:DecisionForest{O,C}} where {O,C} = C +antecedenttype(::Type{M}) where {M<:DecisionForest{O,A}} where {O,A} = A antecedenttype(m::DecisionForest) = antecedenttype(typeof(m)) issymbolic(::DecisionForest) = false @@ -1282,7 +1259,7 @@ and IF-ELSEIF-ELSE blocks: END END -where the antecedents are conditinos and the consequents are the feasible +where the antecedents are formulas to be checked, and the consequents are the feasible local outcomes of the block. In Sole.jl, this logic can implemented using `ConstrainedModel`s such as diff --git a/src/models/check-modes.jl b/src/models/check-modes.jl deleted file mode 100644 index af9abdb..0000000 --- a/src/models/check-modes.jl +++ /dev/null @@ -1,73 +0,0 @@ -import SoleLogics: syntaxstring - -using SoleLogics: AbstractMultiModalFrame - -""" -Abstract type for model checking modes. - -See also -[`GlobalCheck`](@ref), -[`CenteredCheck`](@ref), -[`WorldCheck`](@ref). -""" -abstract type CheckMode end - -function check( - checkmode::CheckMode, - φ::SoleLogics.AbstractFormula, - X, - i_instance::Integer, - args...; - kwargs... -) - check(φ, X, i_instance, getworld(frame(X, i_instance), checkmode), args...; kwargs...) -end - -""" -A model checking mode where the formula to check is global and no specific -world is required. - -See also -[`CheckMode`](@ref). -""" -struct GlobalCheck <: CheckMode end; - -function getworld(fr::AbstractMultiModalFrame{W}, checkmode::GlobalCheck) where {W<:AbstractWorld} - nothing -end - -syntaxstring(::GlobalCheck) = "global" - -abstract type GroundedCheck <: CheckMode end - -""" -A model checking mode where the formula is checked on the central world; -(whenever the notion of "central" makes sense). - -See also -[`CheckMode`](@ref). -""" -struct CenteredCheck <: GroundedCheck end; - -syntaxstring(::CenteredCheck) = "center" - -function getworld(fr::AbstractMultiModalFrame{W}, checkmode::CenteredCheck) where {W<:AbstractWorld} - SoleLogics.centeredworld(fr) -end - -""" -A model checking mode where the formula is checked on a specific world `w`. - -See also -[`CheckMode`](@ref), -[`centeredworld`](@ref), -[`CenteredCheck`](@ref). -""" -struct WorldCheck{W<:AbstractWorld} <: GroundedCheck - w::W -end -syntaxstring(checkmode::WorldCheck) = "$(getworld(checkmode))" - -function getworld(::AbstractMultiModalFrame{W}, checkmode::WorldCheck{W}) where {W<:AbstractWorld} - checkmode.w -end diff --git a/src/models/evaluation.jl b/src/models/evaluation.jl index 0de4c23..b511b36 100644 --- a/src/models/evaluation.jl +++ b/src/models/evaluation.jl @@ -32,12 +32,12 @@ function readmetrics(m::LeafModel{L}; digits = 2) where {L<:Label} end, (; coverage = 1.0)) end -function readmetrics(m::Rule; kwargs...) +function readmetrics(m::Rule; digits = 2, kwargs...) if haskey(info(m), :supporting_labels) && haskey(info(consequent(m)), :supporting_labels) _gts = info(m).supporting_labels _gts_leaf = info(consequent(m)).supporting_labels coverage = length(_gts_leaf)/length(_gts) - merge(readmetrics(consequent(m); kwargs...), (; coverage = coverage)) + merge(readmetrics(consequent(m); digits = digits, kwargs...), (; coverage = round(coverage; digits = digits))) elseif haskey(info(m), :supporting_labels) return (; ninstances = length(info(m).supporting_labels)) elseif haskey(info(consequent(m)), :supporting_labels) @@ -65,13 +65,13 @@ See also [`Rule`](@ref), [`AbstractInterpretationSet`](@ref), [`Label`](@ref), -[`check_antecedent`](@ref). +[`antecedenttops`](@ref). """ function evaluaterule( - rule::Rule{O, C, FM}, + rule::Rule{O,A,FM}, X::AbstractInterpretationSet, Y::AbstractVector{<:Label} -) where {O,C,FM<:AbstractModel} +) where {O,A,FM<:AbstractModel} ys = apply(rule,X) antsat = ys .!= nothing @@ -106,19 +106,14 @@ function evaluaterule( end # """ -# npropositions(rule::Rule{O,<:TrueAntecedent}) where {O} -# npropositions(rule::Rule{O,<:TruthAntecedent}) where {O} +# npropositions(rule::Rule{O,<:AbstractFormula}) where {O} # See also # [`Rule`](@ref), -# [`TrueAntecedent`](@ref), -# [`TruthAntecedent`](@ref), +# [`AbstractFormula`](@ref), # [`antecedent`](@ref), -# [`formula`](@ref), -# [`n_propositions`](@ref). # """ -# npropositions(rule::Rule{O,<:TrueAntecedent}) where {O} = 1 -# npropositions(rule::Rule{O,<:TruthAntecedent}) where {O} = npropositions(antecedent(rule)) +# npropositions(rule::Rule{O,<:AbstractFormula}) where {O} = npropositions(antecedent(rule)) """ rulemetrics( @@ -146,10 +141,10 @@ See also [`consequent`](@ref). """ function rulemetrics( - rule::Rule{O, C, FM}, + rule::Rule{O,A,FM}, X::AbstractInterpretationSet, Y::AbstractVector{<:Label} -) where {O,C,FM<:AbstractModel} +) where {O,A,FM<:AbstractModel} eval_result = evaluaterule(rule, X, Y) ys = eval_result[:ys] antsat = eval_result[:antsat] diff --git a/src/models/multi-antecedents.jl b/src/models/multi-antecedents.jl deleted file mode 100644 index ef58fcf..0000000 --- a/src/models/multi-antecedents.jl +++ /dev/null @@ -1,105 +0,0 @@ - -using SoleLogics: AbstractFormula, AbstractSyntaxStructure, AbstractOperator -import SoleLogics: syntaxstring, joinformulas - -import SoleLogics: tree -import SoleLogics: normalize - -""" - struct MultiAntecedent{F<:AbstractFormula} <: AbstractSyntaxStructure - modants::Dict{Int,F} - end - -A symbolic antecedent that can be checked on a `MultiLogiset`, associating -antecedents to modalities. -""" -struct MultiAntecedent{F<:AbstractFormula} <: AbstractSyntaxStructure - modants::Dict{Int,F} -end - -modants(f::MultiAntecedent) = f.modants - -function MultiAntecedent(i_modality::Integer, modant::AbstractFormula) - MultiAntecedent(Dict{Int,F}(i_modality => modant)) -end - -function syntaxstring( - f::MultiAntecedent; - hidemodality = false, - variable_names_map::Union{Nothing,AbstractDict,AbstractVector,AbstractVector{<:Union{AbstractDict,AbstractVector}}} = nothing, - kwargs... -) - map_is_multimodal = begin - if !isnothing(variable_names_map) && all(e->!(e isa Union{AbstractDict,AbstractVector}), variable_names_map) - @warn "With multimodal formulas, variable_names_map should be a vector of vectors/maps of " * - "variable names. Got $(typeof(variable_names_map)) instead. This may fail, " * - "or lead to unexpected results." - false - else - !isnothing(variable_names_map) - end - end - join([begin - _variable_names_map = map_is_multimodal ? variable_names_map[i_modality] : variable_names_map - φ = syntaxstring(modants(f)[i_modality]; variable_names_map = _variable_names_map, kwargs...) - hidemodality ? "$φ" : "{$(i_modality)}($φ)" - end for i_modality in sort(collect(keys(modants(f))))], " $(CONJUNCTION) ") -end - -function joinformulas(op::typeof(∧), children::NTuple{N,MultiAntecedent{F}}) where {N,F} - new_formulas = Dict{Int,F}() - i_modalities = unique(vcat(collect.(keys.([modants(ch) for ch in children]))...)) - for i_modality in i_modalities - chs = filter(ch->haskey(modants(ch), i_modality), children) - fs = map(ch->modants(ch)[i_modality], chs) - new_formulas[i_modality] = (length(fs) == 1 ? first(fs) : joinformulas(op, fs)) - end - return MultiAntecedent(new_formulas) -end - -# function joinformulas(op::typeof(¬), children::NTuple{N,MultiAntecedent{F}}) where {N,F} -# if length(children) > 1 -# error("Cannot negate $(length(children)) MultiAntecedent's.") -# end -# f = first(children) -# ks = keys(modants(f)) -# if length(ks) != 1 -# error("Cannot negate a $(length(ks))-MultiAntecedent.") -# end -# i_modality = first(ks) -# MultiAntecedent(i_modality, ¬(modants(f)[i_modality])) -# end -function joinformulas(op::AbstractOperator, children::NTuple{N,MultiAntecedent{F}}) where {N,F} - if !all(c->length(modants(c)) == 1, children) - error("Cannot join $(length(children)) MultiAntecedent's by means of $(op). " * - "$(children)\n" * - "$(map(c->length(modants(c)), children)).") - end - ks = map(c->first(keys(modants(c))), children) - if !allequal(ks) - error("Cannot join $(length(children)) MultiAntecedent's by means of $(op)." * - "Found different modalities: $(unique(ks)).") - end - i_modality = first(ks) - MultiAntecedent(i_modality, joinformulas(op, map(c->modants(c)[i_modality], children))) -end - -function normalize(φ::MultiAntecedent{F}; kwargs...) where {F<:AbstractFormula} - MultiAntecedent(Dict{Int,F}([i_modality => SoleLogics.normalize(f; kwargs...) for (i_modality,f) in pairs(modants(φ))])) -end - -function check( - φ::MultiAntecedent, - X::MultiLogiset, - i_instance::Integer, - args...; - kwargs..., -) - # TODO in the fuzzy case: use collatetruth(fuzzy algebra, ∧, ...) - all([check(f, X, i_modality, i_instance, args...; kwargs...) - for (i_modality, f) in modants(φ)]) -end - -# # TODO join MultiAntecedent leads to a SyntaxTree with MultiAntecedent children -# function joinformulas(op::AbstractOperator, children::NTuple{N,MultiAntecedent{F}}) where {N,F} -# end diff --git a/src/models/print.jl b/src/models/print.jl index 1c57406..fe74699 100644 --- a/src/models/print.jl +++ b/src/models/print.jl @@ -249,9 +249,9 @@ function _displaymodel( print(io, subm_str) else print(io, "$(pipe)$(ant_str)") - show_metrics != false && print(io, " : $(get_metrics_string(m; (show_metrics isa NamedTuple ? show_metrics : [])...))") ind_str = "" - subm_str = @_display_submodel consequent(m) ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs + subm_str = @_display_submodel consequent(m) ind_str indentation depth max_depth show_subtree_info false show_intermediate_finals tree_mode syntaxstring_kwargs kwargs + show_metrics != false && (subm_str = rstrip(subm_str, '\n') * " : $(get_metrics_string(m; (show_metrics isa NamedTuple ? show_metrics : [])...))") print(io, " $(arrow) ") print(io, subm_str) end @@ -370,7 +370,7 @@ function _displaymodel( subm_str = @_display_submodel consequent(rule) ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, subm_str) end - pipe = indentation_last_first*"$(indentation_cross)" + pipe = indentation_last_first*"$(CROSS)" print(io, "$(indentation_str*pipe)") # print(io, "$(indentation_str*indentation_last_space*repeat(indentation_hspace, length(pipe)-length(indentation_last_space)-1)*indentation_last_space)") ind_str = indentation_str*indentation_last_space*repeat(indentation_hspace, length(pipe)-length(indentation_last_space)-1)*indentation_last_space diff --git a/src/models/symbolic-utils.jl b/src/models/symbolic-utils.jl index c80494c..9883aec 100644 --- a/src/models/symbolic-utils.jl +++ b/src/models/symbolic-utils.jl @@ -148,7 +148,7 @@ advanceformula(f::AbstractFormula, assumed_formula::Union{Nothing,AbstractFormul isnothing(assumed_formula) ? f : ∧(assumed_formula, f) advanceformula(r::Rule, assumed_formula::Union{Nothing,AbstractFormula}) = - Rule(TruthAntecedent(advanceformula(formula(r), assumed_formula)), consequent(r), info(r)) + Rule(advanceformula(antecedent(r), assumed_formula), consequent(r), info(r)) ############################################################################################ ############################################################################################ @@ -170,25 +170,25 @@ listimmediaterules(m::AbstractModel{O} where {O})::Rule{<:O} = end end) -listimmediaterules(m::LeafModel) = [Rule(TrueAntecedent, m)] +listimmediaterules(m::LeafModel) = [Rule(TopFormula, m)] listimmediaterules(m::Rule) = [m] -listimmediaterules(m::Branch{O,FM}) where {O,FM} = [ - Rule{O,FM}(antecedent(m), posconsequent(m)), - Rule{O,FM}(SoleLogics.NEGATION(antecedent(m)), negconsequent(m)), +listimmediaterules(m::Branch{O}) where {O} = [ + Rule{O}(antecedent(m), posconsequent(m)), + Rule{O}(SoleLogics.NEGATION(antecedent(m)), negconsequent(m)), ] -function listimmediaterules(m::DecisionList{O,C,FM}) where {O,C,FM} +function listimmediaterules(m::DecisionList{O}) where {O} assumed_formula = nothing normalized_rules = [] for rule in rulebase(m) rule = advanceformula(rule, assumed_formula) push!(normalized_rules, rule) - assumed_formula = advanceformula(SoleLogics.NEGATION(formula(rule)), assumed_formula) + assumed_formula = advanceformula(SoleLogics.NEGATION(antecedent(rule)), assumed_formula) end - default_antecedent = isnothing(assumed_formula) ? TrueAntecedent : TruthAntecedent(assumed_formula) - push!(normalized_rules, Rule(default_antecedent, defaultconsequent(m))) + default_antecedent = isnothing(assumed_formula) ? TopFormula : assumed_formula + push!(normalized_rules, Rule{O}(default_antecedent, defaultconsequent(m))) normalized_rules end @@ -203,9 +203,10 @@ listimmediaterules(m::MixedSymbolicModel) = listimmediaterules(root(m)) """ listrules( m::AbstractModel; - force_syntaxtree::Bool = false, use_shortforms::Bool = true, use_leftmostlinearform::Bool = false, + normalize::Bool = false, + force_syntaxtree::Bool = false, )::Vector{<:Rule} Return a list of rules capturing the knowledge enclosed in symbolic model. @@ -285,32 +286,32 @@ end listrules(m::LeafModel; kwargs...) = [m] function listrules( - m::Rule{O,<:TrueAntecedent}; + m::Rule{O,<:TopFormula}; kwargs..., ) where {O} [m] end function listrules( - m::Rule{O,<:TruthAntecedent}; + m::Rule{O}; force_syntaxtree::Bool = false ) where {O} - ant = force_syntaxtree ? tree(formula(m)) : formula(m) - [(force_syntaxtree ? Rule{O}(TruthAntecedent(ant), consequent(m), info(m)) : m)] + ant = force_syntaxtree ? tree(antecedent(m)) : antecedent(m) + [(force_syntaxtree ? Rule{O}(ant, consequent(m), info(m)) : m)] end function listrules( - m::Branch{O,<:TrueAntecedent}; + m::Branch{O,<:TopFormula}; kwargs..., ) where {O} pos_rules = begin submodels = listrules(posconsequent(m); kwargs...) - submodels isa Vector{<:LeafModel} ? [Rule{O,TrueAntecedent}(fm) for fm in submodels] : submodels + submodels isa Vector{<:LeafModel} ? [Rule{O,TopFormula}(fm) for fm in submodels] : submodels end neg_rules = begin submodels = listrules(negconsequent(m); kwargs...) - submodels isa Vector{<:LeafModel} ? [Rule{O,TrueAntecedent}(fm) for fm in submodels] : submodels + submodels isa Vector{<:LeafModel} ? [Rule{O,TopFormula}(fm) for fm in submodels] : submodels end return [ @@ -320,16 +321,17 @@ function listrules( end function listrules( - m::Branch{O,<:TruthAntecedent}; + m::Branch{O}; use_shortforms::Bool = true, - force_syntaxtree::Bool = false, use_leftmostlinearform::Bool = false, + normalize::Bool = false, + force_syntaxtree::Bool = false, kwargs..., ) where {O} _subrules = [ - [(true, r) for r in listrules(posconsequent(m); use_shortforms = use_shortforms, use_leftmostlinearform = use_leftmostlinearform, force_syntaxtree = force_syntaxtree, kwargs...)]..., - [(false, r) for r in listrules(negconsequent(m); use_shortforms = use_shortforms, use_leftmostlinearform = use_leftmostlinearform, force_syntaxtree = force_syntaxtree, kwargs...)]... + [(true, r) for r in listrules(posconsequent(m); use_shortforms = use_shortforms, use_leftmostlinearform = use_leftmostlinearform, normalize = normalize, force_syntaxtree = force_syntaxtree, kwargs...)]..., + [(false, r) for r in listrules(negconsequent(m); use_shortforms = use_shortforms, use_leftmostlinearform = use_leftmostlinearform, normalize = normalize, force_syntaxtree = force_syntaxtree, kwargs...)]... ] rules = map(((flag, subrule),)->begin @@ -354,9 +356,9 @@ function listrules( antformula, using_shortform = begin if (use_shortforms && haskey(info(subrule), :shortform)) - formula(info(subrule)[:shortform]), true + info(subrule)[:shortform], true else - (flag ? formula(antecedent(m)) : ¬formula(antecedent(m))), false + (flag ? antecedent(m) : antecedent(m)), false end end antformula = force_syntaxtree ? tree(antformula) : antformula @@ -365,7 +367,8 @@ function listrules( # @show typeof(subrule) if subrule isa LeafModel - ant = TruthAntecedent(SoleLogics.normalize(antformula; allow_proposition_flipping = true)) + ant = antformula + normalize && (ant = SoleLogics.normalize(ant; allow_proposition_flipping = true)) subi = (;) # if use_shortforms # subi = merge((;), (; @@ -376,22 +379,20 @@ function listrules( elseif subrule isa Rule ant = begin if using_shortform - TruthAntecedent(antformula) + antformula else # Combine antecedents - f = begin - f = formula(subrule) - if use_leftmostlinearform - subantformulas = (f isa LeftmostLinearForm ? children(f) : [f]) - lf = LeftmostConjunctiveForm([antformula, subantformulas...]) - force_syntaxtree ? tree(lf) : lf - else - antformula ∧ f - end + f = antecedent(subrule) + if use_leftmostlinearform + subantformulas = (f isa LeftmostLinearForm ? children(f) : [f]) + lf = LeftmostConjunctiveForm([antformula, subantformulas...]) + force_syntaxtree ? tree(lf) : lf + else + antformula ∧ f end - TruthAntecedent(SoleLogics.normalize(f; allow_proposition_flipping = true)) end end + normalize && (ant = SoleLogics.normalize(ant; allow_proposition_flipping = true)) Rule(ant, consequent(subrule), merge(info(subrule), i)) else error("Unexpected rule type: $(typeof(subrule)).") @@ -416,7 +417,7 @@ listrules(m::MixedSymbolicModel; kwargs...) = listrules(root(m); kwargs...) function joinrules( rules::AbstractVector{ - <:Rule{<:Any,<:SoleModels.AbstractAntecedent,<:SoleModels.ConstantModel} + <:Rule{<:Any,<:SoleModels.AbstractFormula,<:SoleModels.ConstantModel} }, silent = false ) @@ -463,15 +464,15 @@ function joinrules( end if any([haskey(info(r), :shortform) for r in these_rules]) ruleinfo = merge(ruleinfo, (; - shortform = LeftmostDisjunctiveForm(vcat([formula(info(r, :shortform)) for r in these_rules if haskey(info(r), :shortform)]...)) + shortform = LeftmostDisjunctiveForm(vcat([info(r, :shortform) for r in these_rules if haskey(info(r), :shortform)]...)) )) end ruleinfo end leafinfo, ruleinfo end - formulas = formula.(antecedent.(these_rules)) - newant = LeftmostDisjunctiveForm(formulas) + ants = antecedent.(these_rules) + newant = LeftmostDisjunctiveForm(ants) newcons = ConstantModel(_outcome, leafinfo) Rule(newant, newcons, ruleinfo) end for _outcome in alloutcomes] diff --git a/test/logisets/cube2logiset.jl b/test/logisets/cube2logiset.jl index edffe1e..69236fa 100644 --- a/test/logisets/cube2logiset.jl +++ b/test/logisets/cube2logiset.jl @@ -1,4 +1,5 @@ using Test +using Logging using StatsBase using Random using SoleLogics @@ -6,23 +7,25 @@ using SoleModels using SoleModels.DimensionalDatasets n_instances = 2 -_nvars = 2 +nvars = 2 -for (dataset, relations) in [ - # (Array(reshape(1.0:4.0, _nvars,n_instances)), []), - (Array(reshape(1.0:4.0, _nvars,n_instances)), [globalrel]), - (Array(reshape(1.0:12.0, 3,_nvars,n_instances)), [IARelations..., globalrel]), - (Array(reshape(1.0:36.0, 3,3,_nvars,n_instances)), [IA2DRelations..., globalrel]), +generic_features_oneworld = collect(Iterators.flatten([[SoleModels.UnivariateValue{Float64}(i_var)] for i_var in 1:nvars])) +generic_features = collect(Iterators.flatten([[UnivariateMax{Float64}(i_var), UnivariateMin{Float64}(i_var)] for i_var in 1:nvars])) + +for (dataset, relations, features) in [ + # (Array(reshape(1.0:4.0, nvars,n_instances)), []), + (Array(reshape(1.0:4.0, nvars,n_instances)), [globalrel], generic_features_oneworld), + (Array(reshape(1.0:12.0, 3,nvars,n_instances)), [IARelations..., globalrel], generic_features), + (Array(reshape(1.0:36.0, 3,3,nvars,n_instances)), [IA2DRelations..., globalrel], generic_features), ] -nvars = nvariables(dataset) +@test nvars == nvariables(dataset) -generic_features = collect(Iterators.flatten([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:nvars])) -logiset = @test_logs (:warn,) scalarlogiset(dataset, generic_features; use_full_memoization = false, use_onestep_memoization = false) +logiset = @test_nowarn scalarlogiset(dataset, features; use_full_memoization = false, use_onestep_memoization = false) logiset = @test_nowarn scalarlogiset(dataset; use_full_memoization = false, use_onestep_memoization = false) -metaconditions = [ScalarMetaCondition(feature, >) for feature in generic_features] +metaconditions = [ScalarMetaCondition(feature, >) for feature in features] @test_nowarn SupportedLogiset(logiset, ()) @test_throws AssertionError SupportedLogiset(logiset, [Dict()]) @test_throws AssertionError SupportedLogiset(logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}()]) @@ -31,24 +34,24 @@ metaconditions = [ScalarMetaCondition(feature, >) for feature in generic_feature @test_throws AssertionError SupportedLogiset(logiset; use_full_memoization = false) @test_throws AssertionError SupportedLogiset(logiset; use_onestep_memoization = true) -@test_logs (:warn,) SupportedLogiset(logiset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = [relations..., identityrel]) +@test_logs min_level=Logging.Error SupportedLogiset(logiset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = [relations..., identityrel]) -supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = relations) +supported_logiset = @test_logs min_level=Logging.Error SupportedLogiset(logiset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = relations) @test_throws AssertionError SupportedLogiset(logiset, (supported_logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:n_instances])) -supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = false, use_onestep_memoization = true, conditions = metaconditions, relations = relations) +supported_logiset = @test_logs min_level=Logging.Error SupportedLogiset(logiset; use_full_memoization = false, use_onestep_memoization = true, conditions = metaconditions, relations = relations) @test_nowarn SupportedLogiset(logiset, (supported_logiset, [Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:n_instances])) @test_nowarn SupportedLogiset(logiset, ([Dict{SyntaxTree,WorldSet{worldtype(logiset)}}() for i in 1:n_instances], supported_logiset)) -supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = true, conditions = metaconditions, relations = relations) +supported_logiset = @test_logs min_level=Logging.Error SupportedLogiset(logiset; use_full_memoization = true, conditions = metaconditions, relations = relations) -metaconditions = vcat([[ScalarMetaCondition(feature, >), ScalarMetaCondition(feature, <)] for feature in generic_features]...) -complete_supported_logiset = @test_nowarn SupportedLogiset(logiset; use_full_memoization = true, conditions = metaconditions, relations = relations) +metaconditions = vcat([[ScalarMetaCondition(feature, >), ScalarMetaCondition(feature, <)] for feature in features]...) +complete_supported_logiset = @test_logs min_level=Logging.Error SupportedLogiset(logiset; use_full_memoization = true, conditions = metaconditions, relations = relations) rng = Random.MersenneTwister(1) -alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, generic_features), rand(rng, [>, <]), rand(rng)) for i in 1:n_instances]); +alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:n_instances]); # syntaxstring.(alph) -_formulas = [randformula(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:16:end]...]) for i in 1:20]; +_formulas = [randformula(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in filter(r->r != globalrel, relations)]...)[1:16:end]...]) for i in 1:20]; # syntaxstring.(_formulas) .|> println; i_instance = 1 diff --git a/test/logisets/logisets.jl b/test/logisets/logisets.jl index e778807..ec1cf4f 100644 --- a/test/logisets/logisets.jl +++ b/test/logisets/logisets.jl @@ -174,93 +174,3 @@ memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstanc @time check(φ, scalar_logiset, 1, w; use_memo = nothing) @time check(φ, scalar_logiset, 1, w; use_memo = memoset) - -############################################################################################ -# Scalar memoset's -############################################################################################ - -using SoleModels: ScalarMetaCondition -using SoleModels: ScalarOneStepRelationalMemoset, ScalarOneStepGlobalMemoset - -# metaconditions = [ScalarMetaCondition(features[1], >)] -metaconditions = [ScalarMetaCondition(f, test_op) for f in features for test_op in [>,<]] - -@test_nowarn ScalarOneStepGlobalMemoset{Interval,Float64}(rand(1,22)) - -perform_initialization = true -bool_relationalmemoset = @test_nowarn ScalarOneStepRelationalMemoset(bool_logiset, metaconditions, [globalrel], perform_initialization) -bool_globalmemoset = @test_nowarn ScalarOneStepGlobalMemoset(bool_logiset, metaconditions, perform_initialization) - -@test_throws MethodError SupportedLogiset(bool_logiset, bool_relationalmemoset) - -using SoleModels: ScalarOneStepMemoset - -relations = [identityrel, globalrel] - -# bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(bool_relationalmemoset, bool_globalmemoset, metaconditions, relations) -bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset{Bool}(bool_relationalmemoset, bool_globalmemoset, metaconditions, relations) - -bool_onestepmemoset_empty = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations) -bool_onestepmemoset_full = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations; precompute_globmemoset = false, precompute_relmemoset = false) -bool_onestepmemoset_full = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations; precompute_globmemoset = true, precompute_relmemoset = true) - -@test_nowarn SupportedLogiset(bool_logiset, bool_onestepmemoset) -@test_nowarn SupportedLogiset(bool_logiset, (bool_onestepmemoset,)) -@test_nowarn SupportedLogiset(bool_logiset, (bool_onestepmemoset, bool_onestepmemoset)) -@test_nowarn SupportedLogiset(bool_logiset, [bool_onestepmemoset, bool_onestepmemoset]) - -@test_nowarn SupportedLogiset(bool_logiset, bool_onestepmemoset, memoset) -@test_nowarn SupportedLogiset(bool_logiset, (bool_onestepmemoset, memoset)) -@test_nowarn SupportedLogiset(bool_logiset, [bool_onestepmemoset, memoset]) - -# bool_logiset_2layer = SupportedLogiset(bool_logiset, bool_onestepmemoset) -memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstances(bool_logiset)] -bool_logiset_2layer = SupportedLogiset(bool_logiset) -# bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset, memoset]) -bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset_empty, memoset]) -# bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset_full, memoset]) - -rng = Random.MersenneTwister(1) -alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]) -syntaxstring.(alph) -_formulas = [randformula(rng, 10, alph, SoleLogics.BASE_MULTIMODAL_OPERATORS) for i in 1:20]; - -# Below are the times with a testset of 1000 formulas -############################################################################################ -# 223.635 ms (1459972 allocations: 59.18 MiB) -############################################################################################ -c1 = @test_nowarn [check(φ, bool_logiset, 1, w) for φ in _formulas] -############################################################################################ -c1 = @test_nowarn [check(φ, bool_logiset, 1, w; perform_normalization = false) for φ in _formulas] - -############################################################################################ -# 107.169 ms (545163 allocations: 14.71 MiB) -############################################################################################ -c2 = @test_nowarn [check(φ, bool_logiset_2layer, 1, w) for φ in _formulas] -############################################################################################ -c2 = @test_nowarn [check(φ, bool_logiset_2layer, 1, w; perform_normalization = false) for φ in _formulas] - -############################################################################################ -# 34.990 ms (301175 allocations: 14.93 MiB) -############################################################################################ -memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstances(bool_logiset)] -bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset_empty, memoset]) -c4 = @test_nowarn [check(φ, bool_logiset_3layer, 1, w; perform_normalization = false) for φ in _formulas] -############################################################################################ - -@test c1 == c2 == c4 - - -@test SoleModels.nmemoizedvalues(bool_logiset_3layer.supports[1].relmemoset) > 0 - -@test_nowarn slicedataset(bool_relationalmemoset, [1]) -@test_nowarn slicedataset(bool_globalmemoset, [1]) -@test_nowarn slicedataset(bool_onestepmemoset, [1]) -@test_nowarn slicedataset(bool_logiset_2layer, [1]) -@test_nowarn slicedataset(bool_logiset_3layer, [1]) - -@test_nowarn concatdatasets(bool_relationalmemoset, bool_relationalmemoset, bool_relationalmemoset) -@test_nowarn concatdatasets(bool_globalmemoset, bool_globalmemoset, bool_globalmemoset) -@test_nowarn concatdatasets(bool_onestepmemoset, bool_onestepmemoset, bool_onestepmemoset) -@test_nowarn concatdatasets(bool_logiset_2layer, bool_logiset_2layer, bool_logiset_2layer) -@test_nowarn concatdatasets(bool_logiset_3layer, bool_logiset_3layer, bool_logiset_3layer) diff --git a/test/logisets/memosets.jl b/test/logisets/memosets.jl new file mode 100644 index 0000000..0afa28f --- /dev/null +++ b/test/logisets/memosets.jl @@ -0,0 +1,110 @@ + +############################################################################################ +# Scalar memoset's +############################################################################################ + +using SoleModels: ScalarMetaCondition +using SoleModels: ScalarOneStepRelationalMemoset, ScalarOneStepGlobalMemoset + +using Test +using StatsBase +using SoleLogics +using SoleModels +using Graphs +using Random +using ThreadSafeDicts + +features = SoleModels.Feature.(string.('p':'z')) +worlds = SoleLogics.World.(1:10) +fr = SoleLogics.ExplicitCrispUniModalFrame(worlds, SimpleDiGraph(length(worlds), 4)) + +i_instance = 1 + +# Boolean +rng = Random.MersenneTwister(1) +bool_logiset = SoleModels.ExplicitBooleanLogiset([(Dict([w => sample(rng, features, 2, replace = false) for w in worlds]), fr)]) + +# metaconditions = [ScalarMetaCondition(features[1], >)] +metaconditions = [ScalarMetaCondition(f, test_op) for f in features for test_op in [>,<]] + +@test_nowarn ScalarOneStepGlobalMemoset{Interval,Float64}(rand(1,22)) + +perform_initialization = true +bool_relationalmemoset = @test_nowarn ScalarOneStepRelationalMemoset(bool_logiset, metaconditions, [globalrel], perform_initialization) +bool_globalmemoset = @test_nowarn ScalarOneStepGlobalMemoset(bool_logiset, metaconditions, perform_initialization) + +@test_throws MethodError SupportedLogiset(bool_logiset, bool_relationalmemoset) + +using SoleModels: ScalarOneStepMemoset + +relations = [identityrel, globalrel] + +# bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset(bool_relationalmemoset, bool_globalmemoset, metaconditions, relations) +bool_onestepmemoset = @test_logs (:warn,) ScalarOneStepMemoset{Bool}(bool_relationalmemoset, bool_globalmemoset, metaconditions, relations) + +bool_onestepmemoset_empty = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations) +bool_onestepmemoset_full = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations; precompute_globmemoset = false, precompute_relmemoset = false) + +# TODO test: +# bool_onestepmemoset_full = @test_logs (:warn,) ScalarOneStepMemoset(bool_logiset, metaconditions, relations; precompute_globmemoset = true, precompute_relmemoset = true) + +@test_nowarn SupportedLogiset(bool_logiset, bool_onestepmemoset) +@test_nowarn SupportedLogiset(bool_logiset, (bool_onestepmemoset,)) +@test_nowarn SupportedLogiset(bool_logiset, (bool_onestepmemoset, bool_onestepmemoset)) +@test_nowarn SupportedLogiset(bool_logiset, [bool_onestepmemoset, bool_onestepmemoset]) + +@test_nowarn SupportedLogiset(bool_logiset, bool_onestepmemoset, memoset) +@test_nowarn SupportedLogiset(bool_logiset, (bool_onestepmemoset, memoset)) +@test_nowarn SupportedLogiset(bool_logiset, [bool_onestepmemoset, memoset]) + +# bool_logiset_2layer = SupportedLogiset(bool_logiset, bool_onestepmemoset) +memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstances(bool_logiset)] +bool_logiset_2layer = SupportedLogiset(bool_logiset) +# bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset, memoset]) +bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset_empty, memoset]) +# bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset_full, memoset]) + +rng = Random.MersenneTwister(1) +alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, features), rand(rng, [>, <]), rand(rng)) for i in 1:10]) +syntaxstring.(alph) +_formulas = [randformula(rng, 10, alph, SoleLogics.BASE_MULTIMODAL_OPERATORS) for i in 1:20]; + +# Below are the times with a testset of 1000 formulas +############################################################################################ +# 223.635 ms (1459972 allocations: 59.18 MiB) +############################################################################################ +c1 = @test_nowarn [check(φ, bool_logiset, 1, w) for φ in _formulas] +############################################################################################ +c1 = @test_nowarn [check(φ, bool_logiset, 1, w; perform_normalization = false) for φ in _formulas] + +############################################################################################ +# 107.169 ms (545163 allocations: 14.71 MiB) +############################################################################################ +c2 = @test_nowarn [check(φ, bool_logiset_2layer, 1, w) for φ in _formulas] +############################################################################################ +c2 = @test_nowarn [check(φ, bool_logiset_2layer, 1, w; perform_normalization = false) for φ in _formulas] + +############################################################################################ +# 34.990 ms (301175 allocations: 14.93 MiB) +############################################################################################ +memoset = [ThreadSafeDict{SyntaxTree,WorldSet{W}}() for i_instance in 1:ninstances(bool_logiset)] +bool_logiset_3layer = SupportedLogiset(bool_logiset, [bool_onestepmemoset_empty, memoset]) +c4 = @test_nowarn [check(φ, bool_logiset_3layer, 1, w; perform_normalization = false) for φ in _formulas] +############################################################################################ + +@test c1 == c2 == c4 + + +@test SoleModels.nmemoizedvalues(bool_logiset_3layer.supports[1].relmemoset) > 0 + +@test_nowarn slicedataset(bool_relationalmemoset, [1]) +@test_nowarn slicedataset(bool_globalmemoset, [1]) +@test_nowarn slicedataset(bool_onestepmemoset, [1]) +@test_nowarn slicedataset(bool_logiset_2layer, [1]) +@test_nowarn slicedataset(bool_logiset_3layer, [1]) + +@test_nowarn concatdatasets(bool_relationalmemoset, bool_relationalmemoset, bool_relationalmemoset) +@test_nowarn concatdatasets(bool_globalmemoset, bool_globalmemoset, bool_globalmemoset) +@test_nowarn concatdatasets(bool_onestepmemoset, bool_onestepmemoset, bool_onestepmemoset) +@test_nowarn concatdatasets(bool_logiset_2layer, bool_logiset_2layer, bool_logiset_2layer) +@test_nowarn concatdatasets(bool_logiset_3layer, bool_logiset_3layer, bool_logiset_3layer) diff --git a/test/logisets/multilogisets.jl b/test/logisets/multilogisets.jl index b5ed2da..e761952 100644 --- a/test/logisets/multilogisets.jl +++ b/test/logisets/multilogisets.jl @@ -38,7 +38,7 @@ alph = ExplicitAlphabet([SoleModels.ScalarCondition(rand(rng, generic_features), i_instance = 1 -@test_broken multiformulas = [begin +multiformulas = [begin _formulas_dict = Dict{Int,SoleLogics.AbstractFormula}() for (i_modality, relations) in enumerate(multirelations) f = randformula(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:32:end]...]) @@ -47,17 +47,20 @@ i_instance = 1 coin = rand(2:3) f = begin if coin == 1 - AnchoredFormula(f, SoleModels.GlobalCheck()) + BoxRelationalOperator(globalrel)(f) elseif coin == 2 - AnchoredFormula(f, SoleModels.CenteredCheck()) + BoxRelationalOperator(SoleLogics.tocenterrel)(f) + # else + # TODO operator for going to a world. Note that this cannot be done with singletons... + # AnchoredFormula(f, SoleModels.WorldCheck(rand(collect(allworlds(modality(multilogiset, i_modality), i_instance))))) else - AnchoredFormula(f, SoleModels.WorldCheck(rand(collect(allworlds(modality(multilogiset, i_modality), i_instance))))) + f end end _formulas_dict[i_modality] = f end end - MultiAntecedent(_formulas_dict) + MultiFormula(_formulas_dict) end for i in 1:200]; # syntaxstring.(multiformulas) .|> println; diff --git a/test/misc.jl b/test/misc.jl index 957e4b5..8219c94 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -7,8 +7,8 @@ using SoleLogics using SoleModels using SoleModels: AbstractModel using SoleModels: ConstantModel, LeafModel -using SoleModels: TruthAntecedent, TrueAntecedent -using SoleModels: listrules, formula, displaymodel, submodels +using SoleModels: TopFormula +using SoleModels: listrules, displaymodel, submodels io = IOBuffer() @@ -71,60 +71,38 @@ st_100 = @test_nowarn SyntaxTree(prop_100) p = @test_nowarn SoleLogics.parsebaseformula("p") p_tree = @test_nowarn SoleLogics.parsetree("p") -# @test TruthAntecedent(p) == TruthAntecedent{Formula}(p) -# @test TruthAntecedent(p_tree) == TruthAntecedent{SyntaxTree}(p_tree) - # phi = @test_nowarn SoleLogics.parsebaseformula("p∧q∨r") # phi_tree = @test_nowarn SoleLogics.parsetree("p∧q∨r") -# @test TruthAntecedent(phi) == TruthAntecedent{Formula}(phi) -# @test TruthAntecedent(phi_tree) == TruthAntecedent{SyntaxTree}(phi_tree) # phi2 = @test_nowarn SoleLogics.parsebaseformula("q∧s→r") # phi2_tree = @test_nowarn SoleLogics.parsetree("q∧s→r") -# @test TruthAntecedent(phi2) == TruthAntecedent{Formula}(phi2) -# @test TruthAntecedent(phi2_tree) == TruthAntecedent{SyntaxTree}(phi2_tree) - -@test TruthAntecedent(p) != TruthAntecedent{Formula}(p) -@test TruthAntecedent(p_tree) != TruthAntecedent{SyntaxTree}(p_tree) -@test TruthAntecedent(p) isa TruthAntecedent{<:Formula} -@test TruthAntecedent(p_tree) isa TruthAntecedent{<:SyntaxTree} phi = @test_nowarn SoleLogics.parsebaseformula("p∧q∨r") phi_tree = @test_nowarn SoleLogics.parsetree("p∧q∨r") -@test TruthAntecedent(phi) isa TruthAntecedent{<:Formula} -@test TruthAntecedent(phi_tree) isa TruthAntecedent{<:SyntaxTree} phi2 = @test_nowarn SoleLogics.parsebaseformula("q∧s→r") phi2_tree = @test_nowarn SoleLogics.parsetree("q∧s→r") -@test TruthAntecedent(phi2) isa TruthAntecedent{<:Formula} -@test TruthAntecedent(phi2_tree) isa TruthAntecedent{<:SyntaxTree} formula_p = @test_nowarn SoleLogics.parsebaseformula("p") formula_q = @test_nowarn SoleLogics.parsebaseformula("q") formula_r = @test_nowarn SoleLogics.parsebaseformula("r") formula_s = @test_nowarn SoleLogics.parsebaseformula("s") -############################### TruthAntecedent ###################################### -cond_r = @test_nowarn TruthAntecedent(st_r) -cond_s = @test_nowarn TruthAntecedent(st_s) -cond_t = @test_nowarn TruthAntecedent(st_t) -cond_q = @test_nowarn TruthAntecedent(st_q) - -cond_not_r = @test_nowarn TruthAntecedent(¬(formula(cond_r))) -cond_not_s = @test_nowarn TruthAntecedent(¬(formula(cond_s))) - -cond_1 = @test_nowarn TruthAntecedent(st_1) -cond_100 = @test_nowarn TruthAntecedent(st_100) +############################### SyntaxTree ###################################### +st_not_r = @test_nowarn ¬st_r +st_not_s = @test_nowarn ¬st_s ##################################### Rule ################################################# -r1_string = @test_nowarn Rule(TruthAntecedent(∧(∧(prop_r,prop_s),prop_t)),outcome_string) -r2_string = @test_nowarn Rule(TruthAntecedent(¬(prop_r)),outcome_string) +r1_string = @test_nowarn Rule((∧(∧(prop_r,prop_s),prop_t)),outcome_string) +r2_string = @test_nowarn Rule((¬(prop_r)),outcome_string) r_true_string = @test_nowarn Rule(outcome_string) r_true_number = @test_nowarn Rule(cmodel_number) -r1_r2_string = @test_nowarn Rule(TruthAntecedent(∧(∧(prop_r,prop_s),prop_t)), r2_string) +r_true_string = @test_nowarn Rule(formula_p, outcome_string) + +r1_r2_string = @test_nowarn Rule((∧(∧(prop_r,prop_s),prop_t)), r2_string) rmodel_number = @test_nowarn Rule(phi, cmodel_number) rmodel_integer = @test_nowarn Rule(phi, cmodel_integer) @@ -207,10 +185,10 @@ dlmodel_integer = @test_nowarn DecisionList(rules_integer, defaultconsequent) @test outputtype(dlmodel_integer) == Union{outcometype(defaultconsequent),outcometype.(rules_integer)...} ################################### Branch ################################################# -b_nsx = @test_nowarn Branch(cond_q,outcome_string,outcome_string2) -b_fsx = @test_nowarn Branch(cond_s,outcome_string,outcome_string2) -b_fdx = @test_nowarn Branch(cond_t,b_nsx,outcome_string) -b_p = @test_nowarn Branch(cond_r,b_fsx,b_fdx) +b_nsx = @test_nowarn Branch(st_q,outcome_string,outcome_string2) +b_fsx = @test_nowarn Branch(st_s,outcome_string,outcome_string2) +b_fdx = @test_nowarn Branch(st_t,b_nsx,outcome_string) +b_p = @test_nowarn Branch(st_r,b_fsx,b_fdx) bmodel_integer = @test_nowarn Branch(phi, dlmodel_integer, dlmodel_integer) @test outputtype(bmodel_integer) == Int @@ -245,7 +223,7 @@ branch_r0 = @test_nowarn Branch(formula_r, (branch_s, "yes")) branch_r = @test_nowarn Branch(formula_r, (branch_r0, "yes")) branch_r = @test_nowarn Branch(formula_r, (branch_r, "yes")) -branch_true = @test_nowarn Branch(TrueAntecedent(), (branch_r, "yes")) +branch_true = @test_nowarn Branch(TopFormula(), (branch_r, "yes")) @test typeof(branch_r0) == typeof(branch_r) @@ -265,7 +243,7 @@ dtmodel = @test_nowarn DecisionTree(branch_r) df = @test_nowarn DecisionForest([dt1,dt2]) ############################### MixedSymbolicModel ######################################### -b_msm = @test_nowarn Branch(cond_q,outcome_int,outcome_float) +b_msm = @test_nowarn Branch(st_q,outcome_int,outcome_float) dt_msm = @test_nowarn DecisionTree(b_msm) msm = @test_nowarn MixedSymbolicModel(dt_msm) @@ -313,12 +291,12 @@ ms_model = MixedSymbolicModel(ms_model) @test immediatesubmodels(cmodel_string) isa Vector{Vector{<:AbstractModel{<:String}}} @test immediatesubmodels(r1_string) isa Vector{<:AbstractModel} -@test join(displaymodel.(immediatesubmodels(r1_string); header = false)) == """ +@test_broken join(displaymodel.(immediatesubmodels(r1_string); header = false)) == """ YES """ @test immediatesubmodels(r2_string) isa Vector{<:AbstractModel} -@test join(displaymodel.(immediatesubmodels(r2_string); header = false)) == """ +@test_broken join(displaymodel.(immediatesubmodels(r2_string); header = false)) == """ YES """ @@ -327,24 +305,24 @@ YES @test immediatesubmodels(b_fdx) isa Vector{<:AbstractModel} @test immediatesubmodels(b_p) isa Vector{<:AbstractModel} -@test join(displaymodel.(immediatesubmodels(b_nsx); header = false)) == """ +@test_broken join(displaymodel.(immediatesubmodels(b_nsx); header = false)) == """ YES NO """ -@test join(displaymodel.(immediatesubmodels(b_fsx); header = false)) == """ +@test_broken join(displaymodel.(immediatesubmodels(b_fsx); header = false)) == """ YES NO """ -@test join(displaymodel.(immediatesubmodels(b_fdx); header = false)) == """ +@test_broken join(displaymodel.(immediatesubmodels(b_fdx); header = false)) == """ ┐ q ├ ✔ YES └ ✘ NO YES """ -@test join(displaymodel.(immediatesubmodels(b_p); header = false)) == """ +@test_broken join(displaymodel.(immediatesubmodels(b_p); header = false)) == """ ┐ s ├ ✔ YES └ ✘ NO @@ -356,7 +334,7 @@ YES """ @test immediatesubmodels(d1_string) isa Vector{<:AbstractModel} -@test join(displaymodel.(immediatesubmodels(d1_string); header = false)) == """ +@test_broken join(displaymodel.(immediatesubmodels(d1_string); header = false)) == """ ┐(r ∧ s) ∧ t └ ✔ YES ┐¬(r) @@ -365,7 +343,7 @@ YES """ @test immediatesubmodels(dt1) isa Vector{<:AbstractModel} -@test join(displaymodel.(immediatesubmodels(dt1); header = false)) == """ +@test_broken join(displaymodel.(immediatesubmodels(dt1); header = false)) == """ ┐ s ├ ✔ YES └ ✘ NO @@ -377,7 +355,7 @@ YES """ @test immediatesubmodels(dt2) isa Vector{<:AbstractModel} -@test join(displaymodel.(immediatesubmodels(dt2); header = false)) == """ +@test_broken join(displaymodel.(immediatesubmodels(dt2); header = false)) == """ ┐ q ├ ✔ YES └ ✘ NO @@ -385,7 +363,7 @@ YES """ @test immediatesubmodels(msm) isa Vector{<:AbstractModel} -@test join(displaymodel.(immediatesubmodels(msm); header = false)) == """ +@test_broken join(displaymodel.(immediatesubmodels(msm); header = false)) == """ 2 1.5 """ @@ -407,12 +385,12 @@ YES @test submodels(cmodel_string) isa Vector{Any} @test submodels(r1_string) isa Vector{<:AbstractModel} -@test join(displaymodel.(submodels(r1_string); header = false)) == """ +@test_broken join(displaymodel.(submodels(r1_string); header = false)) == """ YES """ @test submodels(r2_string) isa Vector{<:AbstractModel} -@test join(displaymodel.(submodels(r2_string); header = false)) == """ +@test_broken join(displaymodel.(submodels(r2_string); header = false)) == """ YES """ @@ -421,17 +399,17 @@ YES @test submodels(b_fdx) isa Vector{<:AbstractModel} @test submodels(b_p) isa Vector{<:AbstractModel} -@test join(displaymodel.(submodels(b_nsx); header = false)) == """ +@test_broken join(displaymodel.(submodels(b_nsx); header = false)) == """ YES NO """ -@test join(displaymodel.(submodels(b_fsx); header = false)) == """ +@test_broken join(displaymodel.(submodels(b_fsx); header = false)) == """ YES NO """ -@test join(displaymodel.(submodels(b_fdx); header = false)) == """ +@test_broken join(displaymodel.(submodels(b_fdx); header = false)) == """ ┐ q ├ ✔ YES └ ✘ NO @@ -440,7 +418,7 @@ NO YES """ -@test join(displaymodel.(submodels(b_p); header = false)) == """ +@test_broken join(displaymodel.(submodels(b_p); header = false)) == """ ┐ s ├ ✔ YES └ ✘ NO @@ -460,7 +438,7 @@ YES """ @test submodels(d1_string) isa Vector{<:AbstractModel} -@test join(displaymodel.(submodels(d1_string); header = false)) == """ +@test_broken join(displaymodel.(submodels(d1_string); header = false)) == """ ┐(r ∧ s) ∧ t └ ✔ YES YES @@ -471,7 +449,7 @@ YES """ @test submodels(dt1) isa Vector{<:AbstractModel} -@test join(displaymodel.(submodels(dt1); header = false)) == """ +@test_broken join(displaymodel.(submodels(dt1); header = false)) == """ ┐ s ├ ✔ YES └ ✘ NO @@ -491,7 +469,7 @@ YES """ @test submodels(dt2) isa Vector{<:AbstractModel} -@test join(displaymodel.(submodels(dt2); header = false)) == """ +@test_broken join(displaymodel.(submodels(dt2); header = false)) == """ ┐ q ├ ✔ YES └ ✘ NO @@ -501,7 +479,7 @@ YES """ @test submodels(msm) isa Vector{<:AbstractModel} -@test join(displaymodel.(submodels(msm); header = false)) == """ +@test_broken join(displaymodel.(submodels(msm); header = false)) == """ 2 1.5 """ @@ -528,19 +506,19 @@ YES @test_broken listrules(dlmodel) @test listrules(r1_string) isa Vector{<:Rule} -@test join(displaymodel.(listrules(r1_string); header = false)) == """ +@test_broken join(displaymodel.(listrules(r1_string); header = false)) == """ ┐(r ∧ s) ∧ t └ ✔ YES """ @test listrules(r2_string) isa Vector{<:Rule} -@test join(displaymodel.(listrules(r2_string); header = false)) == """ +@test_broken join(displaymodel.(listrules(r2_string); header = false)) == """ ┐¬(r) └ ✔ YES """ @test listrules(d1_string) isa Vector{<:Rule} -@test join(displaymodel.(listrules(d1_string); header = false)) == """ +@test_broken join(displaymodel.(listrules(d1_string); header = false)) == """ ┐(r ∧ s) ∧ t └ ✔ YES ┐(¬((r ∧ s) ∧ t)) ∧ (¬(r)) @@ -550,7 +528,7 @@ YES """ @test listrules(b_nsx) isa Vector{<:Rule} -@test join(displaymodel.(listrules(b_nsx); header = false)) == """ +@test_broken join(displaymodel.(listrules(b_nsx); header = false)) == """ ┐q └ ✔ YES ┐¬(q) @@ -558,7 +536,7 @@ YES """ @test listrules(b_fsx) isa Vector{<:Rule} -@test join(displaymodel.(listrules(b_fsx); header = false)) == """ +@test_broken join(displaymodel.(listrules(b_fsx); header = false)) == """ ┐s └ ✔ YES ┐¬(s) @@ -566,7 +544,7 @@ YES """ @test listrules(b_fdx) isa Vector{<:Rule} -@test join(displaymodel.(listrules(b_fdx); header = false)) == """ +@test_broken join(displaymodel.(listrules(b_fdx); header = false)) == """ ┐(t) ∧ (q) └ ✔ YES ┐(t) ∧ (¬(q)) @@ -576,7 +554,7 @@ YES """ @test listrules(b_p) isa Vector{<:Rule} -@test join(displaymodel.(listrules(b_p); header = false)) == """ +@test_broken join(displaymodel.(listrules(b_p); header = false)) == """ ┐(r) ∧ (s) └ ✔ YES ┐(r) ∧ (¬(s)) @@ -590,7 +568,7 @@ YES """ @test listrules(dt1) isa Vector{<:Rule} -@test join(displaymodel.(listrules(dt1); header = false)) == """ +@test_broken join(displaymodel.(listrules(dt1); header = false)) == """ ┐(r) ∧ (s) └ ✔ YES ┐(r) ∧ (¬(s)) @@ -604,7 +582,7 @@ YES """ @test listrules(dt2) isa Vector{<:Rule} -@test join(displaymodel.(listrules(dt2); header = false)) == """ +@test_broken join(displaymodel.(listrules(dt2); header = false)) == """ ┐(t) ∧ (q) └ ✔ YES ┐(t) ∧ (¬(q)) @@ -614,7 +592,7 @@ YES """ @test listrules(msm) isa Vector{<:Rule} -@test join(displaymodel.(listrules(msm); header = false)) == """ +@test_broken join(displaymodel.(listrules(msm); header = false)) == """ ┐q └ ✔ 2 ┐¬(q) diff --git a/test/parse.jl b/test/parse.jl index 26d8f3a..1c04f62 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -1,3 +1,4 @@ +using Logging using SoleModels: parsecondition @test_nowarn SoleModels.parsefeature(SoleModels.VarFeature{Float64}, "min[V1]") @@ -12,11 +13,10 @@ using SoleModels: parsecondition @test_logs (:warn,) parsecondition(SoleModels.ScalarCondition, "F1 > 2"; featuretype = SoleModels.Feature) @test_logs (:warn,) parsecondition(SoleModels.ScalarCondition, "1 > 2"; featuretype = SoleModels.Feature{Int}) -@test_logs (:warn,) parsecondition(SoleModels.ScalarCondition, "1 > 2"; featuretype = SoleModels.Feature{Proposition{Int}}) C = SoleModels.ScalarCondition -@test_logs (:warn,) SoleModels.parsecondition(C, "min[V1] <= 32") +@test_logs min_level=Logging.Error SoleModels.parsecondition(C, "min[V1] <= 32") @test_logs (:warn,) SoleModels.parsecondition(C, "min[V1] <= 32"; featvaltype = Float64) @test_nowarn SoleModels.parsecondition(C, "min[V1] <= 32"; featvaltype = Float64, featuretype = SoleModels.AbstractUnivariateFeature) diff --git a/test/runtests.jl b/test/runtests.jl index a573c87..c9d8928 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,7 +17,8 @@ println("Julia version: ", VERSION) test_suites = [ ("Logisets", [ - # "logisets/logisets.jl", TODO bring back + "logisets/logisets.jl", + # "logisets/memosets.jl", # TODO bring back "logisets/cube2logiset.jl", "logisets/dataframe2logiset.jl", "logisets/multilogisets.jl", From 19bf4c2d1e769dbc364155cc6e1b720dec2df6a6 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Mon, 10 Jul 2023 01:01:45 +0200 Subject: [PATCH 39/77] Clean check.jl --- src/logisets/check.jl | 30 +----------------------------- src/logisets/supported-logiset.jl | 1 - 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/src/logisets/check.jl b/src/logisets/check.jl index 241be10..666d024 100644 --- a/src/logisets/check.jl +++ b/src/logisets/check.jl @@ -1,37 +1,9 @@ -############################################################################################ -# Check algorithms -############################################################################################ - -function check( - φ::SoleLogics.AbstractFormula, - X::AbstractLogiset, - i_instance::Integer, - args...; - kwargs... -) - check(tree(φ), X, i_instance, args...; kwargs...) -end - -function check( - φ::SoleLogics.SyntaxTree{ - Union{ - DiamondRelationalOperator{typeof(SoleLogics.tocenterrel)}, - BoxRelationalOperator{typeof(SoleLogics.tocenterrel)}, - } - }, - X::AbstractLogiset, - i_instance::Integer; - kwargs... -) - check(first(children(φ)), X, i_instance, SoleLogics.centralworld(frame(X, i_instance)); kwargs...) -end - function check( φ::SoleLogics.SyntaxTree, X::AbstractLogiset{W,U}, i_instance::Integer, - w::Union{Nothing,<:AbstractWorld} = nothing; + w::Union{Nothing,<:AbstractWorld}; use_memo::Union{Nothing,AbstractMemoset{<:AbstractWorld},AbstractVector{<:AbstractDict{<:FT,<:WorldSet}}} = nothing, perform_normalization::Bool = true, memo_max_height::Union{Nothing,Int} = nothing, diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index fb27db0..43f3451 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -347,7 +347,6 @@ end # nfeatures(X::SupportedLogiset) = nfeatures(base(X)) # nrelations(X::SupportedLogiset) = nrelations(base(X)) # relations(X::SupportedLogiset) = relations(base(X)) -# fwd(X::SupportedLogiset) = fwd(base(X)) # worldtype(X::SupportedLogiset{V,W}) where {V,W} = W # TODO remove: From 813a1db5085e2403396aa25a9cdf32fe8eab5c3b Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:39:36 +0200 Subject: [PATCH 40/77] Fix check, fix most warnings. --- src/logisets/MLJ-interface.jl | 6 ++---- src/logisets/check.jl | 8 ++++++-- src/logisets/scalar/dataset-bindings.jl | 12 ++++++++---- src/logisets/scalar/memosets.jl | 13 +++++++++---- src/models/evaluation.jl | 6 +++--- test/logisets/multilogisets.jl | 15 +++++++-------- 6 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/logisets/MLJ-interface.jl b/src/logisets/MLJ-interface.jl index aaca5c3..8214379 100644 --- a/src/logisets/MLJ-interface.jl +++ b/src/logisets/MLJ-interface.jl @@ -92,11 +92,9 @@ function Tables.columnnames(row::Tuple{MultiLogiset,Integer}) 1:nmodalities(row[1]) end -using MLJBase -using MLJModelInterface -import MLJModelInterface: selectrows, _selectrows +import MLJBase: selectrows -function MLJModelInterface.selectrows(X::Union{AbstractLogiset,MultiLogiset}, r) +function selectrows(X::Union{AbstractLogiset,MultiLogiset}, r) r = r isa Integer ? (r:r) : r # return slicedataset(X, r; return_view = true) return Tables.subset(X, r) diff --git a/src/logisets/check.jl b/src/logisets/check.jl index 666d024..53cf7f6 100644 --- a/src/logisets/check.jl +++ b/src/logisets/check.jl @@ -1,9 +1,12 @@ +""" +TODO docstring +""" function check( φ::SoleLogics.SyntaxTree, X::AbstractLogiset{W,U}, i_instance::Integer, - w::Union{Nothing,<:AbstractWorld}; + w::Union{Nothing,<:AbstractWorld} = nothing; # TODO remove defaulting use_memo::Union{Nothing,AbstractMemoset{<:AbstractWorld},AbstractVector{<:AbstractDict{<:FT,<:WorldSet}}} = nothing, perform_normalization::Bool = true, memo_max_height::Union{Nothing,Int} = nothing, @@ -76,10 +79,11 @@ function check( worldset = begin if !isnothing(onestep_memoset) && SoleLogics.height(ψ) == 1 && tok isa SoleLogics.AbstractRelationalOperator && + ((SoleLogics.relation(tok) == globalrel && nworlds(fr) != 1) || !SoleLogics.isgrounding(SoleLogics.relation(tok))) && SoleLogics.ismodal(tok) && SoleLogics.isunary(tok) && SoleLogics.isdiamond(tok) && token(first(children(ψ))) isa Proposition && # Note: metacond with same aggregator also works. TODO maybe use Conditions with aggregators inside and look those up. - (onestep_memoset_is_complete || metacond(atom(token(first(children(ψ))))) in metaconditions(onestep_memoset)) && + (onestep_memoset_is_complete || (metacond(atom(token(first(children(ψ))))) in metaconditions(onestep_memoset))) && true # println("ONESTEP!") # println(syntaxstring(ψ)) diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index dc892c6..98eb1ee 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -151,12 +151,16 @@ function scalarlogiset( end if isnothing(features) - is_propositional_dataset = all(i_instance->nworlds(frame(dataset, i_instance)) == 1, 1:ninstances(dataset)) features = begin - if is_propositional_dataset - [UnivariateValue{vareltype(dataset, i_var)}(i_var) for i_var in 1:nvariables(dataset)] + if isnothing(conditions) + is_propositional_dataset = all(i_instance->nworlds(frame(dataset, i_instance)) == 1, 1:ninstances(dataset)) + if is_propositional_dataset + [UnivariateValue{vareltype(dataset, i_var)}(i_var) for i_var in 1:nvariables(dataset)] + else + vcat([[UnivariateMax{vareltype(dataset, i_var)}(i_var), UnivariateMin{vareltype(dataset, i_var)}(i_var)] for i_var in 1:nvariables(dataset)]...) + end else - vcat([[UnivariateMax{vareltype(dataset, i_var)}(i_var), UnivariateMin{vareltype(dataset, i_var)}(i_var)] for i_var in 1:nvariables(dataset)]...) + unique(feature.(conditions)) end end end diff --git a/src/logisets/scalar/memosets.jl b/src/logisets/scalar/memosets.jl index 742eb35..77ce720 100644 --- a/src/logisets/scalar/memosets.jl +++ b/src/logisets/scalar/memosets.jl @@ -1,17 +1,22 @@ using UniqueVectors import Base: in, findfirst +using Suppressor + +_in(item, uv) = Base.in(item, uv) +_findfirst(p::UniqueVectors.EqualTo, uv) = Base.findfirst(p, uv) + +# TODO suppress warnings: +# @suppress begin # Fixes -# https://github.com/garrison/UniqueVectors.jl/blob/d63669d1f5c8f7ee4f4bf3f2920bc4afe33fe676/src/UniqueVectors.jl#L56 +# https://github.com/garrison/UniqueVectors.jl/issues/24 Base.in(item, uv::UniqueVector) = haskey(uv.lookup, item) Base.findfirst(p::UniqueVectors.EqualTo, uv::UniqueVector) = get(uv.lookup, p.x, nothing) +# end -_in(item, uv) = Base.in(item, uv) _in(item, uv::UniqueVector) = haskey(uv.lookup, item) -_findfirst(p::UniqueVectors.EqualTo, uv) = Base.findfirst(p, uv) _findfirst(p::UniqueVectors.EqualTo, uv::UniqueVector) = get(uv.lookup, p.x, nothing) - """ A full memoization structure used for checking formulas of scalar conditions on datasets with scalar features. This structure is the equivalent to [`FullMemoset`](@ref), diff --git a/src/models/evaluation.jl b/src/models/evaluation.jl index b511b36..cc2c9b8 100644 --- a/src/models/evaluation.jl +++ b/src/models/evaluation.jl @@ -1,5 +1,5 @@ using SoleModels -using MLJBase +using MLJBase: accuracy, mae using SoleModels: LeafModel import SoleLogics: npropositions @@ -17,9 +17,9 @@ function readmetrics(m::LeafModel{L}; digits = 2) where {L<:Label} _gts = info(m).supporting_labels _preds = _gts = info(m).supporting_predictions if L <: CLabel - (; ninstances = length(_gts), confidence = round(MLJBase.accuracy(_gts, _preds); digits = digits)) + (; ninstances = length(_gts), confidence = round(accuracy(_gts, _preds); digits = digits)) elseif L <: RLabel - (; ninstances = length(_gts), mae = round(MLJBase.mae(_gts, _preds); digits = digits)) + (; ninstances = length(_gts), mae = round(mae(_gts, _preds); digits = digits)) else error("Could not compute readmetrics with unknown label type: $(L).") end diff --git a/test/logisets/multilogisets.jl b/test/logisets/multilogisets.jl index e761952..11c27d5 100644 --- a/test/logisets/multilogisets.jl +++ b/test/logisets/multilogisets.jl @@ -19,11 +19,11 @@ multilogiset = @test_nowarn scalarlogiset(multidataset) generic_features = collect(Iterators.flatten([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:_nvars])) metaconditions = [ScalarMetaCondition(feature, >) for feature in generic_features] -multilogiset = @test_nowarn scalarlogiset(multidataset; use_full_memoization = false, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) -multilogiset = @test_nowarn scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) +multilogiset = @test_logs (:warn,) scalarlogiset(multidataset; use_full_memoization = false, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) +multilogiset = @test_logs (:warn,) scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) metaconditions = vcat([[ScalarMetaCondition(feature, >), ScalarMetaCondition(feature, <)] for feature in generic_features]...) -complete_supported_multilogiset = @test_nowarn scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) +complete_supported_multilogiset = @test_logs (:warn,) scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) @test_nowarn slicedataset(multilogiset, [2,1]) @@ -43,18 +43,17 @@ multiformulas = [begin for (i_modality, relations) in enumerate(multirelations) f = randformula(rng, 3, alph, [SoleLogics.BASE_PROPOSITIONAL_OPERATORS..., vcat([[DiamondRelationalOperator(r), BoxRelationalOperator(r)] for r in relations]...)[1:32:end]...]) if rand(Bool) - # coin = rand(1:3) - coin = rand(2:3) + coin = rand(1:2) f = begin if coin == 1 BoxRelationalOperator(globalrel)(f) - elseif coin == 2 + else BoxRelationalOperator(SoleLogics.tocenterrel)(f) # else # TODO operator for going to a world. Note that this cannot be done with singletons... # AnchoredFormula(f, SoleModels.WorldCheck(rand(collect(allworlds(modality(multilogiset, i_modality), i_instance))))) - else - f + # else + # f end end _formulas_dict[i_modality] = f From 4711351b372c3de06efc654522b6095d760c7847 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Mon, 10 Jul 2023 23:00:26 +0200 Subject: [PATCH 41/77] Fix MultiLogiset and add compat --- Project.toml | 2 ++ src/logisets/multilogiset.jl | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b9258e3..1fd0583 100644 --- a/Project.toml +++ b/Project.toml @@ -32,6 +32,8 @@ UniqueVectors = "2fbcfb34-fd0c-5fbb-b5d7-e826d8f5b0a9" [compat] BenchmarkTools = "1" +SoleLogics = "0.3.0" +SoleData = "0.9.0" julia = "1" [extras] diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index 28fbb98..0aaeeb8 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -15,7 +15,9 @@ See also [`AbstractLogiset`](@ref), [`minify`](@ref). """ -struct MultiLogiset{L<:AbstractLogiset} <: AbstractInterpretationSet{AbstractKripkeStructure{W where W<:AbstractWorld,C where C<:AbstractCondition{_F where _F<:AbstractFeature},T where T<:TruthValue,FR where FR<:AbstractFrame{W where W<:SoleLogics.AbstractWorld}}} +struct MultiLogiset{L<:AbstractLogiset} <: AbstractInterpretationSet{ + AbstractKripkeStructure{W,C where C<:AbstractCondition{_F where _F<:AbstractFeature},T where T<:TruthValue,FR where FR<:AbstractFrame{W}} where W<:AbstractWorld +} modalities :: Vector{L} From 87b6b7d669461af7bc4ee2d24d3397c4a954bec2 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Thu, 13 Jul 2023 11:55:18 +0200 Subject: [PATCH 42/77] Fix CLabel --- src/logisets/features.jl | 2 +- src/machine-learning.jl | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/logisets/features.jl b/src/logisets/features.jl index 98ffe57..4a25a5e 100644 --- a/src/logisets/features.jl +++ b/src/logisets/features.jl @@ -31,7 +31,7 @@ function parsefeature( kwargs... ) return error("Please, provide method parsefeature(::$(FT), " * - " expression::$(typeof(expression)); kwargs...).") + "expression::$(typeof(expression)); kwargs...).") end ############################################################################################ diff --git a/src/machine-learning.jl b/src/machine-learning.jl index 8f4df01..7ce5fdb 100644 --- a/src/machine-learning.jl +++ b/src/machine-learning.jl @@ -1,7 +1,8 @@ using FillArrays +using CategoricalArrays doc_supervised_ml = """ - const CLabel = Union{String,Integer} + const CLabel = Union{String,Integer,CategoricalValue} const RLabel = AbstractFloat const Label = Union{CLabel,RLabel} @@ -9,7 +10,7 @@ Types for supervised machine learning labels (classification and regression). """ """$(doc_supervised_ml)""" -const CLabel = Union{String,Integer} +const CLabel = Union{String,Integer,CategoricalValue} """$(doc_supervised_ml)""" const RLabel = AbstractFloat """$(doc_supervised_ml)""" From b1821aad112ab347b2cfc6d4a0aa2786ff2b18a2 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 14 Jul 2023 20:38:53 +0200 Subject: [PATCH 43/77] Fix display and docstrings --- docs/Manifest.toml | 137 +++++++++++++++++----- src/logisets/check.jl | 4 +- src/logisets/logiset.jl | 4 +- src/logisets/memosets.jl | 5 +- src/logisets/scalar/dataset-bindings.jl | 3 +- src/logisets/scalar/memosets.jl | 3 +- src/logisets/scalar/onestep-memoset.jl | 3 +- src/logisets/scalar/random.jl | 3 +- src/logisets/supported-logiset.jl | 5 +- src/models/base.jl | 7 +- src/models/print.jl | 18 +-- src/models/symbolic-utils.jl | 149 +++++++++++++++--------- 12 files changed, 220 insertions(+), 121 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 2d3c775..a3b6604 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -14,17 +14,21 @@ deps = ["LinearAlgebra", "Requires"] git-tree-sha1 = "76289dc51920fdc6e0013c872ba9551d54961c24" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" version = "3.6.2" +weakdeps = ["StaticArrays"] [deps.Adapt.extensions] AdaptStaticArraysExt = "StaticArrays" - [deps.Adapt.weakdeps] - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.1" +[[deps.ArnoldiMethod]] +deps = ["LinearAlgebra", "Random", "StaticArrays"] +git-tree-sha1 = "62e51b39331de8911e4a7ff6f5aaf38a5f4cc0ae" +uuid = "ec485272-7323-5ecc-a04f-4719b315124d" +version = "0.2.0" + [[deps.ArrayInterfaceCore]] deps = ["LinearAlgebra", "SnoopPrecompile", "SparseArrays", "SuiteSparse"] git-tree-sha1 = "e5f08b5689b1aad068e01751889f2f615c7db36d" @@ -79,6 +83,18 @@ version = "0.10.8" SentinelArrays = "91c51154-3ec4-41a3-a24f-3f23e20d615c" StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" +[[deps.CategoricalDistributions]] +deps = ["CategoricalArrays", "Distributions", "Missings", "OrderedCollections", "Random", "ScientificTypes"] +git-tree-sha1 = "da68989f027dcefa74d44a452c9e36af9730a70d" +uuid = "af321ab8-2d2e-40a6-b165-3d674595d28e" +version = "0.1.10" + + [deps.CategoricalDistributions.extensions] + UnivariateFiniteDisplayExt = "UnicodePlots" + + [deps.CategoricalDistributions.weakdeps] + UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" + [[deps.CodeTracking]] deps = ["InteractiveUtils", "UUIDs"] git-tree-sha1 = "d730914ef30a06732bdd9f763f6cc32e92ffbff1" @@ -117,25 +133,22 @@ deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" version = "1.0.2+0" -[[deps.ComputedFieldTypes]] -git-tree-sha1 = "059a8d396af73d574b679223c91ce209c0d3d809" -uuid = "459fdd68-db75-56b8-8c15-d717a790f88e" -version = "1.0.1" +[[deps.ComputationalResources]] +git-tree-sha1 = "52cb3ec90e8a8bea0e62e275ba577ad0f74821f7" +uuid = "ed09eef8-17a6-5b46-8889-db040fac31e3" +version = "0.3.2" [[deps.ConstructionBase]] deps = ["LinearAlgebra"] git-tree-sha1 = "738fec4d684a9a6ee9598a8bfee305b26831f28c" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" version = "1.5.2" +weakdeps = ["IntervalSets", "StaticArrays"] [deps.ConstructionBase.extensions] ConstructionBaseIntervalSetsExt = "IntervalSets" ConstructionBaseStaticArraysExt = "StaticArrays" - [deps.ConstructionBase.weakdeps] - IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - [[deps.Crayons]] git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" @@ -147,10 +160,10 @@ uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" version = "1.15.0" [[deps.DataFrames]] -deps = ["Compat", "DataAPI", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SnoopPrecompile", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] -git-tree-sha1 = "aa51303df86f8626a962fccb878430cdb0a97eee" +deps = ["Compat", "DataAPI", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] +git-tree-sha1 = "089d29c0fc00a190661517e4f3cba5dcb3fd0c08" uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" -version = "1.5.0" +version = "1.6.0" [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] @@ -242,9 +255,9 @@ uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[deps.FillArrays]] deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"] -git-tree-sha1 = "2250347838b28a108d1967663cba57bfb3c02a58" +git-tree-sha1 = "e5556303fd8c9ad4a8fceccd406ef3433ddb4c45" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.3.0" +version = "1.4.0" [[deps.FixedPointNumbers]] deps = ["Statistics"] @@ -267,11 +280,17 @@ version = "1.1.3" deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" +[[deps.Graphs]] +deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] +git-tree-sha1 = "1cf1d7dcb4bc32d7b4a5add4232db3750c27ecb4" +uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" +version = "1.8.0" + [[deps.HypergeometricFunctions]] deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] -git-tree-sha1 = "0ec02c648befc2f94156eaef13b0f38106212f3f" +git-tree-sha1 = "a6105a85261f35b45aeb394dc917a03d907ec3c3" uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.17" +version = "0.3.19" [[deps.IOCapture]] deps = ["Logging", "Random"] @@ -284,6 +303,11 @@ git-tree-sha1 = "ce1566720fd6b19ff3411404d4b977acd4814f9f" uuid = "313cdc1a-70c2-5d6a-ae34-0150d3930a38" version = "1.1.1" +[[deps.Inflate]] +git-tree-sha1 = "5cd07aab533df5170988219191dfad0519391428" +uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" +version = "0.1.3" + [[deps.InlineStrings]] deps = ["Parsers"] git-tree-sha1 = "9cc2baf75c6d09f9da536ddf58eb2f29dedaf461" @@ -394,12 +418,30 @@ version = "0.3.24" [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +[[deps.LossFunctions]] +deps = ["CategoricalArrays", "Markdown", "Statistics"] +git-tree-sha1 = "44a7bfeb7b5eb9386a62b9cccc6e21f406c15bea" +uuid = "30fc2ffe-d236-52d8-8643-a9d8f7c094a7" +version = "0.10.0" + [[deps.LoweredCodeUtils]] deps = ["JuliaInterpreter"] git-tree-sha1 = "60168780555f3e663c536500aa790b6368adc02a" uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b" version = "2.3.0" +[[deps.MLJBase]] +deps = ["CategoricalArrays", "CategoricalDistributions", "ComputationalResources", "Dates", "DelimitedFiles", "Distributed", "Distributions", "InteractiveUtils", "InvertedIndices", "LinearAlgebra", "LossFunctions", "MLJModelInterface", "Missings", "OrderedCollections", "Parameters", "PrettyTables", "ProgressMeter", "Random", "ScientificTypes", "Serialization", "StatisticalTraits", "Statistics", "StatsBase", "Tables"] +git-tree-sha1 = "4cc167b6c0a3ab25d7050e4ac38fe119e97cd1ab" +uuid = "a7f614a8-145f-11e9-1d2a-a57a1082229d" +version = "0.21.11" + +[[deps.MLJModelInterface]] +deps = ["Random", "ScientificTypesBase", "StatisticalTraits"] +git-tree-sha1 = "c8b7e632d6754a5e36c0d94a4b466a5ba3a30128" +uuid = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" +version = "1.8.0" + [[deps.MacroTools]] deps = ["Markdown", "Random"] git-tree-sha1 = "42324d08725e200c23d4dfb549e0d5d89dede2d2" @@ -471,6 +513,12 @@ git-tree-sha1 = "67eae2738d63117a196f497d7db789821bce61d1" uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" version = "0.11.17" +[[deps.Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.3" + [[deps.Parsers]] deps = ["Dates", "PrecompileTools", "UUIDs"] git-tree-sha1 = "4b2e829ee66d4218e0cef22c0a64ee37cf258c29" @@ -502,9 +550,9 @@ version = "1.4.0" [[deps.PrettyTables]] deps = ["Crayons", "Formatting", "LaTeXStrings", "Markdown", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "213579618ec1f42dea7dd637a42785a608b1ea9c" +git-tree-sha1 = "331cc8048cba270591eab381e7aa3e2e3fef7f5e" uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "2.2.4" +version = "2.2.5" [[deps.Printf]] deps = ["Unicode"] @@ -599,6 +647,16 @@ version = "1.4.0" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +[[deps.SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[deps.SimpleTraits]] +deps = ["InteractiveUtils", "MacroTools"] +git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" +uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" +version = "0.9.4" + [[deps.SnoopPrecompile]] deps = ["Preferences"] git-tree-sha1 = "e760a70afdcd461cf01a575947738d359234665c" @@ -627,10 +685,8 @@ uuid = "b002da8f-3cb3-4d91-bbe3-2953433912b5" version = "0.3.0" [[deps.SoleModels]] -deps = ["BenchmarkTools", "ComputedFieldTypes", "DataStructures", "FillArrays", "FunctionWrappers", "LinearAlgebra", "Logging", "ProgressMeter", "Random", "Reexport", "Revise", "SoleBase", "SoleData", "SoleLogics", "StatsBase", "Suppressor", "ThreadSafeDicts"] -git-tree-sha1 = "23bda16c79ff5878812b3b4284ea8cfe8559fbd8" -repo-rev = "main" -repo-url = "https://github.com/aclai-lab/SoleModels.jl" +deps = ["BenchmarkTools", "CSV", "CategoricalArrays", "DataFrames", "DataStructures", "FillArrays", "FunctionWrappers", "Graphs", "Lazy", "LinearAlgebra", "Logging", "MLJBase", "MLJModelInterface", "ProgressMeter", "Random", "Reexport", "Revise", "SoleBase", "SoleData", "SoleLogics", "StatsBase", "Suppressor", "Tables", "ThreadSafeDicts", "UniqueVectors"] +path = ".." uuid = "4249d9c7-3290-4ddd-961c-e1d3ec2467f8" version = "0.1.0" @@ -656,6 +712,21 @@ version = "2.3.0" [deps.SpecialFunctions.weakdeps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "Random", "StaticArraysCore"] +git-tree-sha1 = "0da7e6b70d1bb40b1ace3b576da9ea2992f76318" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.6.0" +weakdeps = ["Statistics"] + + [deps.StaticArrays.extensions] + StaticArraysStatisticsExt = "Statistics" + +[[deps.StaticArraysCore]] +git-tree-sha1 = "1d5708d926c76a505052d0d24a846d5da08bc3a4" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.1" + [[deps.StatisticalTraits]] deps = ["ScientificTypesBase"] git-tree-sha1 = "30b9236691858e13f167ce829490a68e1a597782" @@ -675,9 +746,9 @@ version = "1.6.0" [[deps.StatsBase]] deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "d1bf48bfcc554a3761a133fe3a9bb01488e06916" +git-tree-sha1 = "75ebe04c5bed70b91614d684259b661c9e6274a4" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.33.21" +version = "0.34.0" [[deps.StatsFuns]] deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] @@ -740,9 +811,9 @@ deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [[deps.ThreadSafeDicts]] -git-tree-sha1 = "8800982249f3657fbf176624db7d39107efb0062" +git-tree-sha1 = "cdc778da600ff2166239a80cf4d82a9b118611d8" uuid = "4239201d-c60e-5e0a-9702-85d713665ba7" -version = "0.1.0" +version = "0.1.3" [[deps.TranscodingStreams]] deps = ["Random", "Test"] @@ -754,9 +825,19 @@ version = "0.9.13" deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +[[deps.UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +[[deps.UniqueVectors]] +git-tree-sha1 = "0a150de447f51342cf2e5b379137b823f3934864" +uuid = "2fbcfb34-fd0c-5fbb-b5d7-e826d8f5b0a9" +version = "1.2.0" + [[deps.WeakRefStrings]] deps = ["DataAPI", "InlineStrings", "Parsers"] git-tree-sha1 = "b1be2855ed9ed8eac54e5caff2afcdb442d52c23" diff --git a/src/logisets/check.jl b/src/logisets/check.jl index 53cf7f6..c9d9d66 100644 --- a/src/logisets/check.jl +++ b/src/logisets/check.jl @@ -1,7 +1,5 @@ -""" -TODO docstring -""" +# TODO docstring function check( φ::SoleLogics.SyntaxTree, X::AbstractLogiset{W,U}, diff --git a/src/logisets/logiset.jl b/src/logisets/logiset.jl index 351e028..4f5b3d0 100644 --- a/src/logisets/logiset.jl +++ b/src/logisets/logiset.jl @@ -49,9 +49,7 @@ function readfeature( return error("Please, provide method readfeature(::$(typeof(X)), featchannel::$(typeof(featchannel)), w::$(typeof(w)), feature::$(typeof(feature))).") end -""" -TODO -""" +# TODO docstring function featvalue( X::AbstractLogiset{W}, i_instance::Integer, diff --git a/src/logisets/memosets.jl b/src/logisets/memosets.jl index 5f73635..9d5120b 100644 --- a/src/logisets/memosets.jl +++ b/src/logisets/memosets.jl @@ -88,6 +88,8 @@ abstract type AbstractFullMemoset{W<:AbstractWorld,U,FT<:AbstractFeature,FR<:Abs ############################################################################################ +# # Examples +# TODO add example showing that checking is faster when using this structure. """ A generic, full memoization structure that works for any *crisp* logic; For each instance of a dataset, @@ -95,9 +97,6 @@ this structure associates formulas to the set of worlds where the formula holds; it was introduced by Emerson-Clarke for the well-known model checking algorithm for CTL*. -# Examples -TODO add example showing that checking is faster when using this structure. - See also [`SuportedLogiset`](@ref), [`AbstractMemoset`](@ref), diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 98eb1ee..7560822 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -349,6 +349,7 @@ function naturalconditions( metaconditions end +# TODO examples """ naturalgrouping( X::AbstractDataFrame; @@ -359,8 +360,6 @@ Return variables grouped by their logical nature; the nature of a variable is automatically derived from its type (e.g., Real, Vector{<:Real} or Matrix{<:Real}) and frame. All instances must have the same frame (e.g., channel size/number of worlds). - -TODO example """ function naturalgrouping( X::AbstractDataFrame; diff --git a/src/logisets/scalar/memosets.jl b/src/logisets/scalar/memosets.jl index 77ce720..a8324c5 100644 --- a/src/logisets/scalar/memosets.jl +++ b/src/logisets/scalar/memosets.jl @@ -17,13 +17,12 @@ Base.findfirst(p::UniqueVectors.EqualTo, uv::UniqueVector) = get(uv.lookup, p.x, _in(item, uv::UniqueVector) = haskey(uv.lookup, item) _findfirst(p::UniqueVectors.EqualTo, uv::UniqueVector) = get(uv.lookup, p.x, nothing) +# TODO complete and explain """ A full memoization structure used for checking formulas of scalar conditions on datasets with scalar features. This structure is the equivalent to [`FullMemoset`](@ref), but with scalar features some important optimizations can be done. -TODO explain - See also [`FullMemoset`](@ref), [`SuportedLogiset`](@ref), diff --git a/src/logisets/scalar/onestep-memoset.jl b/src/logisets/scalar/onestep-memoset.jl index 3232383..f91d3ae 100644 --- a/src/logisets/scalar/onestep-memoset.jl +++ b/src/logisets/scalar/onestep-memoset.jl @@ -539,13 +539,12 @@ end ############################################################################################ +# TODO explain """ A generic, one-step memoization structure used for checking specific formulas of scalar conditions on datasets with scalar features. The formulas are of type ⟨R⟩ (f ⋈ t) -TODO explain - See also [`FullMemoset`](@ref), [`SuportedLogiset`](@ref), diff --git a/src/logisets/scalar/random.jl b/src/logisets/scalar/random.jl index 0d3ab8d..fd945ca 100644 --- a/src/logisets/scalar/random.jl +++ b/src/logisets/scalar/random.jl @@ -1,5 +1,6 @@ import Base: rand +# TODO @Michele Examples """ function Base.rand( rng::AbstractRNG, @@ -18,8 +19,6 @@ is limited to those with `feature`; - if `test_operator` is specified, then the set of metaconditions (feature-operator pairs) is limited to those with `test_operator`. -TODO Examples - See also [`BoundedScalarConditions`](@ref), [`ScalarCondition`](@ref), diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index 43f3451..a758b82 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -1,10 +1,9 @@ +# # Examples +# TODO add example showing that checking is faster when using this structure. """ A logiset associated to a number of cascading full or one-step memoization structures, that are used when checking formulas. -# Examples -TODO add example showing that checking is faster when using this structure. - See also [`SuportedLogiset`](@ref), [`AbstractFullMemoset`](@ref), diff --git a/src/models/base.jl b/src/models/base.jl index 10d9f2f..056ebe8 100644 --- a/src/models/base.jl +++ b/src/models/base.jl @@ -152,11 +152,11 @@ end Return whether a model is symbolic or not. A model is said to be `symbolic` when its application relies on checking formulas of a certain logical language (see [`SoleLogics`](@ref) package) on the instance. -Symbolic models provide a form of transparent and interpretable modeling. +Symbolic models provide a form of transparent and interpretable modeling, +as a symbolic model can be synthethised into a set of mutually exclusive logical rules. Instead, a model is said to be functional when it encodes an algebraic mathematical function (e.g., a neural network). -TODO explain listrules/cascade/rules A symbolic model is one where the computation has a *rule-base structure*. See also [`apply`](@ref), @@ -261,6 +261,7 @@ apply(m::ConstantModel, d::AbstractInterpretationSet; kwargs...) = fill(outcome( convert(::Type{ConstantModel{O}}, o::O) where {O} = ConstantModel{O}(o) convert(::Type{<:AbstractModel{F}}, m::ConstantModel) where {F} = ConstantModel{F}(m) +# TODO @Michele explain functional_args/functional_kwargs """ struct FunctionModel{O} <: LeafModel{O} f::FunctionWrapper{O} @@ -272,8 +273,6 @@ in order to compute the outcome. Over efficiency concerns, it is mandatory to ma the output type `O` by wrapping the `Function` into an object of type `FunctionWrapper{O}`. -TODO @Michele explain functional_args/functional_kwargs - See also [`ConstantModel`](@ref), [`FunctionWrapper`](@ref), [`LeafModel`](@ref). """ struct FunctionModel{O} <: LeafModel{O} diff --git a/src/models/print.jl b/src/models/print.jl index fe74699..3f621e9 100644 --- a/src/models/print.jl +++ b/src/models/print.jl @@ -109,19 +109,9 @@ end """$(doc_printdisplay_model)""" function displaymodel( m::AbstractModel; - header = DEFAULT_HEADER, - indentation_str = "", - indentation = default_indentation, - depth = 0, - max_depth = nothing, - show_subtree_info = false, - show_metrics = false, - show_intermediate_finals = false, - tree_mode = false, - syntaxstring_kwargs = (;), kwargs... ) - @_display_submodel m indentation_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs + _displaymodel(m; kwargs...) end function _displaymodel( @@ -248,11 +238,11 @@ function _displaymodel( subm_str = @_display_submodel consequent(m) ind_str indentation depth max_depth show_subtree_info show_metrics show_intermediate_finals tree_mode syntaxstring_kwargs kwargs print(io, subm_str) else - print(io, "$(pipe)$(ant_str)") - ind_str = "" + line = "$(pipe)$(ant_str)" * " $(arrow) " + ind_str = indentation_str * repeat(" ", length(line) + length("▣") + 1) subm_str = @_display_submodel consequent(m) ind_str indentation depth max_depth show_subtree_info false show_intermediate_finals tree_mode syntaxstring_kwargs kwargs show_metrics != false && (subm_str = rstrip(subm_str, '\n') * " : $(get_metrics_string(m; (show_metrics isa NamedTuple ? show_metrics : [])...))") - print(io, " $(arrow) ") + print(io, line) print(io, subm_str) end else diff --git a/src/models/symbolic-utils.jl b/src/models/symbolic-utils.jl index 9883aec..bf28dc2 100644 --- a/src/models/symbolic-utils.jl +++ b/src/models/symbolic-utils.jl @@ -159,6 +159,30 @@ advanceformula(r::Rule, assumed_formula::Union{Nothing,AbstractFormula}) = List the immediate rules equivalent to a symbolic model. +# Examples +```julia-repl +julia> using SoleLogics + +julia> branch = Branch(SoleLogics.parseformula("p"), Branch(SoleLogics.parseformula("q"), "YES", "NO"), "NO") + p +├✔ q +│├✔ YES +│└✘ NO +└✘ NO + + +julia> printmodel.(listimmediaterules(branch); tree_mode = true); +▣ p +└✔ q + ├✔ YES + └✘ NO + +▣ ¬(p) +└✔ NO + + +``` + See also [`listrules`](@ref), [`issymbolic`](@ref), [`AbstractModel`](@ref). """ listimmediaterules(m::AbstractModel{O} where {O})::Rule{<:O} = @@ -200,6 +224,12 @@ listimmediaterules(m::MixedSymbolicModel) = listimmediaterules(root(m)) ############################################################################################ ############################################################################################ +# TODO @Michi esempi +# TODO +# The keyword argument `force_syntaxtree`, when set to true, causes the logical antecedents +# in the returned rules to be represented as `SyntaxTree`s, as opposed to other syntax +# structure (e.g., `LeftmostConjunctiveForm`). + """ listrules( m::AbstractModel; @@ -210,67 +240,35 @@ listimmediaterules(m::MixedSymbolicModel) = listimmediaterules(root(m)) )::Vector{<:Rule} Return a list of rules capturing the knowledge enclosed in symbolic model. -The behavior of any symbolic model can be extracted and represented as a +The behavior of any symbolic model can be synthesised and represented as a set of mutually exclusive (and jointly exaustive, if the model is closed) rules, which can be useful for many purposes. -The keyword argument `force_syntaxtree`, when set to true, causes the logical antecedents -in the returned rules to be represented as `SyntaxTree`s, as opposed to other syntax -structure (e.g., `LeftmostConjunctiveForm`). - # Examples -# TODO @Michi questi esempi non sono chiari: cosa è r2_string? ```julia-repl -@test listrules(r2_string) isa Vector{<:Rule} -julia> print(join(displaymodel.(listrules(rule); header = false))) -┐¬(r) -└ ✔ YES - -julia> print(join(displaymodel.(listrules(decision_list); header = false))) -┐(r ∧ s) ∧ t -└ ✔ YES -┐¬(r) -└ ✔ YES -┐⊤ -└ ✔ YES - -@test listrules(rcmodel) isa Vector{<:Rule} -julia> print(join(displaymodel.(listrules(rule_cascade); header = false))) -┐(p ∧ (q ∨ r)) ∧ ((p ∧ (q ∨ r)) ∧ (p ∧ (q ∨ r))) -└ ✔ 1 - -julia> print(join(displaymodel.(listrules(branch); header = false))) -┐r ∧ s -└ ✔ YES -┐r ∧ (¬(s)) -└ ✔ NO -┐(¬(r)) ∧ (t ∧ q) -└ ✔ YES -┐(¬(r)) ∧ (t ∧ (¬(q))) -└ ✔ NO -┐(¬(r)) ∧ (¬(t)) -└ ✔ YES - -julia> print(join(displaymodel.(listrules(decision_tree); header = false))) -┐r ∧ s -└ ✔ YES -┐r ∧ (¬(s)) -└ ✔ NO -┐(¬(r)) ∧ (t ∧ q) -└ ✔ YES -┐(¬(r)) ∧ (t ∧ (¬(q))) -└ ✔ NO -┐(¬(r)) ∧ (¬(t)) -└ ✔ YES - -julia> print(join(displaymodel.(listrules(mixed_symbolic_model); header = false))) -┐q -└ ✔ 2 -┐¬(q) -└ ✔ 1.5 +julia> using SoleLogics + +julia> branch = Branch(SoleLogics.parseformula("p"), Branch(SoleLogics.parseformula("q"), "YES", "NO"), "NO") + p +├✔ q +│├✔ YES +│└✘ NO +└✘ NO + + +julia> printmodel.(listrules(branch); tree_mode = true); +▣ p ∧ q +└✔ YES + +▣ p ∧ ¬q +└✔ NO + +▣ ¬p +└✔ NO + ``` -See also [`listimmediaterules`](@ref), [`issymbolic`](@ref), [`LeafModel`](@ref), +See also [`listimmediaterules`](@ref), [`joinrules`](@ref), [`issymbolic`](@ref), [`LeafModel`](@ref), [`AbstractModel`](@ref). """ function listrules(m::AbstractModel; kwargs...) @@ -358,7 +356,7 @@ function listrules( if (use_shortforms && haskey(info(subrule), :shortform)) info(subrule)[:shortform], true else - (flag ? antecedent(m) : antecedent(m)), false + (flag ? antecedent(m) : ¬antecedent(m)), false end end antformula = force_syntaxtree ? tree(antformula) : antformula @@ -415,6 +413,47 @@ listrules(m::MixedSymbolicModel; kwargs...) = listrules(root(m); kwargs...) ############################################################################################ +""" + joinrules(rules::AbstractVector{<:Rule})::Vector{<:Rule} + +Return a set of rules, with exactly one rule per different outcome from the input set of rules. +For each outcome, the output rule is computed as the logical disjunction of the antecedents +of the input rules for that outcome. + +# Examples +```julia-repl +julia> using SoleLogics + +julia> branch = Branch(SoleLogics.parseformula("p"), Branch(SoleLogics.parseformula("q"), "YES", "NO"), "NO") + p +├✔ q +│├✔ YES +│└✘ NO +└✘ NO + + +julia> printmodel.(listrules(branch); tree_mode = true); +▣ p ∧ q +└✔ YES + +▣ p ∧ ¬q +└✔ NO + +▣ ¬p +└✔ NO + +julia> printmodel.(joinrules(listrules(branch)); tree_mode = true); +▣ (p ∧ q) +└✔ YES + +▣ (p ∧ ¬q) ∨ ¬p +└✔ NO + +``` + +See also [`listrules`](@ref), [`issymbolic`](@ref), [`DISJUNCTION`](@ref), [`LeafModel`](@ref), +[`AbstractModel`](@ref). +""" function joinrules( rules::AbstractVector{ <:Rule{<:Any,<:SoleModels.AbstractFormula,<:SoleModels.ConstantModel} From 3729ecda76d7435ab7280920cabf094f154ff23d Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 16 Jul 2023 16:29:40 +0200 Subject: [PATCH 44/77] Bump version --- Project.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 7475b30..7813c04 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SoleModels" uuid = "4249d9c7-3290-4ddd-961c-e1d3ec2467f8" authors = ["Michele GHIOTTI", "Giovanni PAGLIARINI", "Eduard I. STAN"] -version = "0.1.0" +version = "0.2.0" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" @@ -39,9 +39,9 @@ julia = "1" ProgressMeter = "1" Reexport = "1" Revise = "3" -SoleBase = "0.9.2" -SoleData = "0.9.1" -SoleLogics = "0.3.0" +SoleBase = "0.10" +SoleData = "0.10" +SoleLogics = "0.4" StatsBase = "0.34" Suppressor = "0.2" ThreadSafeDicts = "0.1.0" From 87d4046057e21cc1a73c82e256cb3615ad421f45 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 16 Jul 2023 16:40:04 +0200 Subject: [PATCH 45/77] Fix dep --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 7813c04..2fd71ce 100644 --- a/Project.toml +++ b/Project.toml @@ -39,7 +39,7 @@ julia = "1" ProgressMeter = "1" Reexport = "1" Revise = "3" -SoleBase = "0.10" +SoleBase = "0.11" SoleData = "0.10" SoleLogics = "0.4" StatsBase = "0.34" From b6950d226a75b8d12a52878120b50ac24a76dabc Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 16 Jul 2023 18:37:36 +0200 Subject: [PATCH 46/77] Fix dep --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 7813c04..2fd71ce 100644 --- a/Project.toml +++ b/Project.toml @@ -39,7 +39,7 @@ julia = "1" ProgressMeter = "1" Reexport = "1" Revise = "3" -SoleBase = "0.10" +SoleBase = "0.11" SoleData = "0.10" SoleLogics = "0.4" StatsBase = "0.34" From 6afd1e253f7109ea6a42a05162dd625334c32e63 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 16 Jul 2023 18:56:47 +0200 Subject: [PATCH 47/77] Fix test and dependencies --- Project.toml | 9 +++++++++ test/logisets/MLJ.jl | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 2fd71ce..62fa831 100644 --- a/Project.toml +++ b/Project.toml @@ -32,10 +32,17 @@ UniqueVectors = "2fbcfb34-fd0c-5fbb-b5d7-e826d8f5b0a9" [compat] BenchmarkTools = "1" +CategoricalArrays = "0.10.8" +CSV = "0.10.11" +DataFrames = "1.6.0" DataStructures = "0.18" FillArrays = "1" FunctionWrappers = "1" +Graphs = "1.8.0" julia = "1" +Lazy = "0.15.1" +MLJBase = "0.21.11" +MLJModelInterface = "1.8.0" ProgressMeter = "1" Reexport = "1" Revise = "3" @@ -44,7 +51,9 @@ SoleData = "0.10" SoleLogics = "0.4" StatsBase = "0.34" Suppressor = "0.2" +Tables = "1.10.1" ThreadSafeDicts = "0.1.0" +UniqueVectors = "1.2.0" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/logisets/MLJ.jl b/test/logisets/MLJ.jl index 77bee4c..7fd9e9d 100644 --- a/test/logisets/MLJ.jl +++ b/test/logisets/MLJ.jl @@ -12,11 +12,11 @@ multidataset, multirelations = collect.(zip([ multilogiset = @test_nowarn scalarlogiset(multidataset) multilogiset = scalarlogiset(multidataset; relations = multirelations, conditions = vcat([[SoleModels.ScalarMetaCondition(UnivariateMin(i), >), SoleModels.ScalarMetaCondition(UnivariateMax(i), <)] for i in 1:_nvars]...)) -@test_nowarn X = modality(multilogiset, 1) +X = @test_nowarn modality(multilogiset, 1) @test_nowarn selectrows(X, 1:10) @test_nowarn selectrows(multilogiset, 1:10) @test_nowarn selectrows(SoleModels.base(X), 1:10) -@test_nowarn X = modality(multilogiset, 2) +X = @test_nowarn modality(multilogiset, 2) @test_nowarn selectrows(SoleModels.base(X), 1:10) mod2 = modality(multilogiset, 2) From e96f384ef0f5e4a16ea502ef8288c3104e5e8220 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 18 Jul 2023 03:18:58 +0200 Subject: [PATCH 48/77] Add comment to check --- src/logisets/check.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logisets/check.jl b/src/logisets/check.jl index c9d9d66..5bd2db1 100644 --- a/src/logisets/check.jl +++ b/src/logisets/check.jl @@ -97,7 +97,7 @@ function check( elseif tok isa SoleLogics.AbstractOperator _c(SoleLogics.collateworlds(fr, tok, map(f->readformula(memo_structure, f), children(ψ)))) elseif tok isa Proposition - condition = atom(tok) + condition = atom(tok) # TODO write check(tok, X, i_instance, _w) and use it here instead of checkcondition. _f(_w->checkcondition(condition, X, i_instance, _w), _c(allworlds(fr))) else error("Unexpected token encountered in _check: $(typeof(tok))") From 51b652dd47188aed95ba8c21d1a31ff5b5b268cd Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 21 Jul 2023 05:08:23 +0200 Subject: [PATCH 49/77] Bump and add example datasets --- Project.toml | 9 +- src/SoleModels.jl | 2 + src/example-datasets.jl | 239 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 src/example-datasets.jl diff --git a/Project.toml b/Project.toml index 62fa831..4aa800c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SoleModels" uuid = "4249d9c7-3290-4ddd-961c-e1d3ec2467f8" authors = ["Michele GHIOTTI", "Giovanni PAGLIARINI", "Eduard I. STAN"] -version = "0.2.0" +version = "0.2.1" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" @@ -12,6 +12,7 @@ DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" +HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" Lazy = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" @@ -29,22 +30,23 @@ Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" ThreadSafeDicts = "4239201d-c60e-5e0a-9702-85d713665ba7" UniqueVectors = "2fbcfb34-fd0c-5fbb-b5d7-e826d8f5b0a9" +ZipFile = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea" [compat] BenchmarkTools = "1" -CategoricalArrays = "0.10.8" CSV = "0.10.11" +CategoricalArrays = "0.10.8" DataFrames = "1.6.0" DataStructures = "0.18" FillArrays = "1" FunctionWrappers = "1" Graphs = "1.8.0" -julia = "1" Lazy = "0.15.1" MLJBase = "0.21.11" MLJModelInterface = "1.8.0" ProgressMeter = "1" Reexport = "1" +HTTP = "1.9.14" Revise = "3" SoleBase = "0.11" SoleData = "0.10" @@ -54,6 +56,7 @@ Suppressor = "0.2" Tables = "1.10.1" ThreadSafeDicts = "0.1.0" UniqueVectors = "1.2.0" +julia = "1" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/src/SoleModels.jl b/src/SoleModels.jl index a6225da..13931e4 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -27,6 +27,8 @@ include("utils/minify.jl") include("MLJ-utils.jl") +include("example-datasets.jl") + ############################################################################################ ############################################################################################ ############################################################################################ diff --git a/src/example-datasets.jl b/src/example-datasets.jl new file mode 100644 index 0000000..1a9a0ae --- /dev/null +++ b/src/example-datasets.jl @@ -0,0 +1,239 @@ +using HTTP +using ZipFile +using ARFFFiles +using DataFrames + +using DataStructures: OrderedDict + +# SoleModels.load_arff_dataset("NATOPS") + +function load_arff_dataset(dataset_name, path = "http://www.timeseriesclassification.com/ClassificationDownloads/$(dataset_name).zip") +# function load_arff_dataset(dataset_name, path = "../datasets/Multivariate_arff/$(dataset_name)") + (X_train, y_train), (X_test, y_test) = begin + if(any(startswith.(path, ["https://", "http://"]))) + r = HTTP.get(path); + z = ZipFile.Reader(IOBuffer(r.body)) + # ( + # ARFFFiles.load(DataFrame, z.files[[f.name == "$(dataset_name)_TRAIN.arff" for f in z.files]][1]), + # ARFFFiles.load(DataFrame, z.files[[f.name == "$(dataset_name)_TEST.arff" for f in z.files]][1]), + # ) + ( + read(z.files[[f.name == "$(dataset_name)_TRAIN.arff" for f in z.files]][1], String) |> parseARFF, + read(z.files[[f.name == "$(dataset_name)_TEST.arff" for f in z.files]][1], String) |> parseARFF, + ) + else + ( + # ARFFFiles.load(DataFrame, "$(path)/$(dataset_name)_TRAIN.arff"), + # ARFFFiles.load(DataFrame, "$(path)/$(dataset_name)_TEST.arff"), + read("$(path)/$(dataset_name)_TEST.arff", String) |> parseARFF, + read("$(path)/$(dataset_name)_TRAIN.arff", String) |> parseARFF, + ) + end + end + + @assert dataset_name == "NATOPS" "This code is only for showcasing. Need to expand code to comprehend more datasets." + # variable_names = [ + # "Hand tip left, X coordinate", + # "Hand tip left, Y coordinate", + # "Hand tip left, Z coordinate", + # "Hand tip right, X coordinate", + # "Hand tip right, Y coordinate", + # "Hand tip right, Z coordinate", + # "Elbow left, X coordinate", + # "Elbow left, Y coordinate", + # "Elbow left, Z coordinate", + # "Elbow right, X coordinate", + # "Elbow right, Y coordinate", + # "Elbow right, Z coordinate", + # "Wrist left, X coordinate", + # "Wrist left, Y coordinate", + # "Wrist left, Z coordinate", + # "Wrist right, X coordinate", + # "Wrist right, Y coordinate", + # "Wrist right, Z coordinate", + # "Thumb left, X coordinate", + # "Thumb left, Y coordinate", + # "Thumb left, Z coordinate", + # "Thumb right, X coordinate", + # "Thumb right, Y coordinate", + # "Thumb right, Z coordinate", + # ] + + variable_names = [ + "X[Hand tip l]", + "Y[Hand tip l]", + "Z[Hand tip l]", + "X[Hand tip r]", + "Y[Hand tip r]", + "Z[Hand tip r]", + "X[Elbow l]", + "Y[Elbow l]", + "Z[Elbow l]", + "X[Elbow r]", + "Y[Elbow r]", + "Z[Elbow r]", + "X[Wrist l]", + "Y[Wrist l]", + "Z[Wrist l]", + "X[Wrist r]", + "Y[Wrist r]", + "Z[Wrist r]", + "X[Thumb l]", + "Y[Thumb l]", + "Z[Thumb l]", + "X[Thumb r]", + "Y[Thumb r]", + "Z[Thumb r]", + ] + + + variable_names_latex = [ + "\\text{hand tip l}_X", + "\\text{hand tip l}_Y", + "\\text{hand tip l}_Z", + "\\text{hand tip r}_X", + "\\text{hand tip r}_Y", + "\\text{hand tip r}_Z", + "\\text{elbow l}_X", + "\\text{elbow l}_Y", + "\\text{elbow l}_Z", + "\\text{elbow r}_X", + "\\text{elbow r}_Y", + "\\text{elbow r}_Z", + "\\text{wrist l}_X", + "\\text{wrist l}_Y", + "\\text{wrist l}_Z", + "\\text{wrist r}_X", + "\\text{wrist r}_Y", + "\\text{wrist r}_Z", + "\\text{thumb l}_X", + "\\text{thumb l}_Y", + "\\text{thumb l}_Z", + "\\text{thumb r}_X", + "\\text{thumb r}_Y", + "\\text{thumb r}_Z", + ] + X_train = fix_dataframe(X_train, variable_names) + X_test = fix_dataframe(X_test, variable_names) + + class_names = [ + "I have command", + "All clear", + "Not clear", + "Spread wings", + "Fold wings", + "Lock wings", + ] + + fix_class_names(y) = class_names[round(Int, parse(Float64, y))] + + y_train = map(fix_class_names, y_train) + y_test = map(fix_class_names, y_test) + + @assert nrow(X_train) == length(y_train) "$(nrow(X_train)), $(length(y_train))" + + ((X_train, y_train), (X_test, y_test)) +end + +const _ARFF_SPACE = UInt8(' ') +const _ARFF_COMMENT = UInt8('%') +const _ARFF_AT = UInt8('@') +const _ARFF_SEP = UInt8(',') +const _ARFF_NEWLINE = UInt8('\n') +const _ARFF_NOMSTART = UInt8('{') +const _ARFF_NOMEND = UInt8('}') +const _ARFF_ESC = UInt8('\\') +const _ARFF_MISSING = UInt8('?') +const _ARFF_RELMARK = UInt8('\'') + +# function readARFF(path::String) +# open(path, "r") do io +# df = DataFrame() +# classes = String[] +# lines = readlines(io) ... +function parseARFF(arffstring::String) + df = DataFrame() + classes = String[] + lines = split(arffstring, "\n") + for i in 1:length(lines) + line = lines[i] + # If not empty line or comment + if !isempty(line) + if UInt8(line[1]) != _ARFF_COMMENT + sline = split(line, " ") + # println(sline[1][1]) + # If the first symbol is @ + if UInt8(sline[1][1]) == _ARFF_AT + # If @relation + if sline[1][2:end] == "relation" + # println("Relation: " * sline[2]) + end + + # if sline[1][2:end] == "variable" && sline[2] == "class" + # classes = sline[3][2:end-1] + # println(classes) + # end + # data, first char is ' + elseif UInt8(sline[1][1]) == _ARFF_RELMARK + sline[1] = sline[1][2:end] + data_and_class = split(sline[1],"\'") + string_data = split(data_and_class[1], "\\n") + class = data_and_class[2][2:end] + + if isempty(names(df)) + for i in 1:length(string_data) + insertcols!(df, Symbol("V$(i)") => Array{Float64, 1}[]) # add the variables as 1,2,3,ecc. + end + end + + float_data = Dict{Int,Vector{Float64}}() + + for i in 1:length(string_data) + float_data[i] = map(x->parse(Float64,x), split(string_data[i], ",")) + end + + # @show float_data + + + push!(df, [float_data[i] for i in 1:length(string_data)]) + push!(classes, class) + # @show data + # @show class + end + end + end + end + + # for i in eachrow(df) + # println(typeof(i)) + # break + # end + p = sortperm(eachrow(df), by=x->classes[rownumber(x)]) + + return df[p, :], classes[p] +end + +function fix_dataframe(df, variable_names = nothing) + s = unique(size.(df[:,1])) + @assert length(s) == 1 "$(s)" + @assert length(s[1]) == 1 "$(s[1])" + nvars, npoints = length(names(df)), s[1][1] + old_var_names = names(df) + X = OrderedDict() + + if isnothing(variable_names) + variable_names = ["V$(i_var)" for i_var in 1:nvars] + end + + @assert nvars == length(variable_names) + + for (i_var,var) in enumerate(variable_names) + X[Symbol(var)] = [row[i_var] for row in eachrow(df)] + end + + X = DataFrame(X) + # Y = df[:,end] + + # X, string.(Y) + # X, Y +end From 9f30a2bef945c1c3c166135c8045d450742e4535 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 21 Jul 2023 05:28:12 +0200 Subject: [PATCH 50/77] Fix example dataset --- src/example-datasets.jl | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/example-datasets.jl b/src/example-datasets.jl index 1a9a0ae..5cd0a02 100644 --- a/src/example-datasets.jl +++ b/src/example-datasets.jl @@ -1,14 +1,19 @@ using HTTP using ZipFile -using ARFFFiles using DataFrames using DataStructures: OrderedDict # SoleModels.load_arff_dataset("NATOPS") -function load_arff_dataset(dataset_name, path = "http://www.timeseriesclassification.com/ClassificationDownloads/$(dataset_name).zip") -# function load_arff_dataset(dataset_name, path = "../datasets/Multivariate_arff/$(dataset_name)") +function load_arff_dataset( + dataset_name, + split = :all; + path = "http://www.timeseriesclassification.com/ClassificationDownloads/$(dataset_name).zip" +) + @assert split in [:train, :test, :split, :all] "Unexpected value for split parameter: $(split). Allowed: :train, :test, :split, :all." + + # function load_arff_dataset(dataset_name, path = "../datasets/Multivariate_arff/$(dataset_name)") (X_train, y_train), (X_test, y_test) = begin if(any(startswith.(path, ["https://", "http://"]))) r = HTTP.get(path); @@ -132,7 +137,17 @@ function load_arff_dataset(dataset_name, path = "http://www.timeseriesclassifica @assert nrow(X_train) == length(y_train) "$(nrow(X_train)), $(length(y_train))" - ((X_train, y_train), (X_test, y_test)) + if split == :all + vcat(X_train, X_test), vcat(y_train, y_test) + elseif split == :train + (X_train, y_train) + elseif split == :test + (X_test, y_test) + elseif split == :traintest + ((X_train, y_train), (X_test, y_test)) + else + error("Unexpected value for split parameter: $(split)") + end end const _ARFF_SPACE = UInt8(' ') From e32aebf55f85b1a60348decfa4f14113f56cb8ca Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 21 Jul 2023 05:54:07 +0200 Subject: [PATCH 51/77] Minor --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 4aa800c..796165f 100644 --- a/Project.toml +++ b/Project.toml @@ -41,12 +41,12 @@ DataStructures = "0.18" FillArrays = "1" FunctionWrappers = "1" Graphs = "1.8.0" +HTTP = "1.9.14" Lazy = "0.15.1" MLJBase = "0.21.11" MLJModelInterface = "1.8.0" ProgressMeter = "1" Reexport = "1" -HTTP = "1.9.14" Revise = "3" SoleBase = "0.11" SoleData = "0.10" From fe910a723eab6acb444bd02e6a9b716afea90855 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 21 Jul 2023 06:05:12 +0200 Subject: [PATCH 52/77] Add compat --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 796165f..099ad7e 100644 --- a/Project.toml +++ b/Project.toml @@ -56,6 +56,7 @@ Suppressor = "0.2" Tables = "1.10.1" ThreadSafeDicts = "0.1.0" UniqueVectors = "1.2.0" +ZipFile = "0.10.1" julia = "1" [extras] From f2f1c302407e692bfb3a4c0665952506d8cad312 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 21 Jul 2023 06:35:10 +0200 Subject: [PATCH 53/77] Minor --- src/models/print.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/models/print.jl b/src/models/print.jl index 3f621e9..46bff1b 100644 --- a/src/models/print.jl +++ b/src/models/print.jl @@ -197,7 +197,7 @@ function _displaymodel( show_metrics = false, show_intermediate_finals = false, tree_mode = (subtreeheight(m) != 1), - syntaxstring_kwargs = (;), + syntaxstring_kwargs = (; parentheses_at_propositions = true), arrow = "🠮", # ⮞, 🡆, 🠮, 🠲, => kwargs..., ) @@ -263,7 +263,7 @@ function _displaymodel( show_metrics = false, show_intermediate_finals = false, tree_mode = true, # subtreeheight(m) != 1 - syntaxstring_kwargs = (;), + syntaxstring_kwargs = (; parentheses_at_propositions = true), kwargs..., ) io = IOBuffer() @@ -326,7 +326,7 @@ function _displaymodel( show_metrics = false, show_intermediate_finals = false, tree_mode = true, - syntaxstring_kwargs = (;), + syntaxstring_kwargs = (; parentheses_at_propositions = true), kwargs..., ) io = IOBuffer() @@ -385,7 +385,7 @@ function _displaymodel( show_metrics = false, show_intermediate_finals = false, tree_mode = true, - syntaxstring_kwargs = (;), + syntaxstring_kwargs = (; parentheses_at_propositions = true), kwargs..., ) io = IOBuffer() @@ -423,7 +423,7 @@ function _displaymodel( show_metrics = false, show_intermediate_finals = false, tree_mode = true, - syntaxstring_kwargs = (;), + syntaxstring_kwargs = (; parentheses_at_propositions = true), kwargs..., ) io = IOBuffer() @@ -463,7 +463,7 @@ function _displaymodel( show_metrics = false, show_intermediate_finals = false, tree_mode = true, - syntaxstring_kwargs = (;), + syntaxstring_kwargs = (; parentheses_at_propositions = true), kwargs..., ) io = IOBuffer() From 8cd676f62fc8f1f3d9f0e2b7719ae7cf763e25aa Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 21 Jul 2023 06:45:50 +0200 Subject: [PATCH 54/77] Fix example datasets --- src/example-datasets.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/example-datasets.jl b/src/example-datasets.jl index 5cd0a02..93fcf49 100644 --- a/src/example-datasets.jl +++ b/src/example-datasets.jl @@ -137,6 +137,8 @@ function load_arff_dataset( @assert nrow(X_train) == length(y_train) "$(nrow(X_train)), $(length(y_train))" + y_train = categorical(y_train) + y_test = categorical(y_test) if split == :all vcat(X_train, X_test), vcat(y_train, y_test) elseif split == :train From db74223c0dbaec85ffa4ba120a86d317ab76d4d3 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 21 Jul 2023 06:46:03 +0200 Subject: [PATCH 55/77] Bump --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 099ad7e..136119f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SoleModels" uuid = "4249d9c7-3290-4ddd-961c-e1d3ec2467f8" authors = ["Michele GHIOTTI", "Giovanni PAGLIARINI", "Eduard I. STAN"] -version = "0.2.1" +version = "0.2.2" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" From ced6b695bfdcb51acad36316224b6a6507f7f336 Mon Sep 17 00:00:00 2001 From: Michele21 Date: Fri, 21 Jul 2023 08:30:30 +0200 Subject: [PATCH 56/77] adding the logo to the documentation --- docs/src/assets/logo.png | Bin 0 -> 33960 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/src/assets/logo.png diff --git a/docs/src/assets/logo.png b/docs/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8d6c40c1686766ade1395418b96b0abf3eb03fb0 GIT binary patch literal 33960 zcmeFYWl&trw>LV2yF-8=gS)%CyGw8%+}+(ZxVr=o8l0dZIE3Kt!AWqqljnc5?t4F+ zs{4LBT{BgC?_O*5TE8XTJ-sJJRapiVi4X|@0HDgrN~!|@KvBq-2LTT9WVJnO9snTa z^VQUKS2y(nIlDSpf$c3p?mo_zAWLts6#(G9Ql6`mVcwZ)`NkGY0>x?aHN@bPwd&W4 z08#X5l$1r5PvWy-%0@dmyn_I*!@k!GkGCjeruJX1;2!1_G0!jvkJHGfBOCB?nBn$&He9J75w|tTa|MWmZ$bNo|1Ot!Zb5M zI?Cxjx&S87v6CzKQfl8gAlGlprEGBG_9E#66(}fn{*J|5>ds&ND(c5)vn9~XiM6&(+a9^+jUV6Olp3bwo@H??i z9z8Tf9Ipa~Pdj9P$asy;)i^yf8I<&Fj$9~fvB;Y~?bGz>G-8a8qVoM>9Yj48s&CXa zOB@{@dB;|twUH*&YU9%L%kl+{r_BOfshql`;HP}$$Fv<#`>?-J=^^X`Z$t?F4!L{Z z_|5a}59(bFuDEy8Gi9*HaFxkt6iel|YxdT=7bIIB;Oo`XlJT~d^MRJ69+Mf2XCO11 z!aXwLoPzsKM8v2HdSqK4vuO1F(V&?)IkW{+8J6uO(+D0^$S+Ep#@j5{JPXiAES|V{ zzH)g--vybQ47?2m^BBQ1ZxU7t?1)6Wy^%SP9^bo@%6(;2)Ewqabz5-xJBHGV9J}h) zHhNVerjX>imKFWe$(5t(LnGg_x;w`J-4+-dyiY%BbC>b`54OIAd7&Gzj>L0J3dZGn zv@KUoHqjQQ>*#s3E@sv>?JB>2{^`-a-hSpXSo~30czQ98P2WFz4tdqz(WUd`6_*$B_6z@>6~IbMJkjFmqg|`;FsN zm)BO96Ji{7`|-T37wI>O3tySLkJv%d@}`~z2&HEkCoQS9L+ZZo%Tbo}mVdo8|5A9~ZDerg&q^gkh-wYZY?wT0$V z>`Ncs5@UdL7MC)G{)`$qX#$$!q39k= z#ng-0Uckc{Cdtny9AOlc#t}3;o_tQkT_^?7e3A?A|c=N=b z;Pe*HDd<~XYlj>rl$v%veO|@YE>0uf^uj#aN@-a_F(bRw{q2UcY_#P|uf@&-rZoz^ zpXR@XfM~u6+^`XjL@COM?FC?q*^v@MPn7)Oe1BXSS6m%xk2P_E7eHmiqV5NSqnv!@ zP-ce3K^$=Ak5IP*8&N$>)8R{yIQpSRx%~kir5K5^sGoRx|h>1SlfjwjL-MMYi zf-~rMm-;vELw>)`v_Dy;d!1fzM)aaN4(D8qA9+!>$Z(!7erU?u)D_qvREWu}bxsEp z>hIT}ElV|ooOgzB6;!1~4pMb)OAAQjq?_@8Z=4T{8zx-=?H?FgJ{U8Wj-FCBl{3VP z23G(PXn1_c4++WZGe=_W%FVW-mf63#uKT+;WONP>idWt2=;pvP{lL0nQgC1JUH39O z1)$crV7br-Jt-Fsh#}JIwxh2I&3raw^*3Nz2-^%^6t%;H3a}Sf1K|(!3PdzxP3X(< z90??7-!6*PVbb=l2y&RDKzkdj{o$ae+kA&vFg^$1e59<8JEEkgPDnXK{3fhuc+Z1} z*C+w|`4}zh9mr{W%{5W@@-!^S|JTz@+nn0k@vZpy-!DfCuD za|6Ff{P-n2XG6PI2TlQ|p?HclDdb7B=1lo<7IF8x#IYKqs2zDrGVa7iH_}Nt(LB(f zw$TDpFGNu z$OjSGBG3L=nd+ygmnyz6Bc&5SP35D_LM#FJMV|1#CDYJ8;v0yf{VQK?7zN z8QUx+^(MkL%O!*0IG+w{TeHI^^aSm@Vl+&DUIN>NbsS9UHP-Rl_Iz}DJ!jgSU>I(S^c$(jZ&?fpb1cnBR}U~Ps~ij zAf_2z;@6R)sQ`0yRCq)`wn#gT8jB~!p^sQ>!}!(fTY>lB?(b0F=zc=DmyhV;qW}=$ zYD1tI-?+yzTSuIChx z-|@!YX^T;CPG!eIw~%(-K+4e2!fy>qZM#ZbXLcU5F3i0=75so01h{lbF1r(nnG?1F z5T%pCAmH+9emTOk?p{hlrJO1%m;MIYX)Z5t{wd09JD%JrRd?LAD6cdp`ccE1@N-E) zW@nGX&@-mwRbNUu0+&r91Ik9q$9*F6gC8U%6y%CtTt18Y;<&R|Hi6(q4X_g>oQ)5l zIADO1e)+wMx{UQ5+V_5!HAKhhfk_;Kk}7>!1!^+v(dPFKP<+?~oIur6vf|ix@IPqP zA2Up9abD&+tbQNud>kuhS2f_+B>=n}$B|v!NFs-K>sOmH#Eu%R$4nh<>0nmMd#pz#aRy0Ug$d7F~m2nqHPYUg<+js3CkC zGEE8Oge_jiP~g}a-5`GDgvZX)EVbp6G=#QU6wiT#`}D1l<1x%u&?LXzmY{AXR9zlP6Z zFo=?+@0ac-$lZUSlhhPOK(Ax`g{M&T7WpmTy8;TN`KX%cQ7pjr3Jvp(5eBo$B(p;y zqfhh*fZBLt;92dLWM+H)*pIR-Hu1z?b=-mSz;Tp^eP0wM&*~s$IT;TsAb^KGEz-YI z8KRffyfP-Qjg|D7(Ki-2<1ptY}AdSVMc~K>xZ%pY9<1`?9AnOGjDP&rqoTc4cO_WYJEhppONgx zUtwf2W3M9q)X}JVt{eS8DZmf&MJa6p*1qIILVBw6)mB3{*55(rddm1)?PcV*C|h;F zThk@L!+`c(HVj}}^_&rZsY$#J7BfVKkJgzms#X*|TGWEU#FV`%OqH>iaV#-({E4i>xc2Rb;XYJY- z3A-_LD5Io7R`Z2FP75zDyw5N`ArmnrhQDjmJ{`zj=HO`E+#k|g)U6?e9%mqhAFX#3 zvq>eJAoa*~eAhTi`frXcI}35$r#w=*nJA9_+zJ-d>YEIg+6Wyi;W zQ!lJ?DK+Q}AB!kO1{!L!1ONME=uVY|;OeqzSPyW!s~mN{S`gmMH0{U~-bXbC@%}<9 zOWLatA?R}5t?A5Wq*{MaQ7P}dOR67Fh9id005@7~q7lJR5FpTh0;jehjdXg7G5g4f z^P8CviDS?!cLJ3^oVtYPL`)7!#y!A;_&xsO4OR>MNQ4_0uKHTl1;P7j3!2cOMubON z3T6pfYcqI`!ol5_5DihhE%>O|s6?aYN-Zm{t6e(PrIfc0iKK-?jYGP+CF13de#@|a zL9n8Nn9%s(J!`FikjfF)lPGfU_)vhK3xg*U!KXS|4%e?Pe5t zOm~p-JW=QfDwD|2a$$)RlvdSe83*`}Ic_s3x*X(R-jsdN`r#GgW2v|s58-IBOf8q; z!snA6lyDvv@g2qN_=?8`Ud_GArV?z%Zp(au+hDbBVa8xM+c&9?`406O=0g8@;w zPr8!14O9)E$qa{R8*^$+U_zFvUD$}jj-CGWELdgU_@Tekb zXMNi(1bprJFF9u)ZHQ{`^X4 zNm5#JMASSqactDLS%WwG1y-V1ERZp2akJdT!S@Eck#z0a8zTw~W7H*nt#MwkeJbT( z`q=iY;Os^OJti#*WBG;~+7h947U7kti(*k_~85BC3iuOk$JVd1RR$Vnj@k8j=}mM z5AUi2-PT;hZWby?x8{<}EQ)2g>=8~)@NMZxN_0|!7yuprKChAHPU*9ohiq#7maUOz z;f!jSVNH>*J9otN$^DROnKqm)$`P{DN5?XeXqUz=Zx9p*#+fPsPh4P5X>k;Yg)sL3 ztsmL)OV^1(YEqyivXVL4Ot-MBV`x|7^0#*!@1uiibIeGVWi-ddZEJ$GWxP{) zn9qqt1vo>Ym=^NF;aJG@p0lUK7!Y#%^wiJFuqgwsBgl&JT$@~7Zf?yCEb9T>&^PVb zQ?e$*Ed8d0W~wd0d?*r54lbzDm;tVS(qq$5i)Y8-Q2@7@qjD*Ioc9rvqDS9dxu^^h znr+t)YGQW6mPOC=F}UEv0PJ=G4<*l6LN9|lL#V*`EN(u!%PUD3Cm3GkpX?#`WS9GD z{D`1}?jXSP>)7GiGmA4X~$D|Z>m%9;Z^+DBh^ zYIku6*IU~}3HE;ZLs!*wBcQV0b86aF^qDv@ZhSl{uAy!3X!67qS`60zIc!J%&?5wZ zF{ht`>h`Ogx1MXmt|e^2l$trt7NS3DFqKSU`H1GF)L-g?@1evL_k2bFr;3^sN)H(i zF64Tf+jcWLa6=ZlIguC`~VL7#WA*CF=86=IdDZI1q=x zJhcTAfhBd6CQ=^YdUptGOgZwK?`kfr3v*%dXBu^MZ|~TBxLHrpVF7h0U!y&R90t2m zj3AuE1?L%0IC<#O;T}@SK7S!^8B;?X8pC{Zqf5-7H8_A)^Vf|HoM4ts!NnR{TbK_7 zHtcMwS~ZprpFP!0wi6a8DfQl4e|6v!2}7sQHr7XFAAKh_Bv&#X-3e=eW%|a94WmiJ6y1PJYA|6*DHw1&ttwKIpa*hx{J4f^*>7)x^Rf- z4Q$7QXT#oi6@W?*-L=3)-#~H#hZIQcBgK%)r;b9BStlH+4$BSD4l{01Tg1pBNKH2p z@74t3eO#J{S-uh+vI_23FU)ZZN!B!)^_MPHbYP}(APPN0@C>b57pVO@OsqcTbc%Y+ zwpToSD5>`aM)F5faW6?(Xg)wpWe#>RS2wM`!seXAp5NIGoZ?AW;kSk8kkIlBd#|h- zhV~&soBv}%e%Xwjw(-sUZ5cwBf2%}1$NqdZX1S%I(Ff+AK>(+y{#3T64YE~phGA(z zD(S)8k*b|y%$ZPk6bvKD5Z0e7qFWE3pJW5d64_+fF%duhELim54@Sh{bW0fXBEYbo z0Kr?;DWXSr0l3wR`$&13f@h4=1S33?(9IDyn&QwDGCm(1BC(TR`mI*fmYnUQd|T4L zaH)i!bz18j-O(oxnw8}6qhUY+H!YS@!A`<r5fe~q7^m{7* zh!|En?)Wiym(o1iX9eJnQ}V|aT?@((^D=NvqrGt?V_qz+F;Mh_sun_}a>y1@MV=-r z4qzx45k>bTiTPzlXUXDLbg&Fh=H2}Jxm{EXa>WxQ5(m!9kMJD}0_lueUmEX_XVn&c zR!F+@L%Z$9D?vHBREbYY&55p;;m#KpsNEH{>{3c{D&8IDnp|ms4(GTgRGO3*6u2L( zM`ggONB{sd`eYK;9oeR+_j)2R^^UH8U0fe?&I@`7`e05 z{NKYFsXPrm{9_eXzTVrBFe`) z3Y7!8bfaHqpTN_r1o)w_b-!Tp>xAN^o|*`kE9Ao2!B$JfZgW=iuBFhTeRv_lN|x%> z2#^(1y_&3Jcd6o+wgIJQMzz7s6+Hc-hd~exE;-v zRLP~OoNS_xcA*E~B-u+tsl*hGD*>(AA@kui(r{C$B21mp(&pVmG~Pna(fj82WQDtB z3L8EwAaDOzw4_S{z1z2ol;%DXVq=>pbP+?IA5kcOKIKb3Beh4G&qnIP&~)$+Cg!YX z1%E2O8@V*wqMDSE`B8K`=Qb6%TL8f5No6)2rfn%whN3War=M-K*xK*LAld)jwuBZC zRL|f5+L#$NUc!1h{;419M9B0|=&r6PE;C~5GSQv+nArfLc|vungwEV5Lc_MzDDUCJ zJ^3o}suX%;emCVMT2Qm(5&?H)#H?uV!9O`5ht1LGu8SD$gnHDma@Sk9y&u|5o28%@ zFe1+Xg1YqVYD?cCii?Jnwf2pI%q-zk6SY0=Hyl`$xp`5UN7ArrAE#nPAF3f}SG0_; zt%in0(4Pi|Y)Mq^pm9`HVNo_)@TqQ;9TsgwMjVbFH8_dH=qw!4%v9El5oa~7J$v$%589vU8i5PdP-jQY zwwK5yyAY_@+jsuqix(|=G_-3~RV!Edf=fN>vovCWAe>DeYqHY(+6cXA@U#h>Otu-9 z(&O3;ktBJVzkYu_le~iJ)A;V zc(I7PsIcflJZLS^y7y5N{6M$sdab}fo6KgLOZe|ikMyDES1DC+IR1rD75!nxD-3pp5+QPKdNMxk#LGQt@ilc117gN%JasG!wAEo7uuVfGLt3oq$Gds zbiE`R;7_jTvBmP?P|dD(Q(;BGaM(v*C*lIMErQJ*fkiy^*|wKOPF1(`@KmS!F@N`s z(UD>$;z^Crsao3%9obxNsq~*ceH!`nC1od3d?1V0Plz?=M-Os1%3^VRmoQ}?2&c)j zJa<%n|C}jHGu>6HztrGIiS;*5YIjyQz`soMtmh~HhMUh_BTG>&wDO`X5+GMT1uMqX zaPuq8E}eW7*Z_9Jlges@cL`~)hyP|gnJmNN=i6j@OMob9Nwrq_rzq3&`^BY#iRLV3 zk#86JwtI7&@fuJ+l-zyiv zFqvnsJ~B`g44eY0Gww*KI!D=y5rG_34%G=SBij71Y}Ne4^{SKWco@9zStEZTR!&_X z8RU9qL{f6Ns#;%%rNg( z+{Qc)nw28kYlU%eG|IqT1>utPg=L7zkU*g1Prck=+{z@kFKF8efBbS%9cGT^Xw#^% zZRZ-RnBV7YEDBNjgP;vgYgRCsHQ*@_=v?(ga)(!}p7urKr!dCm+LWS;vsPzboXJms zhmq(^T8F$!@(Z_VD$B<-Wz6w8RUIYcIXsJDE3&jz(COH|3a^t%9`OxXs?Pj0z)nExZbKS2 zev6b+A}}zDG&gux1Z(U(X)_qGDuAb$>i(0+Jd~UW;S(3;fpjUCh3u$A$px22Su;x^ zpGWv)OLoNEFWr`^Y}-mM`5eH{g#nFjkRbfQ$JI@6Vgq)6!QHX7?UD1j-z^@YoVdVP zP$7BFsGZp7GmD08-nr7bVk42!=3A=5>PW`olS3&{Xx6GvS28d2bkWA7DJYyh%4-xO zJn1hZMzea-ueI&qaCPz^(1)oWdPCsnNZ$oCfrtn~gF=3V;y3B-bJC!(Eo0%=sCQ6C zHgb1w09ldd3isJNe8`D<1Xw~sRZc?U-w)9tN9TC~DMGSeMTv%uHCpL$F_B|=Evl69 z%3^tKabs1<7Le-rvkV@|IKi^&21Kw+p<$$PTJZ0z4T%k4#V(d}npo@z|+WTHWeaAu@y2qY|be;W!Q*N;1$94BXB0(%Z@=<4Dh= zK*NlCpw`aG!c|Q0PUWq&<=;mJCuvQ6$ZIvrUc4AL#{YgYp_r#(E%`KatUTEcI-cl;RxIq-{;y}_K?u{6Ffm|ayg!?w4DCT8DW6tEmG?A z4e-(~dRMdbA%L_JT{QNK{2Bm0s^6xdc}c&!>vLy&(DcI76VRK~wc3g*dKdDx10fjl zR)emRBEPwl1GA}xlbI#6w}Ug}tp@-=P{iBW)ZEU}9b{%{4R#bFzv${C2Z1ew$aT1s zSe2Y5EN#HDzOI%UzRH^BzINt(7UUwrNP^z{5CIOB?xrAb2YW|1es3Z2f8_E*uKzw} zAqV|K;%+BIuB)U9l5ldh1aUHRGP5#Cd4oOK$%THMf-Vv~+Z*_^%=?%>RSm*~8WTA9F0sSuE`>9U!D`kWtzG z+mJGHN~-_C@fQMXu!HkIToADT8>Ktg>OaByZ@T^M`Ny39stAPrKji*5>VLQWA7TiV zk`lk9lex!V_2eXl$p4PdZ{cJPw&4HglFO2ho7;@ToQac*gOiEVlADu>*V3GiiH*mM zjhEGwosYwk=f8-Ob98ezbu_p9D+(f<84QtQWp2j9$;)fT#9?7>!NkeO#>r&HVad+K zYr(<8VZq97ZpHIoMX0!fAyH{+|6jHGE6M^Q%94wlosFG?or#On)C{5$A1f0tCqx@= zR!a*m9xHQmPE)>rw6QSfmv(Y>Fon1i>|knb$>Qv2{ZGeV!1={g<%G!DnOXnyiK@M+ zyA^~%h+Glu=;8gJ9hzVVOAU9^zi6^?^Kx@=aq@C-^Kh{7akKp=k+!9)8zd6{%4B0@ z=J=P~zi#1&7y|*;^lzL(1pLDRv4vm4)zZ}6$yL+I$zF*3uS%f5od1M3Nbp}ykp;U! zD181#{J+J#hNa8D?*5em_TYcIK%jrZmfzI;UxT=rdRkiiGZ2LDUtQ)lrjFK@ko^9) zfcp36;Qy1eIIPSpc)0kOEI2GInK;cjc$iFi__!dRx8miq;N{@qH8cC~(%qb_+`UX) zEyb)MmO`w71kgX$fN1}LlJ37#d)Zk2#fg=jgNc=!iH%j0m5rZ^gP)6=ft8(~m6e?3 z-wbB?o7Mkru^`L;3lqVA2>weMfbjk68Kk^GsujzBl&gQk>@SS}7u&!2;{W0Z5YhiT z$o~l6|Ap&+;rbsT@INB{-{|^Zxc)~7{EvwLH@g0RgA3_DH$0Y(kX4WuWIIy;G^v7Y zh2YE-WF-ID6agM2x`H5Ah|aQlZU6u>_TMiMkdp`5-2&j<<&>o058)8saq(6}-8w*y z5&&|NVw&D7zj}PK3@r1x{R`{<)YsH+2q<`ED2xAriQbaLTIleP4o2p`K&I?I0fi-+ z9%x~I1iE85KXXPShoXe=B86fgARs7W;X&ieiGz;M-RIWjd^c^4r>|et4Ab?yPV-i# zssqnYLr+hXbr!1+1*Zc7PhUzYsCkl5WT{LN^~-sArqN(;z|+_c2FAOrB3Cj5sFjr!c52}t4aP}J#zFav zSimL#B0V;`@V&{8+Kcy2-688;UTIR~AUu%RJ{dkrNoy;vgoK3No7HeEF0cC$27V+N zva~28WSD_OVu1=u5dg36Ev@I-iWHt59NEmW^53q=iG6Ve1!M!}rIsK8rwu_O0Z$aT zunu8{G+F{7iUs$>@6PvK*P|G`I~ZU+`)1rXFtnjV79p-Aevfm!sHi-8783pp;spl!eMn{`G`*y+QV7*o;Yk zlldqs1p4M8`tZB;u%ovI_${GpW{|QHg?lT|gTe zit-Y@bv~SKite^9VI#9*+iRnJ<-;oKk8|!EgnUf+2(=laD)gB%Sz*L5oloo9@qYEd zi1pXCZ^%D}PXJ;L*^IFOSHYg!)QBPw$Lf>KeMEE!lttIJ}b^XH;U zA6fNdusQL@9u2aRSdM!{<(pNovP|hpy@VlG^&dK1&x?(OPxW7Fwl1C?PaDi%9)DYe zCRUL#e>+{vEG(v_|X;Kp2nMl9JF$!eTBk;3spK~ z)E8QEDJ5n)Q;I2p1AwG?Yo8fT*f6#nIc@@3>dxe!t#&$UW9WcC7n@1{RGG(x$}#bG z7~S(R1NnW!N63C4-yV&d0%iEN72pp?Xbbv32*9AZ=?bdD>Yb-gD^NsU$VV6Gl#tyz zELcvB9ft~WZ2QA33hZaT9JwDZlqOl|p#z`{W<&i2IwuD0+J3)_9vQqYz$J0uJL{ku zh5JO30e43nLoL{;ZGZze!)tf{m8U4yuE_LyAcL)l8&wmaGW6>=6BE$Gt86L^{Rhvm z*i=GPq*O{VtQ2keG@MKfO7AsH$cL3Np{9nGfPIMdW5Ux#U*8c0a}tSA>?nyJit+1p z=6EQ>m8*mpiqPvO{=+;eCINB&3cEa=>G-MJ*KNw{Qhc}{t?w|9S>F*|TuW?CGm$7k zwZ# zg0S$|*s%tWEZ!R>NWe#>6Lr|%FHZ+*V_id+ybME%GpP6VcSBl4;YsoI4Zh!j3cm02 z9fGB*sobDD6=Rxr>mf?ybD+VDZjw~uNnV2#0%cUw?PqHVCNA5Wc0f-crch-Xbd|;k za9=Qc&mt9!1Sb?0$&ZGIP4*Q(GBUE!`)Vie_bbxhDt3E&i!r-^5{-ho^SvPuZRzx> zLemu3oY~kRVY+tnLrnA7)YDX*b}=?xvpN+hqL0jIF1nx7qWFeJ}-_hl&aFeJ-?6|&i}HrJA|e>@Kx*-;b* z%noKW8=wWgqI^9+cVsmDqS>}#OlATptv9Q@hyC4JS;wn+|kLOa$TdA9o`zs$bqOxGC5OK6HzyA z*1Uk6CB;d`lnMj?Nf=u_aEBlki zHB3|DriESvS4OFE3=iAlrbay`rg8bAo}S2f^REh->{$9so&rmAJQjzJIgjXO+^@en ze)H|GZpR`InUB8eg#nt`z~Io%7#uXcbjjcn5xx+xUZ=j+>8I>s+5B~2xP&+q z8C@K7Z1=mAEz!q=Vvj9FCiYIvP+H{?RJUWn>SXS2o(*qk(uXk_O&2DVuUkVg*y&U` z^a%I?2Xv4!A^@34vcFh_7Z3bA+o~asjesq%zZmT6yV4#B>bgmdb6G5b9cOl-jc1zf zi~n(n74QBo*LvjL%SDi}lmVH^)1Nc%UrnQ&(|+5p!nY!q z42s>o=<1AZIT6pUTBhT$B0h)f@qK-s%c-1@)c9l+^*327MhlgjVZ4mm=;#nrb6(0( ze6#Rav=(#hMo_BV8VL-Q7PICwHLD{n!K{p2X9e-C+a3kC+uoVz)=`5PlD{{j{!SB5 zcNaQ~Ttct+yHePuw6N4hzQED?9zvNuZ`cvzz$>DM@^deG+cScL=_u1ze^yVwjXih^ z6<33Hf4Q}t)!U!*x{s@y+vn|QBXXDD+F-#$SuLX)NJX6|In5Ks;0%D?K_1QzJ}Aw> z;N{x7!6tCaO*q`dY!QL&ibX=dg0phC%X9cU;>u@yq6E>pkeCbqLxGy^dU4sy%mr5u z0hdRSpzyDSKZDOn{YeKjXg)7p9J_B3zBkcdgEMq#;8!3JS|WI$zob4gY|)@s@p}2Qb(6=b_TqUG zWIe2fRfL0LW&%lJ_i2u;QRzy)6vhsy>c+8QNS4UK+}mCd7_`~|r#`uu99rgHwug`z zEOU#zUIe`#TYC$bt(690g?!Yd5>>0x@6qGD!XHCTbkdnkv z3(oZxz|cupy4?!JVq+H365`ZBmo`94xqvt2QcF!`HCS_WIe#3REMTKba8b+zqF9qY z;|ju~Lp?(#um~N~6lgCcV1 zCB$%-SYf**}YVN8`t<#>BEC~2F9RHCEL$u=phVnB1X z_>(7q3KaP_bInV4J6z6?Y6}{aq1V*tKPM{eCnry_)#PU|_Q^y@{7i?%LJpV$pJ&nqB~9}5Cu z8yOQS;Dqcg4&?MeaL>Fiy8>)iD}k~D)pw+ptf6k2@5;idaJx2~MoTY9N2hrlWSiF# zlM;4sRNnZGokUjH-}n7&!G!1_e0O`J7nfW4wRIu`GBJ)rRTdW%l#w6hOfbt#FtvX= z?*-Qrw)0bw6)`=+C7btX(rWA0#&|Vc))g>xA14@?m3mpvr9g&Y+8^ zfC3TRCiupF?t2P)>=rh7duHGj<}Q$oua3sttEESVOhD|fPxQn*mdzBn)yrE)&}2Tu zHO$?z`EW5mxN~}9#Wr!Sb#clal`Cy}o+~xQuO%_qKSU@g!7cn8yPtM7T_!CMUwV6? zUW0$l8Q9_I|GC0mj-=4rvEK?UCl{0T5ab-&s_)6&zk#3X`V;0!Z~$5F4}0ytO2 zZ}0x2^Q>}lyIp2@(N2R|g@Z&*su~$$IV`WW!K3uYoa#$=wYCZ8Y|&JM)I5=mwokG^ z4xjSd%Nn1wcp!cMVBuPn9#0_0`Xe$NCJAG`nWvd2cM43j3i^f_<)VcRt)90y~6jI za%x2G2e26AdIjF82*QE|uOBODS-<4$=mp~BzI#9n%_<-&4vx6_&Uz#ZCCOydVTH{2 z=||CSVP8(!24pfth!ki2p3mS{X2E=~J3+KoCqT=PcsJFLA$)c3XwYdR?CDhyn^q=O z`Snj6i!AwQReA>{v%6~f{5B+AWG~)eL1iud1Xj~p2{3^xoeTO{Fk|Ft({p3hBYIdE z*ZJRMgR2SjTS3?lYB)38n~EboH8{^h_#1D7)?^het!hN@0SXFg>?NDtKE}$-cFgzl zT5qKBjBzw8U6+@$=73aK)esvqDvcP$%LO^v0XNs@-^S1ER!&;hm5J1d7y#iTNP&Qw zRGrmbzD=YQ-$=_=)HrY54uaco z>c3KGWbi~H=MpwrxJJ)Hd@!eJz)qGymVqi4KLt=mL4E%9uCcx=tD`>xW^@)I>Z4-Z z1Qx00wj0D6HpYnIo5`Z_UuLNG>ckmuSJ}6F(Uj!wU)~v*L+eI0H%>^hMmKa{(${`* zOTEpGzL3c@Ai6kKau0A?e$%=23n19+k{_j_;@SgoJt-W!voZ;*w6@EB9v!ogr;EaL zqiL)auSqS+xyX!p<|PiJLPmH1qap{nlIFY}7tW&&4UmODJ>m8ozPEmw73Rjg=sQy& z`%IN+yLNYl3f_YUoaPW-E@)+164`2&?>62vD-js3t|724?_1YuYsqctpa6fYbfQQb z^lIlJ)rul+aU>UR^qb83m+G63!^{`C+~_nOWulY+KBj_%h^H4c`(kV12`}G%-)1{l zLcROk30gu&5NCxo?uE-db!t!OsiWMKxjEz*?4Vlv*xQPYSqA5iix@` zug)vbybyOk-|H&F++Szb59VhYha(M{={S{Sc=WPXsw2?T5)(~dM+`B1PVlp za)#tF%Z89yE0K}O_1qaB+NiI)CyyPN=(bR$4x}C@UuqE;kd)D)#*(cO%n*E1p$F$F z;grO(RA&i{>S}Lmgw7R1{Ar@s3YWjxE#`7Kh9%>nPI~!vTOd_98mFg)r~8}#+2(7M z3#0@>b{ld{13G@w7Wwc{fL#5a=N97oS>1gePUIkHd#3 zs6~2qF5@f`38YdQx}2Mmn)!$T(5G5$+rzOak^n)Pt1v3A#jC`WKTR?5ju%)mTm_G7 zmf!ALCnB7UH~A*M*$&rO8>t7NsNNh%Yf=|7$J2b+vleUn(ME;*J^UGAXN-&Qx>UiH zflclg{y5%Em!~US<4#Y0Dqxm}=TpHmBv66)j7aunDgr~CBgi9EF#uvn@pnN zdlWRX_w{f8HSPPLpkwumcnUVed^9z%;O=Xe+RLoc5ELV9&38q@l<3$m;z&=7$<4`y z3V2ep~_K^SP#l4IGcKL(_9r>?<+X% zt9PtJO6wes_pGaTScA~O4iw2Dx)7M($Sl+LH~63=VX@3$ybr0yAN?VjNTB04we(_H ztGy_{Re2>{{tGiLCW3*i|I_y-78Ibo-WLHPG$F`#-3va1o$-0P(EKtbYUZuKAvKVc zfG89nMQmv;?|17vZZ68-2*zA|hj)-A^v}Rq*jPhj0ERlQ$w8Ya!?}{w5C90mPn}uJ zH((fU)RaE!YwUwh%`y`1cjj(5c)Suc~hh`(AIlxV61vSBKUVeSDu2EKK;s#0HX*c3xPF)aJ4&vUz%fMg3$3VO4eS=btngLJDJJ zZ3VVmKaQ5fZb8L~TV3aF+LG_ds5&<$u7r-+?#F4~WzN0VWC}5!g|Xq@U%|^%!ZIcR zTG={4!!qWn=&vHgJ`NSK^O46VynDe?=jgAUtgwY-8FfFQKyJu9d$K+dx&^Ax)Pf2! z8Tzv~WEOGi-WQ5Y|K!6WA=DIfqqLr;kpc{KI2Tv^s4qcKZJ5xZHZFk8ag|#qZ z5d*u_yHWaVQh6rgL;y4xMVSylnv6iGV533>qU4N2!b90WMj7*Q@wpXgRT5q8hT(74 zbC#`=ea3&jr{sN)kcCl)YEnmmK7BalY_+m4cYyj7A=N*v0tTwLK>oRM+-DshJDxPp zrpwvjy-vKlyNge%gEwDYR~NDFNP|)g2<#($L0Xp$8weg5k#4jWkE$I5Z>>i|a+V8B97EY;z z$7_wR*V!1YvFcqgIx~YIUab1eUm@}9$(0Dc$4Dhhkqq*Sgm;0AbMqSIQVm;d2?hd; z2`8bNhK9yU+=099>mG@Fz~Kkyx8IYJUI@kWJu_UKq1EGkheBM?-xn9b;=qa$Lz9eg z40y$f8TOg%gRsS=(D@}XCr4Oo+w6kp=9V^GKa29!v&6JQ1IQ7Ac<=)eMvk;ZSo4ai z7{=Dk0|ivbf&y2`%!%#WY<-8C5W=%t+R?eB3|GaPv9*x37>Qbs9Mo z4h)cjg2Kk$p7A2og`Y8KXZibhijmKF$^uMuuTRDS76|tg3R=yFvD~pSIp+BrYqP7F z8Pv{R#MrnnRPefElyca6cAf6WM0k;PE>0@QK@y14^zgI!&xc?5&tm%&==hN#i?*;6 z&3vOk?2F}@nPl7AFmXzmIm%0}qgU-(SOfr?0CGD6SsR(stBjILAh^x}vOMl#h5hsvA9V0YVS1`Bc^f-k{n{?)rbEykSMM2ng0CDIO+a!v`=x&cQWLzWC!p; z_)|A!c})!xTo}-(;T?|DzPqs*m2N7fsl9#eEft%dvm?l_xi(|Vog4@4|LN;21ET7p zy-y5XLyvR}jf5a2UDDm%-QC@Sl+q<4-GYF0cb9ZG(p|oX=idAE{Rj+a_BpfnivRkp zHFSeE=1~|D{YJDA*>e@sP(|Q0>%?->^LeY4&`akPdHGn{+S@X956D!_m+|;&>FZYv zJox_fWc48TMgRV?F{1I_vX5?&rN1ydH#nL}(Zzg(VLeg6G{(SpU4WdR9&)y!K2(`) zuKnk)hgYkC`Me-k#NnaMtl5s~V4|9Kg_a96vpglO5axTNh$+GZ{wdr?Zo89r_isLG zyu(XjW_}(RQlbYHO_v-;OmW)S+U{s)Vw0bIaN-W|J=@bBWWDih2C9y}H*0)5Yxuan z2Clo;4|^AW{_gJe$7YgtYvH1lje0EZ*@6FA)s9eK&-Hk=?YU%0;Otr*gTkKd9u5})3*XJ4-6dbn@pJU4WapMQ1J2c2gz zmHi2EL)j-)TE!h^ljBdPY@k&FgBEVgoJ+#K0td4J^EFYzM zZp_?^h%T9&wIKO5x<2<)T{y8eIrxkC@B)Q|{*o zs2mNv+f?$^6SLwY<>3b4|Z&d&w(Pc*k1T;E@eTb8f0Hf`<6y$!Lw870`%AaGZA z7q;@I5J~5E)Hu=fZS6lg+_I@v=s{Coz4T5&7geFg~gPBG&>K{-SM}l z14GkAio?HN=wo7Z16`8H19@xcPLJO9_*!9eWxlPvpqj@gBPNE=m7-!4W9Ak85vC&E zPTrYJmyFV573OT5X>n@Wi<>u^VStrZQa`#uFK@f#HkUhI#N5~GOk&5CS>FAJ{X%4Q z-p^`1`;BxEu%9)`>bErlN;>##YIcA4TMs63+Wx70i}Lj5V-P0s2m8N<{;hnw*Ezhd zv<~-!e@SPLuGxsu?uh7BkAu147O>tOCr5%p@*5=XEyW^rg<=&+J|F? zp|@X#BsCGMWIhAqv3d282F9-hy@tdm!n_%&Eq*u!4ycp_bnHcgIJBDeY1 zy_mP*V@BcBO;83p=ga+}k3{9!?yxOe(YNXi%xnch#fSkK(VRVYF0R6opI_2QCb>Cn zRA4RouqnF5cWO+inPgWMx$wUrThNl#Jv{jIw*JVqYUV#ZGF*F@OsZPQLd5*%p-*kG zm)7|1kYV|*m#GGT>IRYs)3CUf}Wzzu9JRBF8{f1^+9aZwL5lF#_*4g%Fe)$ z6Hn~q8;AO)q?S9hpH-r$;94t=O`UtH~Tao9po(oPXbP-khv_nk|$40B)|Ye{UdT(y(;iFC-Z5PG|_1 zgJi|Gh@dtEsT$uC)6`5Y=W#f0Q&(3v9?RmzuUL};2!+^_swwSupH?#9*WStGFm_94 zo%zN!srWt29EB$AyiMQ)+e9i|yQcd>8vT1Z1vOjpz&Q9`XE7aAGhK_dS^^N5% z4t?S8G-rcsYUC~ZZeIJ~D?U?GfQq%zF)?YcXop$-Ql@V|;LBybS>k#^YIEu2Ha7c7 zrxsg|qM3C|lQnJ_g7uk9mMZV4s=dt&+GyJUxgeL@aW!^sZmxE9IL)Mv1iJc|n27B{ z5rxL@N&&DNOH0_%;%0QhRvCTEO|GA~F2Aq&BaW?u{IFT6L_r>6?~fyFX0(YqCVw~L zdpuvy7q#AOM3VQVyw#2QgfDKQ|&w)@=>KS@PJEHdxyM1~dP z`n;wl-h-UM8o1{Gp&B?8;QvIwB^*zs_WUvY_O88`qEjpqv(w@y&2vEiXVN#xG%krU z9%UL8D%sbBp@U{IgvG=DS_}*10O9i)qj}VyL@sH)gqCsB|UNA8*7<}Fi*VZDDsiUG4_PqelO6}{@ z0O(UMPVN}pJnEGf|KbEH6eLA~WKhNZVwP(AJV@%L#sJ+1HsNXOt-=9j8Fus*$BrnT zglJG04Ac07?3b3*Ed^yf8r^_a6)7iR@zP?O_FS2V73~moL@|CdN=+IIFR!%5PT=K8 zEUB{R5f<76zyo((1_olj-HJ7wn8K!~2 zT%T)Lihk6Y42?Vj5uFVgvuAft&wlgxFU4&=GE8;IMbpv12eUblNNo+ezxY(OK0|D} z@6GLNcNMxS=5=wMz3-YWs;C%WdPw2weU>fYtD!M$dfdi=9%??EVm)**Pb9^phXRDx zcnu$6l}Kb&(2hMm{LdpDgC&1?`bQoXoT)SEqdiQ-Y^xnAQmCrBjU4gFj+Mey3s%f}m9D)T6>oEKWrDwp~orZKqyQ zfC;``jta{AJI3U1US@p7lZ=mz6~>crNkE3R!M#WSpsAx+=z;Szfk3#i_U{%`;?((W zyok+j=ts$^zNRKpiu$e{N(fBqummO!rSRY1?KMe=P3#oSE0uTa#p$3x??;6+4E30` zt+QN3n=#bbrW%)u4hSp`Qvx_|7d*VpFv`)ZS0vMY;CVUhz=!1Lgk@W%P+3{2aNc@# z?-$dG4De*;Vr-Bv_RqaW^*Y8}nM@AyU9e<{c?q|VvANe)hrX(K_=*;TG+k0qxb`(H zm1+@WB~=InqU5GT9}_ZZRW9Ye?v%&Y+bUReV_~ILG3O;J%Ywp}t*{Wy`QODit6ga> z1?x0Ulo`NKSlCr8OD8zMh`_awhd}YRTiyAxj~|JaJ^+THCITwrk-*nMW$fjpm3WOL z{sB2wl3Ke5C*ObDpWpS#xh}pX_k3-hD-b!8AOKPJFoo#_J;E4BST@}F@M+#lN=mLf z@i4w)kwdNy0i%ZF^}oOXoS8O$GZ!D={Y5q#l9l>GC9Cc>6mg%dFuMe8$h%s#6twp%kJ^0`7ZoYl_nnd!s zJH}wxnWVI|H0i{@5-foYBs!%va6$xunxlXIzdmDpvxoV>p zQY4-wARR)10sLJXfjNKU8j~aExv7knDoS!ROqeHZ@)sAA_|17H<=g1LmjIh3OpzDk z>%BdIZMRs5lF4P?)ftKesHV6;th4rf;#p(V3CDl89$JbUiykiO9QUEg-VrgBlI5Eq zE}N}2#+-jZBC~Au-|kVZ+i(el`=`%|SVIqFHP}cp-FquRN1IOy zmd0lyXYP$7mZ&!iGAr}qSsw0t*AGC!vPb~g^_TE-z%0I`Y*H9qju+f;%G+)X?&YAD zmw?$*5%tv0ux{h0hxzutY|qtbz$>b=nn7_o_xFpLk5~gb00d5sc0@d_>{=^uR3!|a zUr$U>9F89@hs_OCClB4>^2h4Of7jJ=lKfm+?kdxYv3x zk{W)qCI)QV^_*_w-R7rrfGHw4ZU5I+h%)AfrK|HW^ zwzqO$HgBrvm=QUU8MOUep?-cuIL|UH3!%ie_{qz9j`p@^fMa95GxSfLHSR0r%Dv|2 zY5`kbr(5 z@?5Bj&L#y?JnM=M;3Bw3^Hu#jsfGp@XPSe^N`ECrWD>OQvY2oz+xiy2As0rhIP?Pt zIsK~nBbhh!^j5&Vk#HmRDo?CQGxUu%?l1d0teK2Mv4sCktdj8{s;3w?i1qQ=QthcF~ffWy^a(d1(Dg0zu z3OW4g78^UYMRr`o7UMbkE;QeO2n`(r1OLh2osaaDG241v`xl_RwJky20mS%`15H%{>O4T$>XZe$Z5!?rr#8H1#6F+Z^X=H}+kAf>tJe^zmBf^CEjMB;bz zfzO&xOYP6P%KU{PxOwz3cPpm>kxLHE6=oK8yvR;|ek4VTxf*8Ayd;<-8Sl~1yMrh^ zk5^lXHTB&ANED!`Cl?npZXf-Qbl187m~7~U6!%uW(wFI1m5q%Jo9$fr-EyJ~MF-^Y z@bIkl)E^7rdrU=|4N-rq=rEgTz)2_J>DF_~cN?*RFI0n=s#@NaKoNuN(11g-39z1u z4=bhWWvD7f`p7_p)$33`eIO3SDH-q;0>m=F9GC!9px4#pM>HklC%a zym>VOIRd^H0qrfwXMbu;GgwSC4mX+Xec_WnQsdD7gu|6J*~XJR{Zm8D5P=NSu;P)q zDK5jFa4I!3IjM{sqICYa{(QUEZwWBYEw;$`**$>!((_z#2k7zeLf|}f99=V_s`M`# zPTePxAA2Vx-yQFJl^cB|boAHY`;%M%4WNt#i(d9OC-tx4Z4scqA5iAHbiRHMMbNR#5cL z^uowbSL;;7$QJbV^)cp;XwcZSj5XlXJim9U=9Dti4~_RRgGo&8hp`%t7vq6ILqUEW4D1aVa2)m zYQN97`knvQ&yO`~X+Uw5m%YZENf>-sDBhLyn3eML_}w{W9QpR*YZkN`#3>OW;Yldp z7*)yu3vArYu$@?4%_;aL@WVJsk#rqEsn39o*l_IJp)v~_(QSNnbJMkVIhb=VtBeN# z^=d^={3kdQ?p$Fg^Sr6j{mMW^vU^#Q)Gf$jWr;NyGM^Lc7P{#v?Qm7Lj6aew^0G2{@h!}#?a+ag}Fv38$s z6+LspY_G!`i(piAdLt;1_y7Kl8%IrS9^W0$HLT?tcEMhkAk+_QKKadb8mTSfc?DSE z&(t43uP1&aQ+5yHC5OcIHF{(&;~+>UP0Y`0&K(IYV<$)i)XYCUpj=yx4blj~G}=tU zEjQN5lv zeDmM)ndgs?xOMgdpGEl`y1x}h+ zAZA|cLW9#oS{)bomUU}8oQ|$DQxU!Ih-zF7Z1B=}3oiV)YBINY%26Sb7{dTDI znHN8eJy=zMBqCtZi+YV81OGj)y;n~CI*Q5(L9Z?EfXGy=(BehGh;5|L{?liaO2Q+rqCl$Y zvi#JWFC8bHtq|$xw?+JVe*sTh(j2}6N7mW+giIj*cz#p%xlU6sV! zhzsnz1-RcZ>Y#`e@6tsvmQ*IzIO)jlZzvZ_jn33BK!ed1kY9XR`T4;VnaxAhw0Gu% z$FWj<6uta(ab=~3UhbFu2fXJE7wLd={K&Y@O#*-I`K9x7#hq)P>3o`Xn;DKIibD1h zPBJ}B&7y}tYryfu!*XT#yO6C6f;8y3@%`t2OH_-T0D$)^GY6FLRghIW5=U6`gWl|{ zcbAQ%=~uNVj1UBTJBDv0=l6odw(6W85x4opmEI^&fP?0b)XkpR(L(`CKby05IW9!b z+IDBsxj1J*0^4U)qxIUdQ}!z@qtLonL{AydeAsp;G_dVAnPltJ6shjr_@9@+_6S+l z);c?Aqxmp_-)W>!kLSw+w-VxgrPpOjRVQ1Kgt{*s1f9_#uDql~*?hyb+#YD5dL$ME zGP2SPUCyslWfc|b_U!ee7N44_Tq=Zf$2H%nl+`E!DG>UTz#%XujUoX_p>sW9(}|qFzW{JBWPV#4_%DTOt~#48e{DK{0rXS7 zfM`o@OWtgWgRWcK0b2K^Q?_uOZaMxE>4 z#1#i_g>1gBH^L+4?Lc*NB%Pz^SW?*3lv-T52asEwr{%fvTU|rJMu(Ipg#1j*V5+pAeR6y-BL#((Mi@Le-kXSIxD4bkgE|0mNdd+&zuB!s|>(B4shy>XL zL#6jmPvdW0hw9<>M$*3@=6TS*(*e{qvVgmd{E@lQuTOQz?Q7(@2DF+Nt43@_t1aH< z-dY+&Qrdx(xwLpcifI@;U|h1z)LD*~n>g%=beUG>ork6#sp43YxD#Tpo)xzBmp?RF3^LANiL{|y-kRYh(8}N#Tf_mLbyx-GCgi| z+<^-Y5xXqKv{iq)Xb6x?199Y_Q~;o-mZh%VJociETPFWHk{gfAduQTCKvXvZ7beNXlig$qFkUHdwK2!7VBlD(aPT_bK=#p@ zsno!kg4gG7L;~1vHBZXdAR`tW(ZCJN5D*a&v9Ys%D;}TUy97D_Z7ibHXmPfo)Hla> zOnjE+4HiOe>!0?I$hx9z^YRC=qG*M_1mzdU5P69sJ4FZ=O?Nspm;z;4Jc&W7Ei^uWjQ<0zBs_V|UMPpo_N7R9 zQW6bd9=4G@PX|+oNOJS!nn}v(OEU3XJxTXg#eop~&TJfe{{{n!!veLE>7zu|ysd`S zYXfMI6;|0Yy1^#+I?W_%5gP>+a^SJE$4EvLLa(NvMq6_wAzfEeQ=@#@J%$21=CptP z&>{n=GePi?d`9plUlXr3Icc3fPSpyoA@hiXeXG}IzVjAEU zOY|nbf+>QTM>1#vDLk1y3c_QnJ5?aOZW)tpv$({bZ<1eS>{k=_+$allu+|#AOW#y< zZ_>zHJB=LQQ@y_3LYE6({rGr&f3NkIq(8CC5_sQezdlQ!Rs-aayv0)ubzMm6sMe3^ zCx4G7y_gE2vRRpBL*PXyfQ$S<0Qn#cP$Q)*(HrRKhdTD&tsnKvFv<;uWJ$#1Uv8{YK^c z4WJE>{q?^?1hyEL_H>yEdb?5p^|#a`^k{FX`sDJ{m&CO_!cM&k`$0NoJp7Wj9_BVc zxEZ|;z=fLu-&Ml^qQoU4+o~qnq;m2gRCkAFS0*|?3W%B2tDKJ(5f`A1z(pp0Ydm%b z=&_`hH%*iP4w1H8O>~5ZmwEh8lQ%k9138}jx8Egovp4Mi+zm?@B48Im`Xp#@K0pF~ z&VLnhmlIVI^9CI{kg9G>3h0{a_2+H6Gsc;NTBRSg6_xR}2C-;L$1yN4dLF1jT6%Z< z(eT#L#%w{L-BQ=z8Y^$q0>0lF3=;XJ0VKdqeA&gohjgtoKi=>y;6(;dwmg=f(fD5; zh4L3wKB1h_w_UYY_RZO^1&wL(>au~62gtnesD&~L2Ro=Y;Rx5B<>Pauoj!YS5V82j zWhcGCs_EMAd52anW@)xjl;zcM1Y;o3f^_OZ8kFmzfp_0)!N+8;4YNazKdpqL6twg^BrIa}7F_AaE;s6|M(^18p4 znTTjc|HSP`Hn<`NMMH}Eu~G0Fu44*EU&$4ROa{>&xeg>C4MtJ}T*A~19rVsW|=OVMF>;>buR2|qdb zk2Z!1(YLQ7@{@0}t&L6IG(&RmrnjJ=TV`I}oS~p}F|X@xaq=I^Z>H-!a4%PDl5Mp9#4WsC91!ue-jujeZ zjHZG4(7e`D2-U{b9rx&j5rof&PvxdqV=D+K@tjnWba$DkT zvpczv^q%nmo(*3+SVuK_k`YMF&2bHn1$CGSX2I(R(PXi)@fLJKO1%C>jY#>OJVw1Z zdGu1&;=&C0nIky3fKCG8d9QKyhFbEwvIjGp2Zg_2ZYdI-HEE(0^yFoTrqge?6OV}Q zqk>#1iZp{|7~Jr?=_JD|$7um-z@TrwSRnhD*=WUwQA3W&Sm{w}U#YfyEpi7dt0*@0 z#Dwt{tZW6%8|PFUs2gUzWz~5y^CL)%1(OC3sfcAAYG-4WbwLN@{c@eELYfN=gE7JAN{Z&`MWcI z7vW0>y;(6VWixzFi~_g=AaL7!GNOUJc>^nA4D6FF`1oznqUo>d*WrZJJgc7QqEG?B zES;^Qp5JkugcEb24OEsPW^RFKQrCF$!LZ4+RpjVlY%Yu8znj99Z!5~mx`Dz)IcEHy z^@23;z}7z(>eRjX z`1+b-8;$U7@?h>|dm3odwt0)56n{1>lw%d!6NnTK0Z}ITAud|5hUBnYZ2~O;Xv{#* z_`UhNX*3%Y3#Ul`RijP|%<=3Gnm4ym7z(H$xR}T*F!Y!~IaW7NMz0%?*lH8jiXC}A z+}F5?jD^nuhwWof`pVj-{8GLZWA|>#y7MFZYOW9pQJiGj=#Zob?Jrww`j0#$8q4>} zl_QS1N9c$jBk|_%2HiuG(P1!pVVPJE)A(<>c^{83m3yT;KHT*HG`2K05v)}UZ zYS49VU4&X#{UN@{q0;k64T8Ot|O1E+O;BupdY7Bz* z5!T}&l^0J_N-wM!I$FVrykxo4%TQ%?8uQ47EAI?QhMb_VT4jXPkA0bktLawk;>@_|8Niu+_}Ml8=i0|WvIk!oMw+W z#frWz0V?HQ8q$ffkTFgOl}c{dP4E`7IYqlwoZ)%lm^Ck^mNIDHohBJQTkNl_6n(jm z3}<6jFzBPenS27l`;acNgs{833puQv101#_y!yq95~N8Rl4=vZ_1F_f>}2xk#Jmn! z=^dO`!fyZ}QG999V+06Oyk3Pj%De#kt+cJ%OF#A-V+l49=PeLT* zW-|!i6jov9B_$HX5x+!o;7Peo`-1^=-%WV4DIRD^fIWRMsE9*@Yyhhw9zrjbK!xL_ zcxAajK#@&`wA~wZIA3jo%r^V{qxd4Q-foNQ-jtl5UfvsNU2NUv=rNL4Aum2`w!MLg3T4xo(KE1Hl+83+ zjm<_+UnEi3yaVHX zA}5t*3l{5VpQ2TpA2X$9+M<}YMvO(kF@mmX!kJ(i=R=Vl1h96VHfp*5lz*YhFH2@x z9v0#4qIi@-K0}9g-PxluZ@7{EXFx1SvE5}0z5!!XTb+JLF<0VMRSJKT@uNel7OF%m zo!}`?jw|OL+Kuh5`N1j!=F&Y-HtudW&0&{~vhz(m1-Y=L;fJ2`&>^(;@i*`oeR8=! zL8?+1ho?h)#Uyz6 z^M1@%fpx)fwxe<(=X@gZY1fqgESO%H-x({BX54U~t#{gj_s(+XOH+`T;OL))vX1h< zyGyn}rW}!(1T#VT$dn_{*t-aJ&J_KAMRX~!pXfPx!eCP@2nQrAaRk!-03Y+?9sM|r zN=V}VO|yC?@+32?Oj4K=PyL;Cn@E`{I-866my8UOhV)V{l1$!Z{&qwPC=a#MoHvm| zDq6bD6C@=V4~OzjT#7!}lS2O45eGjGrBrqr~ST1hBvK5?!(&60{E1@7XF8OB2*xh!#&E3-hU2A@==NI5ljgwCVP2vbmf zR(xN~Z>~7)!zaV6hLWBzbY_T(BBR1di_9JjQvLjW_*892V8RT2;saSRhai!gNyv6)t0!$U(JJcj}=Z7pX75e@qeO}u>rP>nZ3@!gYy&3dP zN$cyaNHS;5rsCAo|P|M}e%P6^LuMZxPr%Qbb4X zn2d2Sx(ILbeTzSoc4R%_u@2;2n1lkR$x1w#oWURs3i}1}Hu&4ZOq&skX&d%T#t{na zZhbH=Y)vXq+Txl3w=M_K zkBY+NFqSiJe@(7y$FuY^%==Vcr}#(CgO>{v&`b4G-`R+mJ=9B2P!8P-rOXNhed+Xc`-F0C0JUMKTQc_s^5LUuq$nIWQS=(<>7#98OB#*KwiIOU4 z-!np~WMR$Pl$9AHSdk9Ma|qGJx>Zb}a^FSWFg&Ti$QD+F-#ghZ$X1gmC^zwjbda-a z@^~c!BvE6dqXXyN{DrT3Vc$yxvBdpuXHRP^g-bAcXdDFwyW7f3DM?gPLEX?vkjQsA zFc~#Bn~+7=S>%6Y^u!0`98YjE*C67()bSVsZ_W{rhL~3cqm_Mp_}!g{t}L9m;lpS~>&G(iE~`5*xOXqGPiKbBD?6i4y=aUz4v*0BRMB z(Ky*sonnQO0(l;5bHknv3uToKf}(;;_Jb%hQa)M$;aUA;f>!~d3L)fNbdPz#pieH8 zYoh5dJ0OX!qO5^R@>EQfP~dI4|4372_+a9mNG9#&E7yp5&s;HNKm!|%umSP z?1!USJFf2lUuBouo>z&1%^ni4r|tFJHj-i#i;jR20(Qr?=97tPN}!Zb&f~!w4Ua`3 zmXipmOGGpsS%Hy0MZ+wy7`F<6VOn^O(?Pf>Z|QNLIC5uOie+24%1hs583Ht8-Pe+7 zss1o9IcqD1sgnNay%T37K|g;7w!JU0(+xEQ{Rg2iogIvF0t}_ihBBxA7jJT2Z!A%@ zZMPvlA%D&TO3z%{wrq+X!Mvx)v?NY8zfHGCB^+Khbn508$7LZH_E2^6xdoX_P_(Xh zK9#oXXH(-^h|w?NjyE!oPcBV*S}k7ez!(>`hL0NMTEPI$$Up=(c+zDtp5cyVzlGdY z2U&AK)YRmH3QLl_`I2~euivT`@-plel^dqVLwzxS9$J=kVQ91>XxZJS{%A_FtL*9I zZ~=Nnt`JF?J*Z1*--#8FCv)-X0x6u&75;DpV_kwQG$)`@ zzyUcVZ2>W3nJ$EWO+q6kio@pKoLWemy`8xr#|oj02D8`)&8ga#>)B9%%HXL*KBN|LT zea9P;&&{1cxHS$jp423q8Tz2P_k}|<QMFNCGnk%z6)Pq(ic65g0hyNC+|?}g1USV ztS54}|Dehl^YCuH1vVu}83*6`3V&*7`k=A+tEPR_n9nSr?+EMaW-^v{06>q?TAP0{ z9&%C;=XEDlapPjkOCh82Sy~^Sy&y{=-cvIND~@rMN8U_ z4d;Pz4e_@IrwpNDVC)3s<(&Lq&GG#C|IVc<77x1vBQE;`--*5;Yojutq|lOZiwX{9 zNZu_x`a25G&kEIUp#6z*X-kL$$NcLtGxaOCc579+cz7nWGCN+Y_zx1TCKnoDFpSQx z@%GIarfT0?hY^sMr~^75QhsY4@3DkU5^_-tVc(4T=6O5hUfYT-qRW>CFwUhZFrhhY z5o4bTXP)2w`U`SF1tYde}jl~pSXtj?{!hB;z z`R+Zu@AxYr_5667`sDfwQUl1g2`x31INSb7WdGhE571LZsEtD-g^glVlJCQqz8U07 zyxV0{jSH7G_x!_JNzQo0MLM=VlcD;8hk*7ctonP&*{{W4;2m4e5d|+2Ok>mz4?Jy=h-%wbrtTy^Tk2zyd7+8)tTnn#3zT*jNsyvKQ4 z(mC_iE1v?tpn%r$)2BiTntgag(aze+OkgBk%gR~-4*o#hMSl1|VW}0fqAd;MPRHc; zk2Myf8FjpUy+NZEVjn*u0c!q0mm<8#Ci^~F(C|@22imky^Cf-BAim8f_|hpjIrX^F zgF$GvtL=^pUpE^Y#I2e~j!jKCTBfANq!(4sb@aX0cH+mF7i|DzKuAUlXb~)69^OH^!y1{-~X_j`@3t07)$yAArm`d@j{u{W2mN_a(z>mcfiVJbZAgn8p#8lY|QdcLa;~Bzzz05w@ZBe3n z>=Tr|)TjK<5XXU01v{W!4l>ZQ0fby+Rls%G-Kb-LYTfvsU-rb5?Qk6q#Stq?7|J;8 zo1{IBmT;ob$52_EA6AZ5Q5Y)$zsODG8(urW6MO;JEUL0J?;E8tx{VZ!`kW@{xE)Zss)wtPwqGR!(TI zMc9u;DH%02074E=Ng-&|s|@))4-}l0Dpn1Dh-Q$O2_BM-$`7bV$jJMUR5O+q2qeGl zP@>@f3+GCLR<;tdfOg1cDGg7w$7|#aa876@`6b~!@c~!Vq5(VApM}58>BZB?%hs5U zc~>tT=M~vWrp4l4zZ^dh5LrPIy+L=pVyeEW?fs>5sC7Y&eNaO3SNRFulKQ z$@8X+&9b(@G3vlYMCdsCx7Zwr|3055W{VCzhLWg3S{#ZZnq(}G=LaLgWGj-^r&2vb zmo^kxQ&(4ahEi8oBjbT1dD|6XKKJCDwEXz|#b?%7@^kx}E3JWx%G!=x zdinZ_f|kachVmwtX2Kq-5t-E+wwZhWUZ&JKXq ziT{Sr+xAMHs`Iyom|BbPh6pbP+t=F7PkuMHINjnwJQRXQEiO}KRP`A@)Grj3X({`; ztQ-e?oOy~n3Ib7J+6d;B%^y8qZOt9kyP6kqarEs!dlz7N;x!felP19Qbk^&<1yt46 zc_fj%1-rh_Kh;zcZ5QWZQG`jBU7FO2rWb50_PiGz8m^vsH=l+rTc7WCf4_`D%xmA= z?`+K&xgEa$_k$#!#D%}1Gl{APDy?wi4>d;Xcj;0|U2M=N z;Q9?c&gB8^RlO!6Fd;BuzOn32#`ru45#Mv$!pM<@fq}5FK`^dm7+!={}0puJzxL; literal 0 HcmV?d00001 From 23edc1b048421e8d292b3ac23e9fe7cc768f6de5 Mon Sep 17 00:00:00 2001 From: Michele21 <72189006+Michele21@users.noreply.github.com> Date: Fri, 21 Jul 2023 17:42:51 +0200 Subject: [PATCH 57/77] adding svg logo to the documentation --- docs/src/assets/logo.svg | 562 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 562 insertions(+) create mode 100644 docs/src/assets/logo.svg diff --git a/docs/src/assets/logo.svg b/docs/src/assets/logo.svg new file mode 100644 index 0000000..e57b8cc --- /dev/null +++ b/docs/src/assets/logo.svg @@ -0,0 +1,562 @@ + + + + From bc300777d8f485cdac152c0f144786ad8c6a2a03 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 25 Jul 2023 03:59:16 +0200 Subject: [PATCH 58/77] Fix evaluation. Bump --- Project.toml | 2 +- src/models/evaluation.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 136119f..4445567 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SoleModels" uuid = "4249d9c7-3290-4ddd-961c-e1d3ec2467f8" authors = ["Michele GHIOTTI", "Giovanni PAGLIARINI", "Eduard I. STAN"] -version = "0.2.2" +version = "0.2.3" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" diff --git a/src/models/evaluation.jl b/src/models/evaluation.jl index cc2c9b8..abf0a2e 100644 --- a/src/models/evaluation.jl +++ b/src/models/evaluation.jl @@ -15,7 +15,7 @@ Performance metrics can be computed when the `info` structure of the model: function readmetrics(m::LeafModel{L}; digits = 2) where {L<:Label} merge(if haskey(info(m), :supporting_labels) && haskey(info(m), :supporting_predictions) _gts = info(m).supporting_labels - _preds = _gts = info(m).supporting_predictions + _preds = info(m).supporting_predictions if L <: CLabel (; ninstances = length(_gts), confidence = round(accuracy(_gts, _preds); digits = digits)) elseif L <: RLabel From fddd0027c19d5b505e1508c31452a7fb6fd79422 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Tue, 25 Jul 2023 04:00:35 +0200 Subject: [PATCH 59/77] fix version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 4445567..136119f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SoleModels" uuid = "4249d9c7-3290-4ddd-961c-e1d3ec2467f8" authors = ["Michele GHIOTTI", "Giovanni PAGLIARINI", "Eduard I. STAN"] -version = "0.2.3" +version = "0.2.2" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" From 78ed521f1534f0c7195e705df2c83d0588d4f2bf Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sat, 29 Jul 2023 12:26:56 -0400 Subject: [PATCH 60/77] negation->dual --- src/logisets/conditions.jl | 2 +- src/logisets/scalar/conditions.jl | 10 ++++++---- src/logisets/scalar/onestep-memoset.jl | 8 ++++---- src/logisets/scalar/random.jl | 2 +- src/logisets/scalar/templated-formulas.jl | 15 +++++++++------ src/logisets/templated-formulas.jl | 14 +++++++++----- 6 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/logisets/conditions.jl b/src/logisets/conditions.jl index 17949e9..9feca36 100644 --- a/src/logisets/conditions.jl +++ b/src/logisets/conditions.jl @@ -1,7 +1,7 @@ using SoleLogics: AbstractAlphabet using Random -import SoleLogics: negation, propositions +import SoleLogics: hasdual, dual, propositions import Base: isequal, hash, in, isfinite, length diff --git a/src/logisets/scalar/conditions.jl b/src/logisets/scalar/conditions.jl index 4c38cff..e6e4c61 100644 --- a/src/logisets/scalar/conditions.jl +++ b/src/logisets/scalar/conditions.jl @@ -19,7 +19,7 @@ for representing the infinite set of conditions that arise with a free threshold See also [`AbstractCondition`](@ref), -[`negation`](@ref), +[`dual`](@ref), [`ScalarCondition`](@ref). """ struct ScalarMetaCondition{FT<:AbstractFeature,O<:TestOperator} <: AbstractCondition{FT} @@ -39,7 +39,8 @@ end feature(m::ScalarMetaCondition) = m.feature test_operator(m::ScalarMetaCondition) = m.test_operator -negation(m::ScalarMetaCondition) = ScalarMetaCondition(feature(m), inverse_test_operator(test_operator(m))) +hasdual(::ScalarMetaCondition) = true +dual(m::ScalarMetaCondition) = ScalarMetaCondition(feature(m), inverse_test_operator(test_operator(m))) syntaxstring(m::ScalarMetaCondition; kwargs...) = "$(_syntaxstring_metacondition(m; kwargs...)) ⍰" @@ -111,7 +112,7 @@ In this case, the feature a [`UnivariateMin`](@ref) object. See also [`AbstractCondition`](@ref), -[`negation`](@ref), +[`dual`](@ref), [`ScalarMetaCondition`](@ref). """ struct ScalarCondition{U,FT,M<:ScalarMetaCondition{FT}} <: AbstractCondition{FT} @@ -152,7 +153,8 @@ threshold(c::ScalarCondition) = c.threshold feature(c::ScalarCondition) = feature(metacond(c)) test_operator(c::ScalarCondition) = test_operator(metacond(c)) -negation(c::ScalarCondition) = ScalarCondition(negation(metacond(c)), threshold(c)) +hasdual(::ScalarCondition) = true +dual(c::ScalarCondition) = ScalarCondition(dual(metacond(c)), threshold(c)) function checkcondition(c::ScalarCondition, args...; kwargs...) apply_test_operator(test_operator(c), featvalue(feature(c), args...; kwargs...), threshold(c)) diff --git a/src/logisets/scalar/onestep-memoset.jl b/src/logisets/scalar/onestep-memoset.jl index f91d3ae..6c93e0b 100644 --- a/src/logisets/scalar/onestep-memoset.jl +++ b/src/logisets/scalar/onestep-memoset.jl @@ -380,9 +380,9 @@ function featchannel_onestep_aggregation( # Find metacond with same aggregator i_metacond = findfirst((m)->feature(m) == feature(metacond) && existential_aggregator(test_operator(m)) == existential_aggregator(test_operator(metacond)), metaconditions(Xm)) if isnothing(i_metacond) - i_neg_metacond = _findfirst(isequal(negation(metacond)), metaconditions(Xm)) + i_neg_metacond = _findfirst(isequal(dual(metacond)), metaconditions(Xm)) error("Could not find metacondition $(metacond) in memoset of type $(typeof(Xm)) " * - "($(!isnothing(i_neg_metacond) ? "but negation was found " * + "($(!isnothing(i_neg_metacond) ? "but dual was found " * "with i_metacond = $(i_neg_metacond)!" : "")).") end end @@ -433,9 +433,9 @@ function check( if isnothing(i_metacond) i_metacond = findfirst((m)->feature(m) == feature(metacond) && existential_aggregator(test_operator(m)) == existential_aggregator(test_operator(metacond)), metaconditions(Xm)) if isnothing(i_metacond) - i_neg_metacond = _findfirst(isequal(negation(metacond)), metaconditions(Xm)) + i_neg_metacond = _findfirst(isequal(dual(metacond)), metaconditions(Xm)) error("Could not find metacondition $(metacond) in memoset of type $(typeof(Xm)) " * - "($(!isnothing(i_neg_metacond) ? "but negation was found " * + "($(!isnothing(i_neg_metacond) ? "but dual was found " * "with i_metacond = $(i_neg_metacond)!" : "")).") end end diff --git a/src/logisets/scalar/random.jl b/src/logisets/scalar/random.jl index fd945ca..eb33364 100644 --- a/src/logisets/scalar/random.jl +++ b/src/logisets/scalar/random.jl @@ -50,7 +50,7 @@ function Base.rand( filtered_featconds = begin if !isnothing(metaconditions) - filtered_featconds = filter(mc_thresholds->first(mc_thresholds) in [metaconditions..., negation.(metaconditions)...], grouped_featconditions) + filtered_featconds = filter(mc_thresholds->first(mc_thresholds) in [metaconditions..., dual.(metaconditions)...], grouped_featconditions) @assert length(filtered_featconds) == length(metaconditions) "" * "There is at least one metacondition passed that is not among the " * "possible ones\n metaconditions: $(metaconditions)\n filtered " * diff --git a/src/logisets/scalar/templated-formulas.jl b/src/logisets/scalar/templated-formulas.jl index 1fbb4cb..0656790 100644 --- a/src/logisets/scalar/templated-formulas.jl +++ b/src/logisets/scalar/templated-formulas.jl @@ -20,8 +20,9 @@ test_operator(f::ScalarPropositionFormula) = test_operator(proposition(f)) threshold(f::ScalarPropositionFormula) = threshold(proposition(f)) tree(f::ScalarPropositionFormula) = SyntaxTree(f.p) -negation(f::ScalarPropositionFormula{U}) where {U} = - ScalarPropositionFormula{U}(negation(p)) +hasdual(f::ScalarPropositionFormula) = true +dual(f::ScalarPropositionFormula{U}) where {U} = + ScalarPropositionFormula{U}(dual(p)) ############################################################################################ @@ -102,15 +103,17 @@ end tree(f::ScalarUniversalFormula) = BoxRelationalOperator(f.relation)(Proposition(f.p)) -function negation(formula::ScalarExistentialFormula{U}) where {U} +hasdual(f::ScalarExistentialFormula) = true +function dual(formula::ScalarExistentialFormula{U}) where {U} ScalarUniversalFormula{U}( relation(formula), - negation(proposition(formula)) + dual(proposition(formula)) ) end -function negation(formula::ScalarUniversalFormula{U}) where {U} +hasdual(f::ScalarUniversalFormula) = true +function dual(formula::ScalarUniversalFormula{U}) where {U} ScalarExistentialFormula{U}( relation(formula), - negation(proposition(formula)) + dual(proposition(formula)) ) end diff --git a/src/logisets/templated-formulas.jl b/src/logisets/templated-formulas.jl index 7a4fcca..4843574 100644 --- a/src/logisets/templated-formulas.jl +++ b/src/logisets/templated-formulas.jl @@ -1,5 +1,5 @@ import Base: show -import SoleLogics: tree, negation +import SoleLogics: tree, dual """ Abstract type simple formulas of given templates. @@ -11,25 +11,29 @@ Templated formula for ⊤, which always checks top. """ struct TopFormula <: AbstractTemplatedFormula end tree(::TopFormula) = SyntaxTree(⊤) -negation(::TopFormula) = BotFormula() +hasdual(::TopFormula) = true +dual(::TopFormula) = BotFormula() """ Templated formula for ⊥, which always checks bottom. """ struct BotFormula <: AbstractTemplatedFormula end tree(::BotFormula) = SyntaxTree(⊥) -negation(::BotFormula) = TopFormula() +hasdual(::BotFormula) = true +dual(::BotFormula) = TopFormula() """ Templated formula for ⟨R⟩⊤. """ struct ExistentialTopFormula{R<:AbstractRelation} <: AbstractTemplatedFormula end tree(::ExistentialTopFormula{R}) where {R<:AbstractRelation} = DiamondRelationalOperator{R}(⊤) -negation(::ExistentialTopFormula{R}) where {R<:AbstractRelation} = UniversalBotFormula{R}() +hasdual(::ExistentialTopFormula) = true +dual(::ExistentialTopFormula{R}) where {R<:AbstractRelation} = UniversalBotFormula{R}() """ Templated formula for [R]⊥. """ struct UniversalBotFormula{R<:AbstractRelation} <: AbstractTemplatedFormula end tree(::UniversalBotFormula{R}) where {R<:AbstractRelation} = BoxRelationalOperator{R}(⊥) -negation(::UniversalBotFormula{R}) where {R<:AbstractRelation} = ExistentialTopFormula{R}() +hasdual(::UniversalBotFormula) = true +dual(::UniversalBotFormula{R}) where {R<:AbstractRelation} = ExistentialTopFormula{R}() From 971ba42e6ad269ad12a593f65490ad1158efae98 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 30 Jul 2023 16:26:08 -0400 Subject: [PATCH 61/77] Remove old code --- src/SoleModels.jl | 2 + .../active-featured-dataset.jl | 137 ------ src/conditional-data/main.jl | 70 --- .../multi-frame-conditional-datasets.jl | 94 ---- .../datasets/dimensional-featured-dataset.jl | 271 ------------ .../datasets/dimensional-fwds.jl | 366 --------------- .../datasets/featured-dataset.jl | 380 ---------------- .../datasets/generic-supporting-datasets.jl | 81 ---- src/dimensional-datasets/datasets/main.jl | 73 --- .../dimensional-supports.jl | 415 ------------------ .../generic-supports.jl | 108 ----- .../main.jl | 282 ------------ .../datasets/passive-dimensional-dataset.jl | 94 ---- .../datasets/supported-featured-dataset.jl | 192 -------- src/dimensional-datasets/main.jl | 66 --- 15 files changed, 2 insertions(+), 2629 deletions(-) delete mode 100644 src/conditional-data/active-featured-dataset.jl delete mode 100644 src/conditional-data/main.jl delete mode 100644 src/conditional-data/multi-frame-conditional-datasets.jl delete mode 100644 src/dimensional-datasets/datasets/dimensional-featured-dataset.jl delete mode 100644 src/dimensional-datasets/datasets/dimensional-fwds.jl delete mode 100644 src/dimensional-datasets/datasets/featured-dataset.jl delete mode 100644 src/dimensional-datasets/datasets/generic-supporting-datasets.jl delete mode 100644 src/dimensional-datasets/datasets/main.jl delete mode 100644 src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl delete mode 100644 src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl delete mode 100644 src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl delete mode 100644 src/dimensional-datasets/datasets/passive-dimensional-dataset.jl delete mode 100644 src/dimensional-datasets/datasets/supported-featured-dataset.jl delete mode 100644 src/dimensional-datasets/main.jl diff --git a/src/SoleModels.jl b/src/SoleModels.jl index 13931e4..b537d7b 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -95,6 +95,8 @@ export posconsequent, negconsequent export DecisionList export rulebase, defaultconsequent +export apply, apply! + export DecisionTree export root diff --git a/src/conditional-data/active-featured-dataset.jl b/src/conditional-data/active-featured-dataset.jl deleted file mode 100644 index 6e82680..0000000 --- a/src/conditional-data/active-featured-dataset.jl +++ /dev/null @@ -1,137 +0,0 @@ -# Active datasets comprehend structures for representing relation sets, features, enumerating worlds, -# etc. While learning a model can be done only with active modal datasets, testing a model -# can be done with both active and passive modal datasets. -# -abstract type AbstractActiveFeaturedDataset{ - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - FT<:AbstractFeature{V} -} <: AbstractActiveConditionalDataset{W,AbstractCondition,Bool,FR} end - -featvaltype(::Type{<:AbstractActiveFeaturedDataset{V}}) where {V} = V -featvaltype(d::AbstractActiveFeaturedDataset) = featvaltype(typeof(d)) - -featuretype(::Type{<:AbstractActiveFeaturedDataset{V,W,FR,FT}}) where {V,W,FR,FT} = FT -featuretype(d::AbstractActiveFeaturedDataset) = featuretype(typeof(d)) - -function grouped_featsaggrsnops(X::AbstractActiveFeaturedDataset) - return error("Please, provide method grouped_featsaggrsnops(::$(typeof(X))).") -end - -function features(X::AbstractActiveFeaturedDataset) - return error("Please, provide method features(::$(typeof(X))).") -end - -function grouped_metaconditions(X::AbstractActiveFeaturedDataset) - grouped_featsnops = grouped_featsaggrsnops2grouped_featsnops(grouped_featsaggrsnops(X)) - [begin - (feat,[FeatMetaCondition(feat,op) for op in ops]) - end for (feat,ops) in zip(features(X),grouped_featsnops)] -end - -function alphabet(X::AbstractActiveFeaturedDataset) - conds = vcat([begin - thresholds = unique([ - X[i_instance, w, feature] - for i_instance in 1:ninstances(X) - for w in allworlds(X, i_instance) - ]) - [(mc, thresholds) for mc in metaconditions] - end for (feature, metaconditions) in grouped_metaconditions(X)]...) - C = FeatCondition{featvaltype(X),FeatMetaCondition{featuretype(X)}} - BoundedExplicitConditionalAlphabet{C}(collect(conds)) -end - - -# Base.length(X::AbstractActiveFeaturedDataset) = ninstances(X) -# Base.iterate(X::AbstractActiveFeaturedDataset, state=1) = state > ninstances(X) ? nothing : (get_instance(X, state), state+1) - -function find_feature_id(X::AbstractActiveFeaturedDataset, feature::AbstractFeature) - id = findfirst(x->(Base.isequal(x, feature)), features(X)) - if isnothing(id) - error("Could not find feature $(feature) in AbstractActiveFeaturedDataset of type $(typeof(X)).") - end - id -end -function find_relation_id(X::AbstractActiveFeaturedDataset, relation::AbstractRelation) - id = findfirst(x->x==relation, relations(X)) - if isnothing(id) - error("Could not find relation $(relation) in AbstractActiveFeaturedDataset of type $(typeof(X)).") - end - id -end - - -# By default an active modal dataset cannot be minified -isminifiable(::AbstractActiveFeaturedDataset) = false - -# Convenience functions -function grouped_featsnops2grouped_featsaggrsnops( - grouped_featsnops::AbstractVector{<:AbstractVector{<:TestOperator}} -)::AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}} - grouped_featsaggrsnops = Dict{<:Aggregator,<:AbstractVector{<:TestOperator}}[] - for (i_feature, test_operators) in enumerate(grouped_featsnops) - aggrsnops = Dict{Aggregator,AbstractVector{<:TestOperator}}() - for test_operator in test_operators - aggregator = existential_aggregator(test_operator) - if (!haskey(aggrsnops, aggregator)) - aggrsnops[aggregator] = TestOperator[] - end - push!(aggrsnops[aggregator], test_operator) - end - push!(grouped_featsaggrsnops, aggrsnops) - end - grouped_featsaggrsnops -end - -function grouped_featsaggrsnops2grouped_featsnops( - grouped_featsaggrsnops::AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}} -)::AbstractVector{<:AbstractVector{<:TestOperator}} - grouped_featsnops = [begin - vcat(values(grouped_featsaggrsnops)...) - end for grouped_featsaggrsnops in grouped_featsaggrsnops] - grouped_featsnops -end - -function features_grouped_featsaggrsnops2featsnaggrs_grouped_featsnaggrs(features, grouped_featsaggrsnops) - featsnaggrs = Tuple{<:AbstractFeature,<:Aggregator}[] - grouped_featsnaggrs = AbstractVector{Tuple{<:Integer,<:Aggregator}}[] - i_featsnaggr = 1 - for (feat,aggrsnops) in zip(features, grouped_featsaggrsnops) - aggrs = [] - for aggr in keys(aggrsnops) - push!(featsnaggrs, (feat,aggr)) - push!(aggrs, (i_featsnaggr,aggr)) - i_featsnaggr += 1 - end - push!(grouped_featsnaggrs, aggrs) - end - featsnaggrs, grouped_featsnaggrs -end - -function features_grouped_featsaggrsnops2featsnaggrs(features, grouped_featsaggrsnops) - featsnaggrs = Tuple{<:AbstractFeature,<:Aggregator}[] - i_featsnaggr = 1 - for (feat,aggrsnops) in zip(features, grouped_featsaggrsnops) - for aggr in keys(aggrsnops) - push!(featsnaggrs, (feat,aggr)) - i_featsnaggr += 1 - end - end - featsnaggrs -end - -function features_grouped_featsaggrsnops2grouped_featsnaggrs(features, grouped_featsaggrsnops) - grouped_featsnaggrs = AbstractVector{Tuple{<:Integer,<:Aggregator}}[] - i_featsnaggr = 1 - for (feat,aggrsnops) in zip(features, grouped_featsaggrsnops) - aggrs = [] - for aggr in keys(aggrsnops) - push!(aggrs, (i_featsnaggr,aggr)) - i_featsnaggr += 1 - end - push!(grouped_featsnaggrs, aggrs) - end - grouped_featsnaggrs -end diff --git a/src/conditional-data/main.jl b/src/conditional-data/main.jl deleted file mode 100644 index c1aafa5..0000000 --- a/src/conditional-data/main.jl +++ /dev/null @@ -1,70 +0,0 @@ -using SoleLogics: OneWorld, Interval, Interval2D -using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame -using SoleLogics: X, Y, Z -using SoleLogics: AbstractWorld, IdentityRel -import SoleLogics: syntaxstring -import SoleLogics: frame - -import SoleData: nvariables -import SoleData: ninstances -import SoleData: nmodalities, hasnans, instances -# import SoleData: frame # TODO - -# Minification interface for lossless data compression -include("minify.jl") - -# Scalar features to be computed on worlds of dataset instances -include("features.jl") - -export inverse_test_operator, dual_test_operator, - apply_test_operator, - TestOperator - -# Test operators to be used for comparing features and threshold values -include("test-operators.jl") - -# Scalar conditions on the features, to be wrapped in Proposition's -include("conditions.jl") - -# Alphabets of conditions on the features, to be used in logical datasets -include("conditional-alphabets.jl") - -export MixedFeature, CanonicalFeature, canonical_geq, canonical_leq - -export canonical_geq_95, canonical_geq_90, canonical_geq_85, canonical_geq_80, canonical_geq_75, canonical_geq_70, canonical_geq_60, - canonical_leq_95, canonical_leq_90, canonical_leq_85, canonical_leq_80, canonical_leq_75, canonical_leq_70, canonical_leq_60 - -# Types for representing common associations between features and operators -include("canonical-conditions.jl") # TODO fix - -const MixedFeature = Union{AbstractFeature,CanonicalFeature,Function,Tuple{TestOperator,Function},Tuple{TestOperator,AbstractFeature}} - -# Representative accessibles, for optimized model checking -include("representatives.jl") - -# Datasets where the instances are Kripke models with conditional alphabets -include("conditional-datasets.jl") - -include("active-featured-dataset.jl") - -export nmodalities, modalities, frame, - display_structure, - MultiFrameConditionalDataset, - worldtypes - -# # -# # TODO figure out which convert function works best: -# convert(::Type{<:MultiFrameConditionalDataset{T}}, X::MD) where {T,MD<:AbstractConditionalDataset{T}} = MultiFrameConditionalDataset{MD}([X]) -# convert(::Type{<:MultiFrameConditionalDataset}, X::AbstractConditionalDataset) = MultiFrameConditionalDataset([X]) - -# Multi-frame version of conditional datasets, for representing multimodal datasets -include("multi-frame-conditional-datasets.jl") # TODO define interface - -const ActiveMultiFrameConditionalDataset{T} = MultiFrameConditionalDataset{<:AbstractActiveFeaturedDataset{<:T}} - -# TODO decide how to name this. -getframe = frame - -# include("featured-datasets.jl") TODO? - -include("random.jl") diff --git a/src/conditional-data/multi-frame-conditional-datasets.jl b/src/conditional-data/multi-frame-conditional-datasets.jl deleted file mode 100644 index c0b6087..0000000 --- a/src/conditional-data/multi-frame-conditional-datasets.jl +++ /dev/null @@ -1,94 +0,0 @@ -""" - struct MultiFrameConditionalDataset{MD<:AbstractConditionalDataset} - modalities :: Vector{<:MD} - end - -A multi-frame conditional dataset. This structure is useful for representing -multimodal datasets in logical terms. - -See also -[`AbstractConditionalDataset`](@ref), -[`minify`](@ref). -""" -struct MultiFrameConditionalDataset{MD<:AbstractConditionalDataset} - - modalities :: Vector{<:MD} - - function MultiFrameConditionalDataset{MD}(X::MultiFrameConditionalDataset{MD}) where {MD<:AbstractConditionalDataset} - MultiFrameConditionalDataset{MD}(X.modalities) - end - function MultiFrameConditionalDataset{MD}(Xs::AbstractVector) where {MD<:AbstractConditionalDataset} - Xs = collect(Xs) - @assert length(Xs) > 0 && length(unique(ninstances.(Xs))) == 1 "Can't create an empty MultiFrameConditionalDataset or with mismatching number of samples (nmodalities: $(length(Xs)), frame_sizes: $(ninstances.(Xs)))." - new{MD}(Xs) - end - function MultiFrameConditionalDataset{MD}() where {MD<:AbstractConditionalDataset} - new{MD}(MD[]) - end - function MultiFrameConditionalDataset{MD}(X::MD) where {MD<:AbstractConditionalDataset} - MultiFrameConditionalDataset{MD}(MD[X]) - end - function MultiFrameConditionalDataset(Xs::AbstractVector{<:MD}) where {MD<:AbstractConditionalDataset} - MultiFrameConditionalDataset{MD}(Xs) - end - function MultiFrameConditionalDataset(X::MD) where {MD<:AbstractConditionalDataset} - MultiFrameConditionalDataset{MD}(X) - end -end - -modalities(X::MultiFrameConditionalDataset) = X.modalities - -Base.iterate(X::MultiFrameConditionalDataset, state=1) = state > length(X) ? nothing : (get_instance(X, state), state+1) -Base.length(X::MultiFrameConditionalDataset) = ninstances(X) -Base.push!(X::MultiFrameConditionalDataset, f::AbstractConditionalDataset) = push!(modalities(X), f) - -Base.size(X::MultiFrameConditionalDataset) = map(size, modalities(X)) - -frame(X::MultiFrameConditionalDataset, i_frame::Integer) = nmodalities(X) > 0 ? modalities(X)[i_frame] : error("MultiFrameConditionalDataset has no frame!") -nmodalities(X::MultiFrameConditionalDataset) = length(modalities(X)) -ninstances(X::MultiFrameConditionalDataset) = ninstances(frame(X, 1)) - -# max_channel_size(X::MultiFrameConditionalDataset) = map(max_channel_size, modalities(X)) # TODO: figure if this is useless or not. Note: channel_size doesn't make sense at this point. -nfeatures(X::MultiFrameConditionalDataset) = map(nfeatures, modalities(X)) # Note: used for safety checks in fit_tree.jl -# nrelations(X::MultiFrameConditionalDataset) = map(nrelations, modalities(X)) # TODO: figure if this is useless or not -nfeatures(X::MultiFrameConditionalDataset, i_frame::Integer) = nfeatures(frame(X, i_frame)) -nrelations(X::MultiFrameConditionalDataset, i_frame::Integer) = nrelations(frame(X, i_frame)) -worldtype(X::MultiFrameConditionalDataset, i_frame::Integer) = worldtype(frame(X, i_frame)) -worldtypes(X::MultiFrameConditionalDataset) = Vector{Type{<:AbstractWorld}}(worldtype.(modalities(X))) - -get_instance(X::MultiFrameConditionalDataset, i_frame::Integer, idx_i::Integer, args...) = get_instance(frame(X, i_frame), idx_i, args...) -# get_instance(X::MultiFrameConditionalDataset, idx_i::Integer, args...) = get_instance(frame(X, i), idx_i, args...) # TODO should slice across the modalities! - -instances(X::MultiFrameConditionalDataset{MD}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {MD<:AbstractConditionalDataset} = - MultiFrameConditionalDataset{MD}(Vector{MD}(map(frame->instances(frame, inds, return_view), modalities(X)))) - -function display_structure(Xs::MultiFrameConditionalDataset; indent_str = "") - out = "$(typeof(Xs))" # * "\t\t\t$(Base.summarysize(Xs) / 1024 / 1024 |> x->round(x, digits=2)) MBs" - for (i_frame, X) in enumerate(modalities(Xs)) - if i_frame == nmodalities(Xs) - out *= "\n$(indent_str)└ " - else - out *= "\n$(indent_str)├ " - end - out *= "[$(i_frame)] " - # \t\t\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t(worldtype: $(worldtype(X)))" - out *= display_structure(X; indent_str = indent_str * (i_frame == nmodalities(Xs) ? " " : "│ ")) * "\n" - end - out -end - -hasnans(Xs::MultiFrameConditionalDataset) = any(hasnans.(modalities(Xs))) - -isminifiable(::MultiFrameConditionalDataset) = true - -function minify(Xs::MultiFrameConditionalDataset) - if !any(map(isminifiable, modalities(Xs))) - if !all(map(isminifiable, modalities(Xs))) - @error "Cannot perform minification with modalities of types $(typeof.(modalities(Xs))). Please use a minifiable format (e.g., SupportedFeaturedDataset)." - else - @warn "Cannot perform minification on some of the modalities provided. Please use a minifiable format (e.g., SupportedFeaturedDataset) ($(typeof.(modalities(Xs))) were used instead)." - end - end - Xs, backmap = zip([!isminifiable(X) ? minify(X) : (X, identity) for X in modalities(Xs)]...) - Xs, backmap -end diff --git a/src/dimensional-datasets/datasets/dimensional-featured-dataset.jl b/src/dimensional-datasets/datasets/dimensional-featured-dataset.jl deleted file mode 100644 index 02a960f..0000000 --- a/src/dimensional-datasets/datasets/dimensional-featured-dataset.jl +++ /dev/null @@ -1,271 +0,0 @@ - -############################################################################################ -# Interpreted modal dataset -############################################################################################ -# -# A modal dataset can be instantiated in *implicit* form, from a dimensional domain, and a few -# objects inducing an interpretation on the domain; mainly, an ontology (determining worlds and -# relations), and structures for interpreting features onto the domain. -# -############################################################################################ - -struct DimensionalFeaturedDataset{ - V<:Number, - N, - W<:AbstractWorld, - D<:PassiveDimensionalDataset{N,W}, - FT<:AbstractFeature{V}, - G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, - G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, -} <: AbstractActiveFeaturedDataset{V,W,FullDimensionalFrame{N,W,Bool},FT} - - # Core data (a dimensional domain) - domain :: D - - # Worlds & Relations - ontology :: Ontology{W} # Union{Nothing,} - - # Features - features :: Vector{FT} - - # Test operators associated with each feature, grouped by their respective aggregator - # Note: currently, cannot specify the full type (probably due to @computed) - grouped_featsaggrsnops :: G1 - - # Features and Aggregators - grouped_featsnaggrs :: G2 - - # Initial world(s) - initialworld :: Union{Nothing,W,AbstractWorldSet{<:W}} - - ######################################################################################## - - function DimensionalFeaturedDataset{V,N,W}( - domain::PassiveDimensionalDataset{N}, - ontology::Ontology{W}, - features::AbstractVector{<:AbstractFeature}, - grouped_featsaggrsnops::AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}; - allow_no_instances = false, - initialworld = nothing, - ) where {V,N,W<:AbstractWorld} - ty = "DimensionalFeaturedDataset{$(V),$(N),$(W)}" - features = collect(features) - FT = Union{typeof.(features)...} - features = Vector{FT}(features) - @assert allow_no_instances || ninstances(domain) > 0 "" * - "Can't instantiate $(ty) with no instance. (domain's type $(typeof(domain)))" - @assert length(features) == length(grouped_featsaggrsnops) "" * - "Can't instantiate $(ty) with mismatching length(features) and" * - " length(grouped_featsaggrsnops):" * - " $(length(features)) != $(length(grouped_featsaggrsnops))" - @assert length(grouped_featsaggrsnops) > 0 && - sum(length.(grouped_featsaggrsnops)) > 0 && - sum(vcat([[length(test_ops) for test_ops in aggrs] for aggrs in grouped_featsaggrsnops]...)) > 0 "" * - "Can't instantiate $(ty) with no test operator: $(grouped_featsaggrsnops)" - grouped_featsnaggrs = features_grouped_featsaggrsnops2grouped_featsnaggrs(features, grouped_featsaggrsnops) - check_initialworld(DimensionalFeaturedDataset, initialworld, W) - new{ - V, - N, - W, - typeof(domain), - FT, - typeof(grouped_featsaggrsnops), - typeof(grouped_featsnaggrs), - }( - domain, - ontology, - features, - grouped_featsaggrsnops, - grouped_featsnaggrs, - initialworld, - ) - end - - ######################################################################################## - - function DimensionalFeaturedDataset{V,N,W}( - domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, - ontology :: Ontology{W}, - features :: AbstractVector{<:AbstractFeature}, - grouped_featsnops :: AbstractVector; - kwargs..., - ) where {V,N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{N,W}(domain) : domain) - grouped_featsaggrsnops = grouped_featsnops2grouped_featsaggrsnops(grouped_featsnops) - DimensionalFeaturedDataset{V,N,W}(domain, ontology, features, grouped_featsaggrsnops; kwargs...) - end - - function DimensionalFeaturedDataset{V,N,W}( - domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, - ontology :: Ontology{W}, - mixed_features :: AbstractVector; - kwargs..., - ) where {V,N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{N,W}(domain) : domain) - - @assert all(isa.(mixed_features, MixedFeature)) "Unknown feature encountered!" * - " $(filter(f->!isa(f, MixedFeature), mixed_features)), " * - " $(typeof.(filter(f->!isa(f, MixedFeature), mixed_features)))" - - mixed_features = Vector{MixedFeature}(mixed_features) - - _features, featsnops = begin - _features = AbstractFeature[] - featsnops = Vector{<:TestOperator}[] - - # readymade features - cnv_feat(cf::AbstractFeature) = ([≥, ≤], cf) - cnv_feat(cf::Tuple{TestOperator,AbstractFeature}) = ([cf[1]], cf[2]) - # single-attribute features - cnv_feat(cf::Any) = cf - cnv_feat(cf::CanonicalFeature) = cf - cnv_feat(cf::Function) = ([≥, ≤], cf) - cnv_feat(cf::Tuple{TestOperator,Function}) = ([cf[1]], cf[2]) - - mixed_features = cnv_feat.(mixed_features) - - readymade_cfs = filter(x-> - # isa(x, Tuple{<:AbstractVector{<:TestOperator},AbstractFeature}), - isa(x, Tuple{AbstractVector,AbstractFeature}), - mixed_features, - ) - attribute_specific_cfs = filter(x-> - isa(x, CanonicalFeature) || - # isa(x, Tuple{<:AbstractVector{<:TestOperator},Function}) || - (isa(x, Tuple{AbstractVector,Function}) && !isa(x, Tuple{AbstractVector,AbstractFeature})), - mixed_features, - ) - - @assert length(readymade_cfs) + length(attribute_specific_cfs) == length(mixed_features) "" * - "Unexpected" * - " mixed_features. $(mixed_features)." * - " $(filter(x->(! (x in readymade_cfs) && ! (x in attribute_specific_cfs)), mixed_features))." * - " $(length(readymade_cfs)) + $(length(attribute_specific_cfs)) == $(length(mixed_features))." - - for (test_ops,cf) in readymade_cfs - push!(_features, cf) - push!(featsnops, test_ops) - end - - single_attr_feats_n_featsnops(i_attr,cf::SoleModels.CanonicalFeatureGeq) = ([≥],DimensionalDatasets.UnivariateMin{V}(i_attr)) - single_attr_feats_n_featsnops(i_attr,cf::SoleModels.CanonicalFeatureLeq) = ([≤],DimensionalDatasets.UnivariateMax{V}(i_attr)) - single_attr_feats_n_featsnops(i_attr,cf::SoleModels.CanonicalFeatureGeqSoft) = ([≥],DimensionalDatasets.UnivariateSoftMin{V}(i_attr, cf.alpha)) - single_attr_feats_n_featsnops(i_attr,cf::SoleModels.CanonicalFeatureLeqSoft) = ([≤],DimensionalDatasets.UnivariateSoftMax{V}(i_attr, cf.alpha)) - single_attr_feats_n_featsnops(i_attr,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},typeof(minimum)}) = (test_ops,DimensionalDatasets.UnivariateMin{V}(i_attr)) - single_attr_feats_n_featsnops(i_attr,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},typeof(maximum)}) = (test_ops,DimensionalDatasets.UnivariateMax{V}(i_attr)) - single_attr_feats_n_featsnops(i_attr,(test_ops,cf)::Tuple{<:AbstractVector{<:TestOperator},Function}) = (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_attr, (x)->(V(cf(x))))) - single_attr_feats_n_featsnops(i_attr,::Any) = throw_n_log("Unknown mixed_feature type: $(cf), $(typeof(cf))") - - for i_attr in 1:nvariables(domain) - for (test_ops,cf) in map((cf)->single_attr_feats_n_featsnops(i_attr,cf),attribute_specific_cfs) - push!(featsnops, test_ops) - push!(_features, cf) - end - end - _features, featsnops - end - DimensionalFeaturedDataset{V,N,worldtype(ontology)}(domain, ontology, _features, featsnops; kwargs...) - end - - ######################################################################################## - - function DimensionalFeaturedDataset{V,N}( - domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, - ontology :: Ontology{W}, - args...; - kwargs..., - ) where {V,N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{N,W}(domain) : domain) - DimensionalFeaturedDataset{V,N,W}(domain, ontology, args...; kwargs...) - end - - ######################################################################################## - - function DimensionalFeaturedDataset{V}( - domain :: Union{PassiveDimensionalDataset,AbstractDimensionalDataset}, - args...; - kwargs..., - ) where {V} - DimensionalFeaturedDataset{V,dimensionality(domain)}(domain, args...; kwargs...) - end - - ######################################################################################## - - function DimensionalFeaturedDataset( - domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, - ontology :: Ontology{W}, - features :: AbstractVector{<:AbstractFeature}, - args...; - kwargs..., - ) where {N,W<:AbstractWorld} - V = Union{featvaltype.(features)...} - DimensionalFeaturedDataset{V}(domain, ontology, features, args...; kwargs...) - end - - preserves_type(::Any) = false - preserves_type(::CanonicalFeature) = true - preserves_type(::typeof(minimum)) = true # TODO fix - preserves_type(::typeof(maximum)) = true # TODO fix - - function DimensionalFeaturedDataset( - domain :: Union{PassiveDimensionalDataset{N,W},AbstractDimensionalDataset}, - ontology :: Ontology{W}, - mixed_features :: AbstractVector; - kwargs..., - ) where {N,W<:AbstractWorld} - domain = (domain isa AbstractDimensionalDataset ? PassiveDimensionalDataset{dimensionality(domain),W}(domain) : domain) - @assert all((f)->(preserves_type(f)), mixed_features) "Please, specify the feature output type V upon construction, as in: DimensionalFeaturedDataset{V}(...)." # TODO highlight and improve - V = eltype(domain) - DimensionalFeaturedDataset{V}(domain, ontology, mixed_features; kwargs...) - end - -end - -domain(X::DimensionalFeaturedDataset) = X.domain -ontology(X::DimensionalFeaturedDataset) = X.ontology -features(X::DimensionalFeaturedDataset) = X.features -grouped_featsaggrsnops(X::DimensionalFeaturedDataset) = X.grouped_featsaggrsnops -grouped_featsnaggrs(X::DimensionalFeaturedDataset) = X.grouped_featsnaggrs - -function Base.getindex(X::DimensionalFeaturedDataset, args...) - domain(X)[args...]::featvaltype(X) -end - -Base.size(X::DimensionalFeaturedDataset) = Base.size(domain(X)) - -dimensionality(X::DimensionalFeaturedDataset{V,N,W}) where {V,N,W} = N -worldtype(X::DimensionalFeaturedDataset{V,N,W}) where {V,N,W} = W - -ninstances(X::DimensionalFeaturedDataset) = ninstances(domain(X)) -nvariables(X::DimensionalFeaturedDataset) = nvariables(domain(X)) - -relations(X::DimensionalFeaturedDataset) = relations(ontology(X)) -nrelations(X::DimensionalFeaturedDataset) = length(relations(X)) -nfeatures(X::DimensionalFeaturedDataset) = length(features(X)) - -channel_size(X::DimensionalFeaturedDataset, args...) = channel_size(domain(X), args...) -max_channel_size(X::DimensionalFeaturedDataset) = max_channel_size(domain(X)) - -get_instance(X::DimensionalFeaturedDataset, args...) = get_instance(domain(X), args...) - -instances(X::DimensionalFeaturedDataset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) = - DimensionalFeaturedDataset(instances(domain(X), inds, return_view), ontology(X), features(X), grouped_featsaggrsnops(X); initialworld = initialworld(X)) - -frame(X::DimensionalFeaturedDataset, i_instance) = frame(domain(X), i_instance) -initialworld(X::DimensionalFeaturedDataset) = X.initialworld -function initialworld(X::DimensionalFeaturedDataset, i_instance) - initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_instance] : initialworld(X) -end - -function display_structure(X::DimensionalFeaturedDataset; indent_str = "") - out = "$(typeof(X))\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "├ relations: \t$((length(relations(X))))\t$(relations(X))\n" - out *= indent_str * "├ domain shape\t$(Base.size(domain(X)))\n" - out *= indent_str * "├ max_channel_size\t$(max_channel_size(X))" - out *= indent_str * "└ initialworld(s)\t$(initialworld(X))" - out -end - -hasnans(X::DimensionalFeaturedDataset) = hasnans(domain(X)) - diff --git a/src/dimensional-datasets/datasets/dimensional-fwds.jl b/src/dimensional-datasets/datasets/dimensional-fwds.jl deleted file mode 100644 index a12c05f..0000000 --- a/src/dimensional-datasets/datasets/dimensional-fwds.jl +++ /dev/null @@ -1,366 +0,0 @@ -import Base: size, ndims, getindex, setindex! - -############################################################################################ -############################################################################################ -# world-specific FWD implementations -############################################################################################ -############################################################################################ - -abstract type AbstractUniformFullDimensionalFWD{T,N,W<:AbstractWorld,FR<:FullDimensionalFrame{N,W,Bool}} <: AbstractFWD{T,W,FR} end - -channel_size(fwd::AbstractUniformFullDimensionalFWD) = error("TODO add message inviting to add channel_size") -frame(fwd::AbstractUniformFullDimensionalFWD, i_instance) = FullDimensionalFrame(channel_size(fwd)) - -############################################################################################ -############################################################################################ - -struct UniformFullDimensionalFWD{ - T, - W<:AbstractWorld, - N, - D<:AbstractArray{T}, - FR<:FullDimensionalFrame{N,W,Bool} -} <: AbstractUniformFullDimensionalFWD{T,N,W,FR} - - d :: D - - function UniformFullDimensionalFWD{T,W,N,D,FR}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{T},FR<:FullDimensionalFrame{N,W,Bool}} - new{T,W,N,D,FR}(d) - end - - function UniformFullDimensionalFWD{T,W,N}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{T}} - new{T,W,N,D,FullDimensionalFrame{N,W,Bool}}(d) - end - -############################################################################################ - - function UniformFullDimensionalFWD(X::DimensionalFeaturedDataset{T,N,W}) where {T,N,W<:OneWorld} - UniformFullDimensionalFWD{T,W,N}(Array{T,2}(undef, ninstances(X), nfeatures(X))) - end - function UniformFullDimensionalFWD(X::DimensionalFeaturedDataset{T,N,W}) where {T,N,W<:Interval} - UniformFullDimensionalFWD{T,W,N}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, ninstances(X), nfeatures(X))) - end - function UniformFullDimensionalFWD(X::DimensionalFeaturedDataset{T,N,W}) where {T,N,W<:Interval2D} - UniformFullDimensionalFWD{T,W,N}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, ninstances(X), nfeatures(X))) - end - -end - -Base.size(fwd::UniformFullDimensionalFWD, args...) = size(fwd.d, args...) -Base.ndims(fwd::UniformFullDimensionalFWD, args...) = ndims(fwd.d, args...) - -ninstances(fwd::UniformFullDimensionalFWD) = size(fwd, ndims(fwd)-1) -nfeatures(fwd::UniformFullDimensionalFWD) = size(fwd, ndims(fwd)) - -############################################################################################ - -channel_size(fwd::UniformFullDimensionalFWD{T,OneWorld}) where {T} = () -channel_size(fwd::UniformFullDimensionalFWD{T,<:Interval}) where {T} = (size(fwd, 1),) -channel_size(fwd::UniformFullDimensionalFWD{T,<:Interval2D}) where {T} = (size(fwd, 1),size(fwd, 3)) - -############################################################################################ - -function capacity(fwd::UniformFullDimensionalFWD{T,OneWorld}) where {T} - prod(size(fwd)) -end -function capacity(fwd::UniformFullDimensionalFWD{T,<:Interval}) where {T} - prod([ - ninstances(fwd), - nfeatures(fwd), - div(size(fwd, 1)*(size(fwd, 2)),2), - ]) -end -function capacity(fwd::UniformFullDimensionalFWD{T,<:Interval2D}) where {T} - prod([ - ninstances(fwd), - nfeatures(fwd), - div(size(fwd, 1)*(size(fwd, 2)),2), - div(size(fwd, 3)*(size(fwd, 4)),2), - ]) -end - -############################################################################################ - -function hasnans(fwd::UniformFullDimensionalFWD{T,OneWorld}) where {T} - any(_isnan.(fwd.d)) -end -function hasnans(fwd::UniformFullDimensionalFWD{T,<:Interval}) where {T} - any([hasnans(fwd.d[x,y,:,:]) - for x in 1:size(fwd, 1) for y in (x+1):size(fwd, 2)]) -end -function hasnans(fwd::UniformFullDimensionalFWD{T,<:Interval2D}) where {T} - any([hasnans(fwd.d[xx,xy,yx,yy,:,:]) - for xx in 1:size(fwd, 1) for xy in (xx+1):size(fwd, 2) - for yx in 1:size(fwd, 3) for yy in (yx+1):size(fwd, 4)]) -end - -############################################################################################ - -function fwd_init_world_slice(fwd::UniformFullDimensionalFWD, i_instance::Integer, w::AbstractWorld) - nothing -end - -############################################################################################ - -@inline function Base.getindex( - fwd :: UniformFullDimensionalFWD{T,OneWorld}, - i_instance :: Integer, - w :: OneWorld, - i_feature :: Integer -) where {T} - fwd.d[i_instance, i_feature] -end - -@inline function Base.getindex( - fwd :: UniformFullDimensionalFWD{T,<:Interval}, - i_instance :: Integer, - w :: Interval, - i_feature :: Integer -) where {T} - fwd.d[w.x, w.y, i_instance, i_feature] -end - -@inline function Base.getindex( - fwd :: UniformFullDimensionalFWD{T,<:Interval2D}, - i_instance :: Integer, - w :: Interval2D, - i_feature :: Integer -) where {T} - fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] -end - -############################################################################################ - -@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,OneWorld}, threshold::T, w::OneWorld, i_instance::Integer, i_feature::Integer) where {T} - fwd.d[i_instance, i_feature] = threshold -end - -@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,<:Interval}, threshold::T, w::Interval, i_instance::Integer, i_feature::Integer) where {T} - fwd.d[w.x, w.y, i_instance, i_feature] = threshold -end - -@inline function Base.setindex!(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, threshold::T, w::Interval2D, i_instance::Integer, i_feature::Integer) where {T} - fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] = threshold -end - -############################################################################################ - -function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:OneWorld,N} - UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[inds,:] else fwd.d[inds,:] end) -end - -function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:Interval,N} - UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[:,:,inds,:] else fwd.d[:,:,inds,:] end) -end - -function instances(fwd::UniformFullDimensionalFWD{T,W,N}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T,W<:Interval2D,N} - UniformFullDimensionalFWD{T,W,N}(if return_view == Val(true) @view fwd.d[:,:,:,:,inds,:] else fwd.d[:,:,:,:,inds,:] end) -end - -############################################################################################ - -# TODO fix - -Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimensionalFWD{T,OneWorld}, i_feature::Integer, fwdslice::Array{T,1}) where {T} - fwd.d[:, i_feature] = fwdslice -end - -Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,OneWorld}, i_instance::Integer, i_feature::Integer) where {T} = - fwd.d[i_instance, i_feature] -const OneWorldFeaturedChannel{T} = T -fwd_channel_interpret_world(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc - -Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimensionalFWD{T,<:Interval}, i_feature::Integer, fwdslice::Array{T,3}) where {T} - fwd.d[:, :, :, i_feature] = fwdslice -end -Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,<:Interval}, i_instance::Integer, i_feature::Integer) where {T} = - @views fwd.d[:,:,i_instance, i_feature] -const IntervalFeaturedChannel{T} = AbstractArray{T,2} -fwd_channel_interpret_world(fwc::IntervalFeaturedChannel{T}, w::Interval) where {T} = - fwc[w.x, w.y] - - -Base.@propagate_inbounds @inline function fwdslice_set(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, i_feature::Integer, fwdslice::Array{T,5}) where {T} - fwd.d[:, :, :, :, :, i_feature] = fwdslice -end -Base.@propagate_inbounds @inline fwdread_channel(fwd::UniformFullDimensionalFWD{T,<:Interval2D}, i_instance::Integer, i_feature::Integer) where {T} = - @views fwd.d[:,:,:,:,i_instance, i_feature] -const Interval2DFeaturedChannel{T} = AbstractArray{T,4} -fwd_channel_interpret_world(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) where {T} = - fwc[w.x.x, w.x.y, w.y.x, w.y.y] - -const FWDFeatureSlice{T} = Union{ - # FWDFeatureSlice(DimensionalFeaturedDataset{T where T,0,OneWorld}) - T, # Note: should be, but it throws error OneWorldFeaturedChannel{T}, - IntervalFeaturedChannel{T}, - Interval2DFeaturedChannel{T}, - # FWDFeatureSlice(DimensionalFeaturedDataset{T where T,2,<:Interval2D}) -} - - -############################################################################################ - - -# channel_size(fwd::OneWorldFWD) = () - -# ninstances(fwd::OneWorldFWD) = size(fwd.d, 1) -# nfeatures(fwd::OneWorldFWD) = size(fwd.d, 2) - -# function fwd_init(::Type{OneWorldFWD}, X::DimensionalFeaturedDataset{T,0,OneWorld}) where {T} -# OneWorldFWD{T}(Array{T,2}(undef, ninstances(X), nfeatures(X))) -# end - -# function fwd_init_world_slice(fwd::OneWorldFWD, i_instance::Integer, w::AbstractWorld) -# nothing -# end - -# hasnans(fwd::OneWorldFWD) = any(_isnan.(fwd.d)) - -# Base.@propagate_inbounds @inline fwdread( -# fwd :: OneWorldFWD{T}, -# i_instance :: Integer, -# w :: OneWorld, -# i_feature :: Integer) where {T} = fwd.d[i_instance, i_feature] - -# @inline function Base.setindex!(fwd::OneWorldFWD{T},, threshold::T w::OneWorld, i_instance::Integer, i_feature::Integer) where {T} -# fwd.d[i_instance, i_feature] = threshold -# end - -# function instances(fwd::OneWorldFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# OneWorldFWD{T}(if return_view == Val(true) @view fwd.d[inds,:] else fwd.d[inds,:] end) -# end - -# Base.@propagate_inbounds @inline function fwdslice_set(fwd::OneWorldFWD{T}, i_feature::Integer, fwdslice::Array{T,1}) where {T} -# fwd.d[:, i_feature] = fwdslice -# end - -# Base.@propagate_inbounds @inline fwdread_channel(fwd::OneWorldFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = -# fwd.d[i_instance, i_feature] -# const OneWorldFeaturedChannel{T} = T -# fwd_channel_interpret_world(fwc::T #=Note: should be OneWorldFeaturedChannel{T}, but it throws error =#, w::OneWorld) where {T} = fwc - -############################################################################################ -# FWD, Interval: 4D array (x × y × ninstances × nfeatures) -############################################################################################ - -# struct IntervalFWD{T} <: UniformFullDimensionalFWD{T,1,<:Interval} -# d :: Array{T,4} -# end - -# channel_size(fwd::IntervalFWD) = (size(fwd, 1),) - -# ninstances(fwd::IntervalFWD) = size(fwd, 3) -# nfeatures(fwd::IntervalFWD) = size(fwd, 4) - -# function fwd_init(::Type{IntervalFWD}, X::DimensionalFeaturedDataset{T,1,<:Interval}) where {T} -# IntervalFWD{T}(Array{T,4}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, ninstances(X), nfeatures(X))) -# end - -# function fwd_init_world_slice(fwd::IntervalFWD, i_instance::Integer, w::AbstractWorld) -# nothing -# end - -# function hasnans(fwd::IntervalFWD) -# # @show ([hasnans(fwd.d[x,y,:,:]) for x in 1:size(fwd, 1) for y in (x+1):size(fwd, 2)]) -# any([hasnans(fwd.d[x,y,:,:]) for x in 1:size(fwd, 1) for y in (x+1):size(fwd, 2)]) -# end - -# Base.@propagate_inbounds @inline fwdread( -# fwd :: IntervalFWD{T}, -# i_instance :: Integer, -# w :: Interval, -# i_feature :: Integer) where {T} = fwd.d[w.x, w.y, i_instance, i_feature] - -# @inline function Base.setindex!(fwd::IntervalFWD{T},, threshold::T w::Interval, i_instance::Integer, i_feature::Integer) where {T} -# fwd.d[w.x, w.y, i_instance, i_feature] = threshold -# end - -# Base.@propagate_inbounds @inline function fwdslice_set(fwd::IntervalFWD{T}, i_feature::Integer, fwdslice::Array{T,3}) where {T} -# fwd.d[:, :, :, i_feature] = fwdslice -# end - -# function instances(fwd::IntervalFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# IntervalFWD{T}(if return_view == Val(true) @view fwd.d[:,:,inds,:] else fwd.d[:,:,inds,:] end) -# end -# Base.@propagate_inbounds @inline fwdread_channel(fwd::IntervalFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = -# @views fwd.d[:,:,i_instance, i_feature] -# const IntervalFeaturedChannel{T} = AbstractArray{T,2} -# fwd_channel_interpret_world(fwc::IntervalFeaturedChannel{T}, w::Interval) where {T} = -# fwc[w.x, w.y] - -############################################################################################ -# FWD, Interval: 6D array (x.x × x.y × y.x × y.y × ninstances × nfeatures) -############################################################################################ - -# struct Interval2DFWD{T} <: UniformFullDimensionalFWD{T,2,<:Interval2D} -# d :: Array{T,6} -# end - -# channel_size(fwd::Interval2DFWD) = (size(fwd, 1),size(fwd, 3)) - -# ninstances(fwd::Interval2DFWD) = size(fwd, 5) -# nfeatures(fwd::Interval2DFWD) = size(fwd, 6) - - -# function fwd_init(::Type{Interval2DFWD}, X::DimensionalFeaturedDataset{T,2,<:Interval2D}) where {T} -# Interval2DFWD{T}(Array{T,6}(undef, max_channel_size(X)[1], max_channel_size(X)[1]+1, max_channel_size(X)[2], max_channel_size(X)[2]+1, ninstances(X), nfeatures(X))) -# end - -# function fwd_init_world_slice(fwd::Interval2DFWD, i_instance::Integer, w::AbstractWorld) -# nothing -# end - -# function hasnans(fwd::Interval2DFWD) -# # @show ([hasnans(fwd.d[xx,xy,yx,yy,:,:]) for xx in 1:size(fwd, 1) for xy in (xx+1):size(fwd, 2) for yx in 1:size(fwd, 3) for yy in (yx+1):size(fwd, 4)]) -# any([hasnans(fwd.d[xx,xy,yx,yy,:,:]) for xx in 1:size(fwd, 1) for xy in (xx+1):size(fwd, 2) for yx in 1:size(fwd, 3) for yy in (yx+1):size(fwd, 4)]) -# end - -# Base.@propagate_inbounds @inline fwdread( -# fwd :: Interval2DFWD{T}, -# i_instance :: Integer, -# w :: Interval2D, -# i_feature :: Integer) where {T} = fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] - -# @inline function Base.setindex!(fwd::Interval2DFWD{T},, threshold::T w::Interval2D, i_instance::Integer, i_feature::Integer) where {T} -# fwd.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_feature] = threshold -# end - -# Base.@propagate_inbounds @inline function fwdslice_set(fwd::Interval2DFWD{T}, i_feature::Integer, fwdslice::Array{T,5}) where {T} -# fwd.d[:, :, :, :, :, i_feature] = fwdslice -# end - -# function instances(fwd::Interval2DFWD{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# Interval2DFWD{T}(if return_view == Val(true) @view fwd.d[:,:,:,:,inds,:] else fwd.d[:,:,:,:,inds,:] end) -# end -# Base.@propagate_inbounds @inline fwdread_channel(fwd::Interval2DFWD{T}, i_instance::Integer, i_feature::Integer) where {T} = -# @views fwd.d[:,:,:,:,i_instance, i_feature] -# const Interval2DFeaturedChannel{T} = AbstractArray{T,4} -# fwd_channel_interpret_world(fwc::Interval2DFeaturedChannel{T}, w::Interval2D) where {T} = -# fwc[w.x.x, w.x.y, w.y.x, w.y.y] - -############################################################################################ - -############################################################################################ -############################################################################################ - - -# TODO add AbstractWorldSet type -function apply_aggregator(fwdslice::FWDFeatureSlice{T}, worlds::Any, aggregator::Agg) where {T,Agg<:Aggregator} - - # TODO try reduce(aggregator, worlds; init=bottom(aggregator, T)) - # TODO remove this SoleModels.aggregator_to_binary... - - if length(worlds |> collect) == 0 - aggregator_bottom(aggregator, T) - else - aggregator((w)->fwd_channel_interpret_world(fwdslice, w), worlds) - end - - # opt = SoleModels.aggregator_to_binary(aggregator) - # gamma = bottom(aggregator, T) - # for w in worlds - # e = fwd_channel_interpret_world(fwdslice, w) - # gamma = opt(gamma,e) - # end - # gamma -end diff --git a/src/dimensional-datasets/datasets/featured-dataset.jl b/src/dimensional-datasets/datasets/featured-dataset.jl deleted file mode 100644 index 57c7e0a..0000000 --- a/src/dimensional-datasets/datasets/featured-dataset.jl +++ /dev/null @@ -1,380 +0,0 @@ - -############################################################################################ -# Featured world dataset -############################################################################################ -# -# In the most general case, the representation of a modal dataset is based on a -# multi-dimensional lookup table, referred to as *propositional lookup table*, -# or *featured world dataset* (abbreviated into fwd). -# -# This structure, is such that the value at fwd[i, w, f], referred to as *gamma*, -# is the value of feature f on world w on the i-th instance, and can be used to answer the -# question whether a proposition (e.g., minimum(A1) ≥ 10) holds onto a given world and instance; -# however, an fwd table can be implemented in many ways, mainly depending on the world type. -# -# Note that this structure does not constitute a AbstractActiveFeaturedDataset (see FeaturedDataset a few lines below) -# -############################################################################################ - -abstract type AbstractFWD{V<:Number,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} end - -# # A function for getting a threshold value from the lookup table -# Maybe TODO: but fails with ArgumentError: invalid index: − of type SoleLogics.OneWorld: -fwdread(fwd::AbstractFWD, args...) = Base.getindex(fwd, args...) -@inline function Base.getindex( - fwd :: AbstractFWD{V,W}, - i_instance :: Integer, - w :: W, - i_feature :: Integer -) where {V,W<:AbstractWorld} - error("TODO provide...") -end - -# -# Actually, the interface for AbstractFWD's is a bit tricky; the most straightforward -# way of learning it is by considering the fallback fwd structure defined as follows. -# TODO oh, but the implementation is broken due to a strange error (see https://discourse.julialang.org/t/tricky-too-many-parameters-for-type-error/25182 ) - -# # The most generic fwd structure is a matrix of dictionaries of size (ninstances × nfeatures) -# struct GenericFWD{V,W} <: AbstractFWD{V,W} -# d :: AbstractVector{<:AbstractDict{W,AbstractVector{V,1}},1} -# nfeatures :: Integer -# end - -# ninstances(fwd::GenericFWD{V}) where {V} = size(fwd, 1) -# nfeatures(fwd::GenericFWD{V}) where {V} = fwd.d -# Base.size(fwd::GenericFWD{V}, args...) where {V} = size(fwd.d, args...) - -# # The matrix is initialized with #undef values -# function fwd_init(::Type{GenericFWD}, X::DimensionalFeaturedDataset{V}) where {V} -# d = Array{Dict{W,V}, 2}(undef, ninstances(X)) -# for i in 1:ninstances -# d[i] = Dict{W,Array{V,1}}() -# end -# GenericFWD{V}(d, nfeatures(X)) -# end - -# # A function for initializing individual world slices -# function fwd_init_world_slice(fwd::GenericFWD{V}, i_instance::Integer, w::AbstractWorld) where {V} -# fwd.d[i_instance][w] = Array{V,1}(undef, fwd.nfeatures) -# end - -# # A function for getting a threshold value from the lookup table -# Base.@propagate_inbounds @inline fwdread( -# fwd :: GenericFWD{V}, -# i_instance :: Integer, -# w :: AbstractWorld, -# i_feature :: Integer) where {V} = fwd.d[i_instance][w][i_feature] - -# # A function for setting a threshold value in the lookup table -# Base.@propagate_inbounds @inline function fwd_set(fwd::GenericFWD{V}, w::AbstractWorld, i_instance::Integer, i_feature::Integer, threshold::V) where {V} -# fwd.d[i_instance][w][i_feature] = threshold -# end - -# # A function for setting threshold values for a single feature (from a feature slice, experimental) -# Base.@propagate_inbounds @inline function fwd_set_feature(fwd::GenericFWD{V}, i_feature::Integer, fwdslice::Any) where {V} -# throw_n_log("Warning! fwd_set_feature with GenericFWD is not yet implemented!") -# for ((i_instance,w),threshold::V) in read_fwdslice(fwdslice) -# fwd.d[i_instance][w][i_feature] = threshold -# end -# end - -# # A function for slicing the dataset -# function instances(fwd::GenericFWD{V}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {V} -# GenericFWD{V}(if return_view == Val(true) @view fwd.d[inds] else fwd.d[inds] end, fwd.nfeatures) -# end - -# Others... -# Base.@propagate_inbounds @inline fwdread_channeaoeu(fwd::GenericFWD{V}, i_instance::Integer, i_feature::Integer) where {V} = TODO -# const GenericFeaturedChannel{V} = TODO -# fwd_channel_interpret_world(fwc::GenericFeaturedChannel{V}, w::AbstractWorld) where {V} = TODO - -isminifiable(::AbstractFWD) = true - -function minify(fwd::AbstractFWD) - minify(fwd.d) #TODO improper -end - -############################################################################################ -# Explicit modal dataset -# -# An explicit modal dataset is the generic form of a modal dataset, and consists of -# a wrapper around an fwd lookup table. The information it adds are the relation set, -# a few functions for enumerating worlds (`accessibles`, `representatives`), -# and a world set initialization function representing initial conditions (initializing world sets). -# -############################################################################################ - -struct FeaturedDataset{ - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - FT<:AbstractFeature{V}, - FWD<:AbstractFWD{V,W,FR}, - G1<:AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}, - G2<:AbstractVector{<:AbstractVector{Tuple{<:Integer,<:Aggregator}}}, -} <: AbstractActiveFeaturedDataset{V,W,FR,FT} - - # Core data (fwd lookup table) - fwd :: FWD - - ## Modal frame: - # Accessibility relations - relations :: AbstractVector{<:AbstractRelation} - - # Features - features :: Vector{FT} - - # Test operators associated with each feature, grouped by their respective aggregator - grouped_featsaggrsnops :: G1 - - grouped_featsnaggrs :: G2 - - # Initial world(s) - initialworld :: Union{Nothing,W,AbstractWorldSet{<:W}} - - function FeaturedDataset{V,W,FR,FT,FWD}( - fwd :: FWD, - relations :: AbstractVector{<:AbstractRelation}, - features :: AbstractVector{FT}, - grouped_featsaggrsnops :: AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}}; - allow_no_instances = false, - initialworld = nothing, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FWD<:AbstractFWD{V,W,FR},FT<:AbstractFeature{V}} - features = collect(features) - ty = "FeaturedDataset{$(V),$(W),$(FR),$(FT)}" - @assert allow_no_instances || ninstances(fwd) > 0 "Can't instantiate $(ty) with no instance. (fwd's type $(typeof(fwd)))" - @assert length(grouped_featsaggrsnops) > 0 && sum(length.(grouped_featsaggrsnops)) > 0 && sum(vcat([[length(test_ops) for test_ops in aggrs] for aggrs in grouped_featsaggrsnops]...)) > 0 "Can't instantiate $(ty) with no test operator: grouped_featsaggrsnops" - @assert nfeatures(fwd) == length(features) "Can't instantiate $(ty) with different numbers of instances $(ninstances(fwd)) and of features $(length(features))." - grouped_featsnaggrs = features_grouped_featsaggrsnops2grouped_featsnaggrs(features, grouped_featsaggrsnops) - check_initialworld(FeaturedDataset, initialworld, W) - new{ - V, - W, - FR, - FT, - FWD, - typeof(grouped_featsaggrsnops), - typeof(grouped_featsnaggrs) - }( - fwd, - relations, - features, - grouped_featsaggrsnops, - grouped_featsnaggrs, - initialworld, - ) - end - - function FeaturedDataset{V,W,FR}( - fwd :: FWD, - relations :: AbstractVector{<:AbstractRelation}, - features :: AbstractVector{<:AbstractFeature}, - args...; - kwargs... - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FWD<:AbstractFWD{V,W,FR}} - features = collect(features) - FT = Union{typeof.(features)...} - features = Vector{FT}(features) - FeaturedDataset{V,W,FR,FT,FWD}(fwd, relations, features, args...; kwargs...) - end - - function FeaturedDataset{V,W}( - fwd :: AbstractFWD{V,W,FR}, - args...; - kwargs... - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - FeaturedDataset{V,W,FR}(fwd, args...; kwargs...) - end - - function FeaturedDataset( - fwd :: AbstractFWD{V,W}, - relations :: AbstractVector{<:AbstractRelation}, - features :: AbstractVector{<:AbstractFeature}, - grouped_featsaggrsnops_or_featsnops, # AbstractVector{<:AbstractDict{<:Aggregator,<:AbstractVector{<:TestOperator}}} - args...; - kwargs..., - ) where {V,W} - FeaturedDataset{V,W}(fwd, relations, features, grouped_featsaggrsnops_or_featsnops, args...; kwargs...) - end - - function FeaturedDataset( - fwd :: AbstractFWD{V,W}, - relations :: AbstractVector{<:AbstractRelation}, - features :: AbstractVector{<:AbstractFeature}, - grouped_featsnops :: AbstractVector{<:AbstractVector{<:TestOperator}}, - args...; - kwargs..., - ) where {V,W<:AbstractWorld} - grouped_featsaggrsnops = grouped_featsnops2grouped_featsaggrsnops(grouped_featsnops) - FeaturedDataset(fwd, relations, features, grouped_featsaggrsnops, args...; kwargs...) - end - - _default_fwd_type(::Type{<:AbstractWorld}) = error("No GenericFWD has been implemented yet. Please provide a `fwd_type` parameter, as in: FeaturedDataset(X, IntervalFWD)") - _default_fwd_type(::Type{<:Union{OneWorld,Interval,Interval2D}}) = UniformFullDimensionalFWD - - # Quite importantly, an fwd can be computed from a dataset in implicit form (domain + ontology + features) - Base.@propagate_inbounds function FeaturedDataset( - X :: DimensionalFeaturedDataset{V,N,W}, - # fwd_type :: Type{<:AbstractFWD} = _default_fwd_type(W), # TODO - fwd_type = _default_fwd_type(W), - args...; - kwargs..., - ) where {V,N,W<:AbstractWorld} - - fwd = begin - - # Initialize the fwd structure - fwd = fwd_type(X) - - # @logmsg LogOverview "DimensionalFeaturedDataset -> FeaturedDataset" - - _features = features(X) - - _n_samples = ninstances(X) - - # Load any (possible) external features - if any(isa.(_features, ExternalFWDFeature)) - i_external_features = first.(filter(((i_feature,is_external_fwd),)->(is_external_fwd), collect(enumerate(isa.(_features, ExternalFWDFeature))))) - for i_feature in i_external_features - feature = _features[i_feature] - fwdslice_set(fwd, i_feature, feature.fwd) - end - end - - # Load any internal features - i_features = first.(filter(((i_feature,is_external_fwd),)->!(is_external_fwd), collect(enumerate(isa.(_features, ExternalFWDFeature))))) - enum_features = zip(i_features, _features[i_features]) - - # Compute features - # p = Progress(_n_samples, 1, "Computing EMD...") - @inbounds Threads.@threads for i_instance in 1:_n_samples - # @logmsg LogDebug "Instance $(i_instance)/$(_n_samples)" - - # if i_instance == 1 || ((i_instance+1) % (floor(Int, ((_n_samples)/4))+1)) == 0 - # @logmsg LogOverview "Instance $(i_instance)/$(_n_samples)" - # end - - for w in allworlds(X, i_instance) - - fwd_init_world_slice(fwd, i_instance, w) - - # @logmsg LogDebug "World" w - - for (i_feature,feature) in enum_features - - gamma = X[i_instance, w, feature, i_feature] - - # @logmsg LogDebug "Feature $(i_feature)" gamma - - fwd[w, i_instance, i_feature] = gamma - - end - end - # next!(p) - end - fwd - end - - FeaturedDataset( - fwd, - relations(X), - _features, - grouped_featsaggrsnops(X), - args...; - initialworld = SoleLogics.initialworld(X), - kwargs..., - ) - end - -end - - -@inline function Base.getindex( - X::FeaturedDataset{V,W}, - i_instance::Integer, - w::W, - feature::AbstractFeature, - args... -) where {V,W<:AbstractWorld} - i_feature = find_feature_id(X, feature) - # X[i_instance, w, feature, i_feature, args...]::V - fwd(X)[i_instance, w, i_feature, args...]::V -end - -@inline function Base.getindex( - X::FeaturedDataset{V,W}, - i_instance::Integer, - w::W, - feature::AbstractFeature, - i_feature::Integer, - args... -) where {V,W<:AbstractWorld} - fwd(X)[i_instance, w, i_feature, args...]::V -end - -Base.size(X::FeaturedDataset) = Base.size(fwd(X)) - -fwd(X::FeaturedDataset) = X.fwd -relations(X::FeaturedDataset) = X.relations -features(X::FeaturedDataset) = X.features -grouped_featsaggrsnops(X::FeaturedDataset) = X.grouped_featsaggrsnops -grouped_featsnaggrs(X::FeaturedDataset) = X.grouped_featsnaggrs - -nfeatures(X::FeaturedDataset) = length(features(X)) -nrelations(X::FeaturedDataset) = length(relations(X)) -ninstances(X::FeaturedDataset) = ninstances(fwd(X)) -worldtype(X::FeaturedDataset{V,W}) where {V,W<:AbstractWorld} = W - -nfeatsnaggrs(X::FeaturedDataset) = sum(length.(grouped_featsnaggrs(X))) - -frame(X::FeaturedDataset, i_instance) = frame(fwd(X), i_instance) -initialworld(X::FeaturedDataset) = X.initialworld -function initialworld(X::FeaturedDataset, i_instance) - initialworld(X) isa AbstractWorldSet ? initialworld(X)[i_instance] : initialworld(X) -end - -function instances(X::FeaturedDataset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) - FeaturedDataset( - instances(fwd(X), inds, return_view), - relations(X), - features(X), - grouped_featsaggrsnops(X); - initialworld = initialworld(X) - ) -end - - -function display_structure(X::FeaturedDataset; indent_str = "") - out = "$(typeof(X))\t$(Base.summarysize(X) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "├ relations: \t$((length(relations(X))))\t$(relations(X))\n" - out *= indent_str * "├ fwd: \t$(typeof(fwd(X)))\t$(Base.summarysize(fwd(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "└ initialworld(s)\t$(initialworld(X))" - out -end - -function hasnans(X::FeaturedDataset) - # @show hasnans(fwd(X)) - hasnans(fwd(X)) -end - - -isminifiable(::FeaturedDataset) = true - -function minify(X::FeaturedDataset) - new_fwd, backmap = minify(fwd(X)) - X = FeaturedDataset( - new_fwd, - relations(X), - features(X), - grouped_featsaggrsnops(X), - ) - X, backmap -end - -############################################################################################ -############################################################################################ -############################################################################################ - -# World-specific featured world datasets and supports -include("dimensional-fwds.jl") diff --git a/src/dimensional-datasets/datasets/generic-supporting-datasets.jl b/src/dimensional-datasets/datasets/generic-supporting-datasets.jl deleted file mode 100644 index c93f582..0000000 --- a/src/dimensional-datasets/datasets/generic-supporting-datasets.jl +++ /dev/null @@ -1,81 +0,0 @@ - -struct GenericSupportingDataset{ - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - M<:AbstractVector{<:AbstractDict{<:AbstractFormula,Vector{W}}}, -} <: SupportingDataset{W,FR} - - memo::M - - function GenericSupportingDataset{W,FR,M}( - memo :: M, - ) where {W<:AbstractWorld,FR<:AbstractFrame{W,Bool},M<:AbstractVector{<:AbstractDict{<:AbstractFormula,Vector{W}}}} - new{W,FR,M}(memo) - end - - function GenericSupportingDataset( - fd :: FeaturedDataset{V,W,FR}, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, ninstances(fd)) - for i_instance in 1:ninstances(fd) - memo[i_instance] = ThreadSafeDict{AbstractFormula,Vector{W}}() - end - GenericSupportingDataset{W,FR,typeof(memo)}(memo) - end -end - -usesmemo(X::GenericSupportingDataset) = true - -Base.size(X::GenericSupportingDataset) = () -capacity(X::GenericSupportingDataset) = Inf -nmemoizedvalues(X::GenericSupportingDataset) = sum(length.(X.d)) - -function instances(X::GFSD, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {GFSD<:GenericSupportingDataset} - GFSD(X.w0, instances(X.memo[inds], return_view)) -end - -hasnans(X::GenericSupportingDataset) = false # TODO double check that this is intended - -isminifiable(X::GenericSupportingDataset) = false # TODO double check that this is intended - -struct ChainedFeaturedSupportingDataset{ - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - M<:AbstractVector{<:AbstractDict{<:AbstractFormula,V}}, -} <: SupportingDataset{W,FR} - - w0::W - - memo::M - - function ChainedFeaturedSupportingDataset{V,W,FR,M}( - memo :: M, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},M<:AbstractVector{<:AbstractDict{<:AbstractFormula,V}}} - new{V,W,FR,M}(memo) - end - - function ChainedFeaturedSupportingDataset( - fd :: FeaturedDataset{V,W,FR}, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - memo = Vector{ThreadSafeDict{AbstractFormula,Vector{W}}}(undef, ninstances(fd)) - for i_instance in 1:ninstances(fd) - memo[i_instance] = ThreadSafeDict{AbstractFormula,Vector{W}}() - end - ChainedFeaturedSupportingDataset{V,W,FR,typeof(memo)}(memo) - end -end - -usesmemo(X::ChainedFeaturedSupportingDataset) = true - -Base.size(X::ChainedFeaturedSupportingDataset) = () -capacity(X::ChainedFeaturedSupportingDataset) = Inf -nmemoizedvalues(X::ChainedFeaturedSupportingDataset) = sum(length.(X.d)) - -function instances(X::CFSD, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {CFSD<:ChainedFeaturedSupportingDataset} - CFSD(X.w0, instances(X.memo[inds], return_view)) -end - -hasnans(X::ChainedFeaturedSupportingDataset) = false # TODO double check that this is intended - -isminifiable(X::ChainedFeaturedSupportingDataset) = false # TODO double check that this is intended diff --git a/src/dimensional-datasets/datasets/main.jl b/src/dimensional-datasets/datasets/main.jl deleted file mode 100644 index 07231c6..0000000 --- a/src/dimensional-datasets/datasets/main.jl +++ /dev/null @@ -1,73 +0,0 @@ - -import Base: size, show, getindex, iterate, length, push!, eltype - -using BenchmarkTools -using ComputedFieldTypes -using DataStructures -using ThreadSafeDicts -using ProgressMeter - -using SoleBase -using SoleBase: LogOverview, LogDebug, LogDetail, throw_n_log -using Logging: @logmsg - -using SoleLogics -using SoleLogics: AbstractFormula, AbstractWorld, AbstractRelation -using SoleLogics: AbstractFrame, AbstractDimensionalFrame, FullDimensionalFrame -import SoleLogics: worldtype, accessibles, allworlds, alphabet, initialworld - -using SoleData -import SoleData: _isnan, hasnans, nvariables, max_channel_size, channel_size -import SoleData: instance, get_instance, slicedataset, instances -import SoleData: dimensionality - -using SoleModels -using SoleModels: Aggregator, AbstractCondition -using SoleModels: BoundedExplicitConditionalAlphabet -using SoleModels: CanonicalFeatureGeq, CanonicalFeatureGeqSoft, CanonicalFeatureLeq, CanonicalFeatureLeqSoft -using SoleModels: AbstractConditionalDataset, AbstractMultiModalFrame -using SoleModels: MultiFrameConditionalDataset, AbstractActiveFeaturedDataset -using SoleModels: apply_test_operator, existential_aggregator, aggregator_bottom, aggregator_to_binary -import SoleModels: representatives, FeatMetaCondition, FeatCondition, featvaltype -import SoleModels: ninstances, nrelations, nfeatures, check, instances, minify -import SoleModels: nmodalities, modalities, display_structure, frame -import SoleModels: grouped_featsaggrsnops, features, grouped_metaconditions, alphabet, find_feature_id, find_relation_id, isminifiable - -using SoleModels: grouped_featsnops2grouped_featsaggrsnops, - grouped_featsaggrsnops2grouped_featsnops, - features_grouped_featsaggrsnops2featsnaggrs_grouped_featsnaggrs, - features_grouped_featsaggrsnops2featsnaggrs, - features_grouped_featsaggrsnops2grouped_featsnaggrs -############################################################################################ - -function check_initialworld(FD::Type{<:AbstractConditionalDataset}, initialworld, W) - @assert isnothing(initialworld) || initialworld isa W "Cannot instantiate" * - " $(FD) with worldtype = $(W) but initialworld of type $(typeof(initialworld))." -end - -include("passive-dimensional-dataset.jl") - -include("dimensional-featured-dataset.jl") -include("featured-dataset.jl") - -abstract type SupportingDataset{W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} end - -isminifiable(X::SupportingDataset) = false - -worldtype(X::SupportingDataset{W}) where {W} = W - -function display_structure(X::SupportingDataset; indent_str = "") - out = "$(typeof(X))\t$((Base.summarysize(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs" - out *= " ($(round(nmemoizedvalues(X))) values)\n" - out -end - -abstract type FeaturedSupportingDataset{V<:Number,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} <: SupportingDataset{W,FR} end - - -include("supported-featured-dataset.jl") - -include("one-step-featured-supporting-dataset/main.jl") -include("generic-supporting-datasets.jl") - -include("check.jl") diff --git a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl b/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl deleted file mode 100644 index b470712..0000000 --- a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/dimensional-supports.jl +++ /dev/null @@ -1,415 +0,0 @@ -import Base: size, ndims, getindex, setindex! - -############################################################################################ -############################################################################################ -# world-specific FWD supports implementations -############################################################################################ -############################################################################################ - -abstract type AbstractUniformFullDimensionalRelationalSupport{T,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} <: AbstractRelationalSupport{T,W,FR} end - -# TODO switch from nothing to missing? -usesmemo(fwd_rs::AbstractUniformFullDimensionalRelationalSupport) = Nothing <: Base.eltype(fwd_rs.d) -capacity(fwd_rs::AbstractUniformFullDimensionalRelationalSupport) = - error("Please, provide method capacity(...).") -nmemoizedvalues(support::AbstractUniformFullDimensionalRelationalSupport) = (capacity(support) - count(isnothing, support.d)) - -############################################################################################ -# FWD relational support for uniform full dimensional modalities: -# a (ninstances × nfeatsnaggrs × nrelations) structure for each world. -# Each world is linearized, resulting in a (3+N*2)-D array -############################################################################################ - -struct UniformFullDimensionalRelationalSupport{ - T, - W<:AbstractWorld, - N, - D<:AbstractArray{TT} where TT<:Union{T,Nothing}, -} <: AbstractUniformFullDimensionalRelationalSupport{T,W,FullDimensionalFrame{N,W,Bool}} - - d :: D - - function UniformFullDimensionalRelationalSupport{T,W,N,D}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{TT} where TT<:Union{T,Nothing}} - new{T,W,N,D}(d) - end - - function UniformFullDimensionalRelationalSupport{T,W,N}(d::D) where {T,W<:AbstractWorld,N,D<:AbstractArray{TT} where TT<:Union{T,Nothing}} - UniformFullDimensionalRelationalSupport{T,W,N,D}(d) - end - - function UniformFullDimensionalRelationalSupport( - fwd::UniformFullDimensionalFWD{T,W,0}, - nfeatsnaggrs::Integer, - nrelations::Integer, - perform_initialization::Bool = false, - ) where {T,W<:OneWorld} - # error("TODO actually, using a relational or a global support with a OneWorld frame makes no sense. Figure out what to do here!") - _fwd_rs = begin - if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 3}(undef, ninstances(fwd), nfeatsnaggrs, nrelations) - fill!(_fwd_rs, nothing) - else - Array{T,3}(undef, ninstances(fwd), nfeatsnaggrs, nrelations) - end - end - UniformFullDimensionalRelationalSupport{T,W,0,typeof(_fwd_rs)}(_fwd_rs) - end - function UniformFullDimensionalRelationalSupport( - fwd::UniformFullDimensionalFWD{T,W,1}, - nfeatsnaggrs::Integer, - nrelations::Integer, - perform_initialization::Bool = false, - ) where {T,W<:Interval} - _fwd_rs = begin - if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 5}(undef, size(fwd, 1), size(fwd, 2), ninstances(fwd), nfeatsnaggrs, nrelations) - fill!(_fwd_rs, nothing) - else - Array{T,5}(undef, size(fwd, 1), size(fwd, 2), ninstances(fwd), nfeatsnaggrs, nrelations) - end - end - UniformFullDimensionalRelationalSupport{T,W,1,typeof(_fwd_rs)}(_fwd_rs) - end - function UniformFullDimensionalRelationalSupport( - fwd::UniformFullDimensionalFWD{T,W,2}, - nfeatsnaggrs::Integer, - nrelations::Integer, - perform_initialization::Bool = false, - ) where {T,W<:Interval2D} - _fwd_rs = begin - if perform_initialization - _fwd_rs = Array{Union{T,Nothing}, 7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), ninstances(fwd), nfeatsnaggrs, nrelations) - fill!(_fwd_rs, nothing) - else - Array{T,7}(undef, size(fwd, 1), size(fwd, 2), size(fwd, 3), size(fwd, 4), ninstances(fwd), nfeatsnaggrs, nrelations) - end - end - UniformFullDimensionalRelationalSupport{T,W,2,typeof(_fwd_rs)}(_fwd_rs) - end - - function UniformFullDimensionalRelationalSupport( - fd::FeaturedDataset, - perform_initialization::Bool = false, - ) - UniformFullDimensionalRelationalSupport(fwd(fd), nfeatsnaggrs(fd), nrelations(fd), perform_initialization) - end - -end - -Base.size(support::UniformFullDimensionalRelationalSupport, args...) = size(support.d, args...) -Base.ndims(support::UniformFullDimensionalRelationalSupport, args...) = ndims(support.d, args...) - -ninstances(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)-2) -nfeatsnaggrs(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)-1) -nrelations(support::UniformFullDimensionalRelationalSupport) = size(support, ndims(support)) - -############################################################################################ - -function capacity(support::UniformFullDimensionalRelationalSupport{T,OneWorld}) where {T} - prod(size(support)) -end -function capacity(support::UniformFullDimensionalRelationalSupport{T,<:Interval}) where {T} - prod([ - ninstances(support), - nfeatsnaggrs(support), - nrelations(support), - div(size(support, 1)*(size(support, 2)),2), - ]) -end -function capacity(support::UniformFullDimensionalRelationalSupport{T,<:Interval2D}) where {T} - prod([ - ninstances(support), - nfeatsnaggrs(support), - nrelations(support), - div(size(support, 1)*(size(support, 2)),2), - div(size(support, 3)*(size(support, 4)),2), - ]) -end - -############################################################################################ - -function hasnans(support::UniformFullDimensionalRelationalSupport{T,OneWorld}) where {T} - any(_isnan.(support.d)) -end -function hasnans(support::UniformFullDimensionalRelationalSupport{T,<:Interval}) where {T} - any([hasnans(support.d[x,y,:,:,:]) - for x in 1:size(support, 1) for y in (x+1):size(support, 2)]) -end -function hasnans(support::UniformFullDimensionalRelationalSupport{T,<:Interval2D}) where {T} - any([hasnans(support.d[xx,xy,yx,yy,:,:,:]) - for xx in 1:size(support, 1) for xy in (xx+1):size(support, 2) - for yx in 1:size(support, 3) for yy in (yx+1):size(support, 4)]) -end - -############################################################################################ - -function fwd_rs_init_world_slice( - support::UniformFullDimensionalRelationalSupport, - i_instance::Integer, - i_featsnaggr::Integer, - i_relation::Integer -) - nothing -end - -############################################################################################ -############################################################################################ -############################################################################################ - -@inline function Base.getindex( - support :: UniformFullDimensionalRelationalSupport{T,W}, - i_instance :: Integer, - w :: W, - i_featsnaggr :: Integer, - i_relation :: Integer -) where {T,W<:OneWorld} - support.d[i_instance, i_featsnaggr, i_relation] -end -@inline function Base.getindex( - support :: UniformFullDimensionalRelationalSupport{T,W}, - i_instance :: Integer, - w :: W, - i_featsnaggr :: Integer, - i_relation :: Integer -) where {T,W<:Interval} - support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] -end -@inline function Base.getindex( - support :: UniformFullDimensionalRelationalSupport{T,W}, - i_instance :: Integer, - w :: W, - i_featsnaggr :: Integer, - i_relation :: Integer -) where {T,W<:Interval2D} - support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] -end - -############################################################################################ - -Base.@propagate_inbounds @inline function Base.setindex!( - support::UniformFullDimensionalRelationalSupport{T,OneWorld}, - threshold::T, - i_instance::Integer, - w::OneWorld, - i_featsnaggr::Integer, - i_relation::Integer, -) where {T} - support.d[i_instance, i_featsnaggr, i_relation] = threshold -end - -Base.@propagate_inbounds @inline function Base.setindex!( - support::UniformFullDimensionalRelationalSupport{T,<:Interval}, - threshold::T, - i_instance::Integer, - w::Interval, - i_featsnaggr::Integer, - i_relation::Integer, -) where {T} - support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] = threshold -end - -Base.@propagate_inbounds @inline function Base.setindex!( - support::UniformFullDimensionalRelationalSupport{T,<:Interval2D}, - threshold::T, - i_instance::Integer, - w::Interval2D, - i_featsnaggr::Integer, - i_relation::Integer, -) where {T} - support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] = threshold -end - -############################################################################################ - -function instances( - support::UniformFullDimensionalRelationalSupport{T,W,N}, - inds::AbstractVector{<:Integer}, - return_view::Union{Val{true},Val{false}} = Val(false) -) where {T,W<:OneWorld,N} - UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) -end -function instances( - support::UniformFullDimensionalRelationalSupport{T,W,N}, - inds::AbstractVector{<:Integer}, - return_view::Union{Val{true},Val{false}} = Val(false) -) where {T,W<:Interval,N} - UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) -end -function instances( - support::UniformFullDimensionalRelationalSupport{T,W,N}, - inds::AbstractVector{<:Integer}, - return_view::Union{Val{true},Val{false}} = Val(false) -) where {T,W<:Interval2D,N} - UniformFullDimensionalRelationalSupport{T,W,N}(if return_view == Val(true) @view support.d[:,:,:,:,inds,:,:] else support.d[:,:,:,:,inds,:,:] end) -end - -############################################################################################ -# FWD support, OneWorld: 3D array (ninstances × nfeatsnaggrs × nrelations) -############################################################################################ - -# struct OneWorldFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,OneWorld} -# d :: Array{T,3} -# end - -# ninstances(support::OneWorldFWD_RS) = size(support, 1) -# nfeatsnaggrs(support::OneWorldFWD_RS) = size(support, 2) -# nrelations(support::OneWorldFWD_RS) = size(support, 3) -# capacity(support::OneWorldFWD_RS) = prod(size(support.d)) - -# @inline Base.getindex( -# support :: OneWorldFWD_RS{T}, -# i_instance :: Integer, -# w :: OneWorld, -# i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[i_instance, i_featsnaggr, i_relation] -# Base.size(support::OneWorldFWD_RS, args...) = size(support.d, args...) - -# hasnans(support::OneWorldFWD_RS) = any(_isnan.(support.d)) - -# function fwd_rs_init(fd::FeaturedDataset{T,OneWorld}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} -# if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 3}(undef, ninstances(fd), nfeatsnaggrs, nrelations), nothing) -# OneWorldFWD_RS{Union{T,Nothing}}(_fwd_rs) -# else -# _fwd_rs = Array{T,3}(undef, ninstances(fd), nfeatsnaggrs, nrelations) -# OneWorldFWD_RS{T}(_fwd_rs) -# end -# end -# fwd_rs_init_world_slice(support::OneWorldFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = -# nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::OneWorldFWD_RS{T}, threshold::T, i_instance::Integer, w::OneWorld, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[i_instance, i_featsnaggr, i_relation] = threshold -# function instances(support::OneWorldFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# OneWorldFWD_RS{T}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) -# end - -############################################################################################ -# FWD support, Interval: 5D array (x × y × ninstances × nfeatsnaggrs × nrelations) -############################################################################################ - - -# struct IntervalFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,<:Interval} -# d :: Array{T,5} -# end - -# ninstances(support::IntervalFWD_RS) = size(support, 3) -# nfeatsnaggrs(support::IntervalFWD_RS) = size(support, 4) -# nrelations(support::IntervalFWD_RS) = size(support, 5) -# capacity(support::IntervalFWD_RS) = -# prod([ninstances(support), nfeatsnaggrs(support), nrelations(support), div(size(support.d, 1)*(size(support.d, 1)+1),2)]) - -# @inline Base.getindex( -# support :: IntervalFWD_RS{T}, -# i_instance :: Integer, -# w :: Interval, -# i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] -# Base.size(support::IntervalFWD_RS, args...) = size(support.d, args...) - - -# function hasnans(support::IntervalFWD_RS) -# # @show [hasnans(support.d[x,y,:,:,:]) for x in 1:size(support.d, 1) for y in (x+1):size(support.d, 2)] -# any([hasnans(support.d[x,y,:,:,:]) for x in 1:size(support.d, 1) for y in (x+1):size(support.d, 2)]) -# end - -# function fwd_rs_init(fd::FeaturedDataset{T,<:Interval}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} -# _fwd = fd.fwd -# if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, size(_fwd, 1), size(_fwd, 2), ninstances(fd), nfeatsnaggrs, nrelations), nothing) -# IntervalFWD_RS{Union{T,Nothing}}(_fwd_rs) -# else -# _fwd_rs = Array{T,5}(undef, size(_fwd, 1), size(_fwd, 2), ninstances(fd), nfeatsnaggrs, nrelations) -# IntervalFWD_RS{T}(_fwd_rs) -# end -# end -# fwd_rs_init_world_slice(support::IntervalFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = -# nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::IntervalFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[w.x, w.y, i_instance, i_featsnaggr, i_relation] = threshold -# function instances(support::IntervalFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# IntervalFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) -# end - -############################################################################################ -# FWD support, Interval2D: 7D array (x.x × x.y × y.x × y.y × ninstances × nfeatsnaggrs × nrelations) -############################################################################################ - -# struct Interval2DFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,<:Interval2D} -# d :: Array{T,7} -# end - -# ninstances(support::Interval2DFWD_RS) = size(support, 5) -# nfeatsnaggrs(support::Interval2DFWD_RS) = size(support, 6) -# nrelations(support::Interval2DFWD_RS) = size(support, 7) -# @inline Base.getindex( -# support :: Interval2DFWD_RS{T}, -# i_instance :: Integer, -# w :: Interval2D, -# i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] -# size(support::Interval2DFWD_RS) = size(support.d, args...) - -# TODO... hasnans(support::Interval2DFWD_RS) = any(_isnan.(support.d)) -# TODO...? hasnans(support::Interval2DFWD_RS) = any([hasnans(support.d[xx,xy,yx,yy,:,:,:]) for xx in 1:size(support.d, 1) for xy in (xx+1):size(support.d, 2) for yx in 1:size(support.d, 3) for yy in (yx+1):size(support.d, 4)]) - -# fwd_rs_init(fd::FeaturedDataset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} = begin -# _fwd = fd.fwd -# if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), ninstances(fd), nfeatsnaggrs, nrelations), nothing) -# Interval2DFWD_RS{Union{T,Nothing}}(_fwd_rs) -# else -# _fwd_rs = Array{T,7}(undef, size(_fwd, 1), size(_fwd, 2), size(_fwd, 3), size(_fwd, 4), ninstances(fd), nfeatsnaggrs, nrelations) -# Interval2DFWD_RS{T}(_fwd_rs) -# end -# end -# fwd_rs_init_world_slice(support::Interval2DFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = -# nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[w.x.x, w.x.y, w.y.x, w.y.y, i_instance, i_featsnaggr, i_relation] = threshold -# function instances(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# Interval2DFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,:,:,inds,:,:] else support.d[:,:,:,:,inds,:,:] end) -# end - - -############################################################################################ -# FWD support, Interval2D: 7D array (linearized(x) × linearized(y) × ninstances × nfeatsnaggrs × nrelations) -############################################################################################ - -# # TODO rewrite - -# struct Interval2DFWD_RS{T} <: AbstractUniformFullDimensionalRelationalSupport{T,<:Interval2D} -# d :: Array{T,5} -# end - -# ninstances(support::Interval2DFWD_RS) = size(support, 3) -# nfeatsnaggrs(support::Interval2DFWD_RS) = size(support, 4) -# nrelations(support::Interval2DFWD_RS) = size(support, 5) -# capacity(support::Interval2DFWD_RS) = prod(size(support.d)) - -# @inline Base.getindex( -# support :: Interval2DFWD_RS{T}, -# i_instance :: Integer, -# w :: Interval2D, -# i_featsnaggr :: Integer, -# i_relation :: Integer) where {T} = support.d[w.x.x+div((w.x.y-2)*(w.x.y-1),2), w.y.x+div((w.y.y-2)*(w.y.y-1),2), i_instance, i_featsnaggr, i_relation] -# Base.size(support::Interval2DFWD_RS, args...) = size(support.d, args...) - -# hasnans(support::Interval2DFWD_RS) = any(_isnan.(support.d)) - -# function fwd_rs_init(fd::FeaturedDataset{T,<:Interval2D}, nfeatsnaggrs::Integer, nrelations::Integer, perform_initialization::Bool) where {T} -# _fwd = fd.fwd -# if perform_initialization -# _fwd_rs = fill!(Array{Union{T,Nothing}, 5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), ninstances(fd), nfeatsnaggrs, nrelations), nothing) -# Interval2DFWD_RS{Union{T,Nothing}}(_fwd_rs) -# else -# _fwd_rs = Array{T,5}(undef, div(size(_fwd, 1)*size(_fwd, 2),2), div(size(_fwd, 3)*size(_fwd, 4),2), ninstances(fd), nfeatsnaggrs, nrelations) -# Interval2DFWD_RS{T}(_fwd_rs) -# end -# end -# fwd_rs_init_world_slice(support::Interval2DFWD_RS, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) = -# nothing -# Base.@propagate_inbounds @inline Base.setindex!(support::Interval2DFWD_RS{T}, threshold::T, i_instance::Integer, w::Interval2D, i_featsnaggr::Integer, i_relation::Integer) where {T} = -# support.d[w.x.x+div((w.x.y-2)*(w.x.y-1),2), w.y.x+div((w.y.y-2)*(w.y.y-1),2), i_instance, i_featsnaggr, i_relation] = threshold -# function instances(support::Interval2DFWD_RS{T}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {T} -# Interval2DFWD_RS{T}(if return_view == Val(true) @view support.d[:,:,inds,:,:] else support.d[:,:,inds,:,:] end) -# end diff --git a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl b/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl deleted file mode 100644 index 3baa63f..0000000 --- a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/generic-supports.jl +++ /dev/null @@ -1,108 +0,0 @@ - -############################################################################################ -############################################################################################ -############################################################################################ - -struct GenericRelationalSupport{ - V, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - D<:AbstractArray{Dict{W,VV}, 3} where VV<:Union{V,Nothing}, -} <: AbstractRelationalSupport{V,W,FR} - - d :: D - - function GenericRelationalSupport{V,W,FR}(d::D) where {V,W<:AbstractArray,FR<:AbstractFrame{W,Bool},D<:AbstractArray{V,2}} - new{V,W,FR,D}(d) - end - - function GenericRelationalSupport(fd::FeaturedDataset{V,W,FR}, perform_initialization = false) where {V,W,FR<:AbstractFrame{W,Bool}} - _nfeatsnaggrs = nfeatsnaggrs(fd) - _fwd_rs = begin - if perform_initialization - _fwd_rs = Array{Dict{W,Union{V,Nothing}}, 3}(undef, ninstances(fd), _nfeatsnaggrs, nrelations(fd)) - fill!(_fwd_rs, nothing) - else - Array{Dict{W,V}, 3}(undef, ninstances(fd), _nfeatsnaggrs, nrelations(fd)) - end - end - GenericRelationalSupport{V,W,FR}(_fwd_rs) - end -end - -# default_fwd_rs_type(::Type{<:AbstractWorld}) = GenericRelationalSupport # TODO implement similar pattern used for fwd - -function hasnans(support::GenericRelationalSupport) - # @show any(map(d->(any(_isnan.(collect(values(d))))), support.d)) - any(map(d->(any(_isnan.(collect(values(d))))), support.d)) -end - -ninstances(support::GenericRelationalSupport) = size(support, 1) -nfeatsnaggrs(support::GenericRelationalSupport) = size(support, 2) -nrelations(support::GenericRelationalSupport) = size(support, 3) -capacity(support::GenericRelationalSupport) = Inf -nmemoizedvalues(support::GenericRelationalSupport) = sum(length.(support.d)) - -@inline function Base.getindex( - support :: GenericRelationalSupport{V,W}, - i_instance :: Integer, - w :: W, - i_featsnaggr :: Integer, - i_relation :: Integer -) where {V,W<:AbstractWorld} - support.d[i_instance, i_featsnaggr, i_relation][w] -end -Base.size(support::GenericRelationalSupport, args...) = size(support.d, args...) - -fwd_rs_init_world_slice(support::GenericRelationalSupport{V,W}, i_instance::Integer, i_featsnaggr::Integer, i_relation::Integer) where {V,W} = - support.d[i_instance, i_featsnaggr, i_relation] = Dict{W,V}() -@inline function Base.setindex!(support::GenericRelationalSupport{V,W}, threshold::V, i_instance::Integer, w::AbstractWorld, i_featsnaggr::Integer, i_relation::Integer) where {V,W} - support.d[i_instance, i_featsnaggr, i_relation][w] = threshold -end -function instances(support::GenericRelationalSupport{V,W,FR}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {V,W,FR} - GenericRelationalSupport{V,W,FR}(if return_view == Val(true) @view support.d[inds,:,:] else support.d[inds,:,:] end) -end - -############################################################################################ - -# Note: the global support is world-agnostic -struct GenericGlobalSupport{V,D<:AbstractArray{V,2}} <: AbstractGlobalSupport{V} - d :: D - - function GenericGlobalSupport{V,D}(d::D) where {V,D<:AbstractArray{V,2}} - new{V,D}(d) - end - function GenericGlobalSupport{V}(d::D) where {V,D<:AbstractArray{V,2}} - GenericGlobalSupport{V,D}(d) - end - - function GenericGlobalSupport(fd::FeaturedDataset{V}) where {V} - @assert worldtype(fd) != OneWorld "TODO adjust this note: note that you should not use a global support when not using global decisions" - _nfeatsnaggrs = nfeatsnaggrs(fd) - GenericGlobalSupport{V}(Array{V,2}(undef, ninstances(fd), _nfeatsnaggrs)) - end -end - -capacity(support::GenericGlobalSupport) = prod(size(support.d)) -nmemoizedvalues(support::GenericGlobalSupport) = sum(support.d) - -# default_fwd_gs_type(::Type{<:AbstractWorld}) = GenericGlobalSupport # TODO implement similar pattern used for fwd - -function hasnans(support::GenericGlobalSupport) - # @show any(_isnan.(support.d)) - any(_isnan.(support.d)) -end - -ninstances(support::GenericGlobalSupport) = size(support, 1) -nfeatsnaggrs(support::GenericGlobalSupport) = size(support, 2) -Base.getindex( - support :: GenericGlobalSupport, - i_instance :: Integer, - i_featsnaggr :: Integer) = support.d[i_instance, i_featsnaggr] -Base.size(support::GenericGlobalSupport{V}, args...) where {V} = size(support.d, args...) - -Base.setindex!(support::GenericGlobalSupport{V}, threshold::V, i_instance::Integer, i_featsnaggr::Integer) where {V} = - support.d[i_instance, i_featsnaggr] = threshold -function instances(support::GenericGlobalSupport{V}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {V} - GenericGlobalSupport{V}(if return_view == Val(true) @view support.d[inds,:] else support.d[inds,:] end) -end diff --git a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl b/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl deleted file mode 100644 index ab54f6f..0000000 --- a/src/dimensional-datasets/datasets/one-step-featured-supporting-dataset/main.jl +++ /dev/null @@ -1,282 +0,0 @@ - -# Compute modal dataset propositions and 1-modal decisions -struct OneStepFeaturedSupportingDataset{ - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - VV<:Union{V,Nothing}, - FWDRS<:AbstractRelationalSupport{VV,W,FR}, - FWDGS<:Union{AbstractGlobalSupport{V},Nothing}, - G<:AbstractVector{Tuple{<:AbstractFeature,<:Aggregator}}, -} <: FeaturedSupportingDataset{V,W,FR} - - # Relational support - fwd_rs :: FWDRS - - # Global support - fwd_gs :: FWDGS - - # Features and Aggregators - featsnaggrs :: G - - function OneStepFeaturedSupportingDataset( - fwd_rs::FWDRS, - fwd_gs::FWDGS, - featsnaggrs::G, - ) where { - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - VV<:Union{V,Nothing}, - FWDRS<:AbstractRelationalSupport{VV,W,FR}, - FWDGS<:Union{AbstractGlobalSupport{V},Nothing}, - G<:AbstractVector{Tuple{<:AbstractFeature,<:Aggregator}}, - } - @assert nfeatsnaggrs(fwd_rs) == length(featsnaggrs) "Can't instantiate $(ty) with unmatching nfeatsnaggrs for fwd_rs and provided featsnaggrs: $(nfeatsnaggrs(fwd_rs)) and $(length(featsnaggrs))" - if fwd_gs != nothing - @assert nfeatsnaggrs(fwd_gs) == length(featsnaggrs) "Can't instantiate $(ty) with unmatching nfeatsnaggrs for fwd_gs and provided featsnaggrs: $(nfeatsnaggrs(fwd_gs)) and $(length(featsnaggrs))" - @assert ninstances(fwd_gs) == ninstances(fwd_rs) "Can't instantiate $(ty) with unmatching ninstances for fwd_gs and fwd_rs support: $(ninstances(fwd_gs)) and $(ninstances(fwd_rs))" - end - new{V,W,FR,VV,FWDRS,FWDGS,G}(fwd_rs, fwd_gs, featsnaggrs) - end - - _default_rs_type(::Type{<:AbstractWorld}) = GenericRelationalSupport - _default_rs_type(::Type{<:Union{OneWorld,Interval,Interval2D}}) = UniformFullDimensionalRelationalSupport - - # A function that computes the support from an explicit modal dataset - Base.@propagate_inbounds function OneStepFeaturedSupportingDataset( - fd :: FeaturedDataset{V,W}, - relational_support_type :: Type{<:AbstractRelationalSupport} = _default_rs_type(W); - compute_relation_glob = false, - use_memoization = false, - ) where {V,W<:AbstractWorld} - - # @logmsg LogOverview "FeaturedDataset -> SupportedFeaturedDataset " - - _fwd = fwd(fd) - _features = features(fd) - _relations = relations(fd) - _grouped_featsnaggrs = grouped_featsnaggrs(fd) - featsnaggrs = features_grouped_featsaggrsnops2featsnaggrs(features(fd), grouped_featsaggrsnops(fd)) - - compute_fwd_gs = begin - if globalrel in _relations - throw_n_log("globalrel in relations: $(_relations)") - _relations = filter!(l->l≠globalrel, _relations) - true - elseif compute_relation_glob - true - else - false - end - end - - _n_samples = ninstances(fd) - nrelations = length(_relations) - nfeatsnaggrs = sum(length.(_grouped_featsnaggrs)) - - # Prepare fwd_rs - fwd_rs = relational_support_type(fd, use_memoization) - - # Prepare fwd_gs - fwd_gs = begin - if compute_fwd_gs - GenericGlobalSupport(fd) - else - nothing - end - end - - # p = Progress(_n_samples, 1, "Computing EMD supports...") - Threads.@threads for i_instance in 1:_n_samples - # @logmsg LogDebug "Instance $(i_instance)/$(_n_samples)" - - # if i_instance == 1 || ((i_instance+1) % (floor(Int, ((_n_samples)/4))+1)) == 0 - # @logmsg LogOverview "Instance $(i_instance)/$(_n_samples)" - # end - - for (i_feature,aggregators) in enumerate(_grouped_featsnaggrs) - feature = _features[i_feature] - # @logmsg LogDebug "Feature $(i_feature)" - - fwdslice = fwdread_channel(_fwd, i_instance, i_feature) - - # @logmsg LogDebug fwdslice - - # Global relation (independent of the current world) - if compute_fwd_gs - # @logmsg LogDebug "globalrel" - - # TODO optimize: all aggregators are likely reading the same raw values. - for (i_featsnaggr,aggr) in aggregators - # Threads.@threads for (i_featsnaggr,aggr) in aggregators - - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, globalrel, feature, aggr) - - # @logmsg LogDebug "Aggregator[$(i_featsnaggr)]=$(aggr) --> $(gamma)" - - fwd_gs[i_instance, i_featsnaggr] = gamma - end - end - - if !use_memoization - # Other relations - for (i_relation,relation) in enumerate(_relations) - - # @logmsg LogDebug "Relation $(i_relation)/$(nrelations)" - - for (i_featsnaggr,aggr) in aggregators - fwd_rs_init_world_slice(fwd_rs, i_instance, i_featsnaggr, i_relation) - end - - for w in allworlds(fd, i_instance) - - # @logmsg LogDebug "World" w - - # TODO optimize: all aggregators are likely reading the same raw values. - for (i_featsnaggr,aggr) in aggregators - - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, w, relation, feature, aggr) - - # @logmsg LogDebug "Aggregator" aggr gamma - - fwd_rs[i_instance, w, i_featsnaggr, i_relation] = gamma - end - end - end - end - end - # next!(p) - end - OneStepFeaturedSupportingDataset(fwd_rs, fwd_gs, featsnaggrs) - end -end - -fwd_rs(X::OneStepFeaturedSupportingDataset) = X.fwd_rs -fwd_gs(X::OneStepFeaturedSupportingDataset) = X.fwd_gs -featsnaggrs(X::OneStepFeaturedSupportingDataset) = X.featsnaggrs - -ninstances(X::OneStepFeaturedSupportingDataset) = ninstances(fwd_rs(X)) -# nfeatsnaggrs(X::OneStepFeaturedSupportingDataset) = nfeatsnaggrs(fwd_rs(X)) - -# TODO delegate to the two components... -function checksupportconsistency( - fd::FeaturedDataset{V,W}, - X::OneStepFeaturedSupportingDataset{V,W}, -) where {V,W<:AbstractWorld} - @assert ninstances(fd) == ninstances(X) "Consistency check failed! Unmatching ninstances for fd and support: $(ninstances(fd)) and $(ninstances(X))" - # @assert nrelations(fd) == (nrelations(fwd_rs(X)) + (isnothing(fwd_gs(X)) ? 0 : 1)) "Consistency check failed! Unmatching nrelations for fd and support: $(nrelations(fd)) and $(nrelations(fwd_rs(X)))+$((isnothing(fwd_gs(X)) ? 0 : 1))" - @assert nrelations(fd) >= nrelations(fwd_rs(X)) "Consistency check failed! Inconsistent nrelations for fd and support: $(nrelations(fd)) < $(nrelations(fwd_rs(X)))" - _nfeatsnaggrs = nfeatsnaggrs(fd) - @assert _nfeatsnaggrs == length(featsnaggrs(X)) "Consistency check failed! Unmatching featsnaggrs for fd and support: $(featsnaggrs(fd)) and $(featsnaggrs(X))" - return true -end - -usesmemo(X::OneStepFeaturedSupportingDataset) = usesglobalmemo(X) || usesmodalmemo(X) -usesglobalmemo(X::OneStepFeaturedSupportingDataset) = false -usesmodalmemo(X::OneStepFeaturedSupportingDataset) = usesmemo(fwd_rs(X)) - -Base.size(X::OneStepFeaturedSupportingDataset) = (size(fwd_rs(X)), (isnothing(fwd_gs(X)) ? () : size(fwd_gs(X)))) - -find_featsnaggr_id(X::OneStepFeaturedSupportingDataset, feature::AbstractFeature, aggregator::Aggregator) = findfirst(x->x==(feature, aggregator), featsnaggrs(X)) - -function instances(X::OneStepFeaturedSupportingDataset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) - OneStepFeaturedSupportingDataset( - instances(fwd_rs(X), inds, return_view), - (isnothing(fwd_gs(X)) ? nothing : instances(fwd_gs(X), inds, return_view)), - featsnaggrs(X) - ) -end - - -function hasnans(X::OneStepFeaturedSupportingDataset) - hasnans(fwd_rs(X)) || (!isnothing(fwd_gs(X)) && hasnans(fwd_gs(X))) -end - -isminifiable(X::OneStepFeaturedSupportingDataset) = isminifiable(fwd_rs(X)) && (isnothing(fwd_gs(X)) || isminifiable(fwd_gs(X))) - -function minify(X::OSSD) where {OSSD<:OneStepFeaturedSupportingDataset} - (new_fwd_rs, new_fwd_gs), backmap = - minify([ - fwd_rs(X), - fwd_gs(X), - ]) - - X = OSSD( - new_fwd_rs, - new_fwd_gs, - featsnaggrs(X), - ) - X, backmap -end - -function display_structure(X::OneStepFeaturedSupportingDataset; indent_str = "") - out = "$(typeof(X))\t$((Base.summarysize(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "├ fwd_rs\t$(Base.summarysize(fwd_rs(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t" - if usesmodalmemo(X) - out *= "(shape $(Base.size(fwd_rs(X))), $(nmemoizedvalues(fwd_rs(X))) values," * - " $(round(nonnothingshare(fwd_rs(X))*100, digits=2))% memoized)\n" - else - out *= "(shape $(Base.size(fwd_rs(X))))\n" - end - out *= indent_str * "└ fwd_gs\t" - if !isnothing(fwd_gs(X)) - out *= "$(Base.summarysize(fwd_gs(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs\t" - if usesglobalmemo(X) - out *= "(shape $(Base.size(fwd_gs(X))), $(nmemoizedvalues(fwd_gs(X))) values," * - " $(round(nonnothingshare(fwd_gs(X))*100, digits=2))% memoized)\n" - else - out *= "(shape $(Base.size(fwd_gs(X))))\n" - end - else - out *= "−" - end - out -end - - -############################################################################################ - -function compute_global_gamma( - X::OneStepFeaturedSupportingDataset{V,W}, - fd::FeaturedDataset{V,W}, - i_instance::Integer, - feature::AbstractFeature, - aggregator::Aggregator, - i_featsnaggr::Integer = find_featsnaggr_id(X, feature, aggregator), -) where {V,W<:AbstractWorld} - _fwd_gs = fwd_gs(X) - # @assert !isnothing(_fwd_gs) "Error. SupportedFeaturedDataset must be built with compute_relation_glob = true for it to be ready to test global decisions." - if usesglobalmemo(X) && isnothing(_fwd_gs[i_instance, i_featsnaggr]) - error("TODO finish this: memoization on the global table") - # gamma = TODO... - # i_feature = find_feature_id(fd, feature) - # fwdslice = fwdread_channel(fwd(fd), i_instance, i_feature) - _fwd_gs[i_instance, i_featsnaggr] = gamma - end - _fwd_gs[i_instance, i_featsnaggr] -end - -function compute_modal_gamma( - X::OneStepFeaturedSupportingDataset{V,W}, - fd::FeaturedDataset{V,W}, - i_instance::Integer, - w::W, - r::AbstractRelation, - feature::AbstractFeature, - aggregator::Aggregator, - i_featsnaggr = find_featsnaggr_id(X, feature, aggregator), - i_relation = nothing, -)::V where {V,W<:AbstractWorld} - _fwd_rs = fwd_rs(X) - if usesmodalmemo(X) && isnothing(_fwd_rs[i_instance, w, i_featsnaggr, i_relation]) - i_feature = find_feature_id(fd, feature) - fwdslice = fwdread_channel(fwd(fd), i_instance, i_feature) - gamma = fwdslice_onestep_accessible_aggregation(fd, fwdslice, i_instance, w, r, feature, aggregator) - fwd_rs[i_instance, w, i_featsnaggr, i_relation, gamma] - end - _fwd_rs[i_instance, w, i_featsnaggr, i_relation] -end - -include("generic-supports.jl") -include("dimensional-supports.jl") diff --git a/src/dimensional-datasets/datasets/passive-dimensional-dataset.jl b/src/dimensional-datasets/datasets/passive-dimensional-dataset.jl deleted file mode 100644 index 8f35a7c..0000000 --- a/src/dimensional-datasets/datasets/passive-dimensional-dataset.jl +++ /dev/null @@ -1,94 +0,0 @@ -using SoleData: slicedataset -import SoleData: get_instance, ninstances, nvariables, channel_size, max_channel_size, dimensionality, eltype -using SoleData: AbstractDimensionalDataset, - AbstractDimensionalInstance, - AbstractDimensionalChannel, - UniformDimensionalDataset, - DimensionalInstance, - DimensionalChannel - -using SoleLogics: TruthValue - -# A modal dataset can be *active* or *passive*. -# -# A passive modal dataset is one that you can interpret decisions on, but cannot necessarily -# enumerate decisions for, as it doesn't have objects for storing the logic (relations, features, etc.). -# Dimensional datasets are passive. - -struct PassiveDimensionalDataset{ - N, - W<:AbstractWorld, - DOM<:AbstractDimensionalDataset, - FR<:AbstractDimensionalFrame{N,W}, -} <: AbstractConditionalDataset{W,AbstractCondition,Bool,FR} # TODO remove AbstractCondition. Note: truth value could by different - - d::DOM - - function PassiveDimensionalDataset{N,W,DOM,FR}( - d::DOM, - ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset,FR<:AbstractDimensionalFrame{N,W}} - ty = "PassiveDimensionalDataset{$(N),$(W),$(DOM),$(FR)}" - @assert N == dimensionality(d) "ERROR! Dimensionality mismatch: can't instantiate $(ty) with underlying structure $(DOM). $(N) == $(dimensionality(d)) should hold." - @assert SoleLogics.goeswithdim(W, N) "ERROR! Dimensionality mismatch: can't interpret worldtype $(W) on PassiveDimensionalDataset of dimensionality = $(N)" - new{N,W,DOM,FR}(d) - end - - function PassiveDimensionalDataset{N,W,DOM}( - d::DOM, - ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset} - FR = typeof(_frame(d)) - _W = worldtype(FR) - @assert W <: _W "This should hold: $(W) <: $(_W)" - PassiveDimensionalDataset{N,_W,DOM,FR}(d) - end - - function PassiveDimensionalDataset{N,W}( - d::DOM, - ) where {N,W<:AbstractWorld,DOM<:AbstractDimensionalDataset} - PassiveDimensionalDataset{N,W,DOM}(d) - end - - function PassiveDimensionalDataset( - d::AbstractDimensionalDataset, - # worldtype::Type{<:AbstractWorld}, - ) - W = worldtype(_frame(d)) - PassiveDimensionalDataset{dimensionality(d),W}(d) - end -end - -@inline function Base.getindex( - X::PassiveDimensionalDataset{N,W}, - i_instance::Integer, - w::W, - f::AbstractFeature{U}, - args..., -) where {N,W<:AbstractWorld,U} - w_values = interpret_world(w, get_instance(X.d, i_instance)) - computefeature(f, w_values)::U -end - -Base.size(X::PassiveDimensionalDataset) = Base.size(X.d) - -nvariables(X::PassiveDimensionalDataset) = nvariables(X.d) -ninstances(X::PassiveDimensionalDataset) = ninstances(X.d) -channel_size(X::PassiveDimensionalDataset) = channel_size(X.d) -max_channel_size(X::PassiveDimensionalDataset) = max_channel_size(X.d) -dimensionality(X::PassiveDimensionalDataset) = dimensionality(X.d) -eltype(X::PassiveDimensionalDataset) = eltype(X.d) - -get_instance(X::PassiveDimensionalDataset, args...) = get_instance(X.d, args...) - -instances(X::PassiveDimensionalDataset{N,W}, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) where {N,W} = - PassiveDimensionalDataset{N,W}(instances(X.d, inds, return_view)) - -hasnans(X::PassiveDimensionalDataset) = hasnans(X.d) - -worldtype(X::PassiveDimensionalDataset{N,W}) where {N,W} = W - -frame(X::PassiveDimensionalDataset, i_instance) = _frame(X.d, i_instance) - -############################################################################################ - -_frame(X::Union{UniformDimensionalDataset,AbstractArray}, i_instance) = _frame(X) -_frame(X::Union{UniformDimensionalDataset,AbstractArray}) = FullDimensionalFrame(channel_size(X)) diff --git a/src/dimensional-datasets/datasets/supported-featured-dataset.jl b/src/dimensional-datasets/datasets/supported-featured-dataset.jl deleted file mode 100644 index fac189f..0000000 --- a/src/dimensional-datasets/datasets/supported-featured-dataset.jl +++ /dev/null @@ -1,192 +0,0 @@ - -############################################################################################ -# Explicit modal dataset with support -########################################################################################### - -# The lookup table (fwd) in a featured modal dataset provides a quick answer on the truth of -# propositional decisions; as for answering modal decisions (e.g., ⟨L⟩ (minimum(A2) ≥ 10) ) -# with an fwd, one must enumerate the accessible worlds, compute the truth on each world, -# and aggregate the answer (by means of all/any). This process is costly; instead, it is -# sometimes more convenient to initially spend more time computing the truth of any decision, -# and store this information in a *support* lookup table. Similarly, one can decide to deploy -# memoization on this table (instead of computing everything at the beginning, compute it on -# the fly and store it for later calls). -# -# We define an abstract type for explicit modal dataset with support lookup tables -# remove: abstract type ExplicitModalDatasetWithSupport{V,W,FR} <: AbstractActiveFeaturedDataset{V,W,FR,FT} end -# And an abstract type for support lookup tables -abstract type AbstractSupport{V,W} end - -function nonnothingshare(support::AbstractSupport) - (isinf(capacity(support)) ? NaN : nmemoizedvalues(support)/capacity(support)) -end -# -# In general, one can use lookup (with or without memoization) for any decision, even the -# more complex ones, for example: -# ⟨G⟩ (minimum(A2) ≥ 10 ∧ (⟨O⟩ (maximum(A3) > 2) ∨ (minimum(A1) < 0))) -# -# In practice, decision trees only ask about simple decisions such as ⟨L⟩ (minimum(A2) ≥ 10), -# or ⟨G⟩ (maximum(A2) ≤ 50). Because the global operator G behaves differently from other -# relations, it is natural to differentiate between global and relational support tables: -# -abstract type AbstractRelationalSupport{V,W,FR<:AbstractFrame} <: AbstractSupport{V,W} end -abstract type AbstractGlobalSupport{V} <: AbstractSupport{V,W where W<:AbstractWorld} end -# -# Be an *fwd_rs* an fwd relational support, and a *fwd_gs* an fwd global support, -# for simple support tables like these, it is convenient to store, again, modal *gamma* values. -# Similarly to fwd, gammas are basically values on the verge of truth, that can straightforwardly -# anser simple modal questions. -# Consider the decision (w ⊨ f ⋈ a) on the i-th instance, for a given feature f, -# world w, relation R and test operator ⋈, and let gamma (γ) be: -# - fwd_rs[i, f, a, R, w] if R is a regular relation, or -# - fwd_gs[i, f, a] if R is the global relation G, -# where a = aggregator(⋈). In this context, γ is the unique value for which w ⊨ f ⋈ γ holds and: -# - if aggregator(⋈) = minimum: ∀ a > γ: (w ⊨ f ⋈ a) does not hold -# - if aggregator(⋈) = maximum: ∀ a < γ: (w ⊨ f ⋈ a) does not hold -# -# Let us define the world type-agnostic implementations for fwd_rs and fwd_gs (note that any fwd_gs -# is actually inherently world agnostic); world type-specific implementations can be defined -# in a similar way. - -############################################################################################ -############################################################################################ - -isminifiable(::Union{AbstractRelationalSupport,AbstractGlobalSupport}) = true - -function minify(support::Union{AbstractRelationalSupport,AbstractGlobalSupport}) - minify(support.d) #TODO improper -end - -############################################################################################ -# Finally, let us define the implementation for explicit modal dataset with support -############################################################################################ - - -struct SupportedFeaturedDataset{ - V<:Number, - W<:AbstractWorld, - FR<:AbstractFrame{W,Bool}, - FT<:AbstractFeature{V}, - S<:FeaturedSupportingDataset{V,W,FR}, -} <: AbstractActiveFeaturedDataset{V,W,FR,FT} - - # Core dataset - fd :: FeaturedDataset{V,W,FR,FT} - - # Support structure - support :: S - - ######################################################################################## - - function SupportedFeaturedDataset{V,W,FR,FT,S}( - fd :: FeaturedDataset{V,W,FR,FT}, - support :: S; - allow_no_instances = false, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FT<:AbstractFeature{V},S<:FeaturedSupportingDataset{V,W,FR}} - ty = "SupportedFeaturedDataset{$(V),$(W),$(FR),$(FT),$(S)}" - @assert allow_no_instances || ninstances(fd) > 0 "Can't instantiate $(ty) with no instance." - @assert checksupportconsistency(fd, support) "Can't instantiate $(ty) with an inconsistent support:\n\nemd:\n$(display_structure(fd))\n\nsupport:\n$(display_structure(support))" - new{V,W,FR,FT,S}(fd, support) - end - - function SupportedFeaturedDataset{V,W,FR,FT}(fd::FeaturedDataset{V,W}, support::S, args...; kwargs...) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FT<:AbstractFeature{V},S<:FeaturedSupportingDataset{V,W,FR}} - SupportedFeaturedDataset{V,W,FR,FT,S}(fd, support, args...; kwargs...) - end - - function SupportedFeaturedDataset{V,W,FR}(fd::FeaturedDataset{V,W,FR,FT}, args...; kwargs...) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool},FT<:AbstractFeature{V}} - SupportedFeaturedDataset{V,W,FR,FT}(fd, args...; kwargs...) - end - - function SupportedFeaturedDataset{V,W}(fd::FeaturedDataset{V,W,FR}, args...; kwargs...) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - SupportedFeaturedDataset{V,W,FR}(fd, args...; kwargs...) - end - - function SupportedFeaturedDataset{V}(fd::FeaturedDataset{V,W,FR}, args...; kwargs...) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - SupportedFeaturedDataset{V,W}(fd, args...; kwargs...) - end - - function SupportedFeaturedDataset(fd::FeaturedDataset{V,W,FR}, args...; kwargs...) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - SupportedFeaturedDataset{V}(fd, args...; kwargs...) - end - - ######################################################################################## - - function SupportedFeaturedDataset( - fd :: FeaturedDataset{V,W,FR}; - compute_relation_glob :: Bool = true, - use_memoization :: Bool = true, - ) where {V,W<:AbstractWorld,FR<:AbstractFrame{W,Bool}} - - support = OneStepFeaturedSupportingDataset( - fd, - compute_relation_glob = compute_relation_glob, - use_memoization = use_memoization - ); - - SupportedFeaturedDataset(fd, support) - end - - ######################################################################################## - - function SupportedFeaturedDataset( - X :: DimensionalFeaturedDataset{V,N,W}; - kwargs..., - ) where {V,N,W<:AbstractWorld} - SupportedFeaturedDataset(FeaturedDataset(X); kwargs...) - end - -end - -fd(X::SupportedFeaturedDataset) = X.fd -support(X::SupportedFeaturedDataset) = X.support - -Base.getindex(X::SupportedFeaturedDataset, args...) = Base.getindex(fd(X), args...)::featvaltype(X) -Base.size(X::SupportedFeaturedDataset) = (size(fd(X)), size(support(X))) -features(X::SupportedFeaturedDataset) = features(fd(X)) -grouped_featsaggrsnops(X::SupportedFeaturedDataset) = grouped_featsaggrsnops(fd(X)) -grouped_featsnaggrs(X::SupportedFeaturedDataset) = grouped_featsnaggrs(fd(X)) -nfeatures(X::SupportedFeaturedDataset) = nfeatures(fd(X)) -nrelations(X::SupportedFeaturedDataset) = nrelations(fd(X)) -ninstances(X::SupportedFeaturedDataset) = ninstances(fd(X)) -relations(X::SupportedFeaturedDataset) = relations(fd(X)) -fwd(X::SupportedFeaturedDataset) = fwd(fd(X)) -worldtype(X::SupportedFeaturedDataset{V,W}) where {V,W} = W - -usesmemo(X::SupportedFeaturedDataset) = usesmemo(support(X)) - -frame(X::SupportedFeaturedDataset, i_instance) = frame(fd(X), i_instance) -initialworld(X::SupportedFeaturedDataset, args...) = initialworld(fd(X), args...) - -function instances(X::SupportedFeaturedDataset, inds::AbstractVector{<:Integer}, return_view::Union{Val{true},Val{false}} = Val(false)) - SupportedFeaturedDataset( - instances(fd(X), inds, return_view), - instances(support(X), inds, return_view), - ) -end - -hasnans(X::SupportedFeaturedDataset) = hasnans(fd(X)) || hasnans(support(X)) - -isminifiable(X::SupportedFeaturedDataset) = isminifiable(fd(X)) && isminifiable(fd(X)) - -function minify(X::EMD) where {EMD<:SupportedFeaturedDataset} - (new_emd, new_support), backmap = - minify([ - fd(X), - support(X), - ]) - - X = EMD( - new_emd, - new_support, - ) - X, backmap -end - -function display_structure(X::SupportedFeaturedDataset; indent_str = "") - out = "$(typeof(X))\t$((Base.summarysize(fd(X)) + Base.summarysize(support(X))) / 1024 / 1024 |> x->round(x, digits=2)) MBs\n" - out *= indent_str * "├ relations: \t$((length(relations(fd(X)))))\t$(relations(fd(X)))\n" - out *= indent_str * "├ fd\t$(Base.summarysize(fd(X)) / 1024 / 1024 |> x->round(x, digits=2)) MBs" - out *= "\t(shape $(Base.size(fd(X))))\n" - out *= indent_str * "└ support: $(display_structure(support(X); indent_str = " "))" - out -end diff --git a/src/dimensional-datasets/main.jl b/src/dimensional-datasets/main.jl deleted file mode 100644 index 04d83d5..0000000 --- a/src/dimensional-datasets/main.jl +++ /dev/null @@ -1,66 +0,0 @@ -module DimensionalDatasets - -import SoleLogics: worldtype - -using SoleModels.utils - -# Feature brackets -const UVF_OPENING_BRACKET = "[" -const UVF_CLOSING_BRACKET = "]" -# Default prefix for variables -const UVF_VARPREFIX = "V" - -export UnivariateMin, UnivariateMax, - UnivariateSoftMin, UnivariateSoftMax, - MultivariateFeature - -# Features for dimensional datasets -include("dimensional-features.jl") - -export parsecondition - -# Conditions on features for dimensional datasets -include("parse-dimensional-condition.jl") - -# Concrete type for ontologies -include("ontology.jl") # TODO frame inside the ontology? - -export DimensionalFeaturedDataset, FeaturedDataset, SupportedFeaturedDataset - -# Dataset structures -include("datasets/main.jl") - -const GenericModalDataset = Union{AbstractDimensionalDataset,AbstractConditionalDataset,MultiFrameConditionalDataset} - -# TODO? -include("gamma-access.jl") - -# Dimensional ontologies -include("dimensional-ontologies.jl") - -using SoleLogics: Full0DFrame, Full1DFrame, Full2DFrame -using SoleLogics: X, Y, Z - -# Representatives for dimensional modalities -include("representatives/Full0DFrame.jl") -include("representatives/Full1DFrame.jl") -include("representatives/Full1DFrame+IA.jl") -include("representatives/Full1DFrame+RCC.jl") -include("representatives/Full2DFrame.jl") - -_st_featop_abbr(f::UnivariateMin, ::typeof(≥); kwargs...) = "$(attribute_name(f; kwargs...)) ⪴" -_st_featop_abbr(f::UnivariateMax, ::typeof(≤); kwargs...) = "$(attribute_name(f; kwargs...)) ⪳" -_st_featop_abbr(f::UnivariateSoftMin, ::typeof(≥); kwargs...) = "$(attribute_name(f; kwargs...)) $("⪴" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" -_st_featop_abbr(f::UnivariateSoftMax, ::typeof(≤); kwargs...) = "$(attribute_name(f; kwargs...)) $("⪳" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" - -_st_featop_abbr(f::UnivariateMin, ::typeof(<); kwargs...) = "$(attribute_name(f; kwargs...)) ⪶" -_st_featop_abbr(f::UnivariateMax, ::typeof(>); kwargs...) = "$(attribute_name(f; kwargs...)) ⪵" -_st_featop_abbr(f::UnivariateSoftMin, ::typeof(<); kwargs...) = "$(attribute_name(f; kwargs...)) $("⪶" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" -_st_featop_abbr(f::UnivariateSoftMax, ::typeof(>); kwargs...) = "$(attribute_name(f; kwargs...)) $("⪵" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" - -_st_featop_abbr(f::UnivariateMin, ::typeof(≤); kwargs...) = "$(attribute_name(f; kwargs...)) ↘" -_st_featop_abbr(f::UnivariateMax, ::typeof(≥); kwargs...) = "$(attribute_name(f; kwargs...)) ↗" -_st_featop_abbr(f::UnivariateSoftMin, ::typeof(≤); kwargs...) = "$(attribute_name(f; kwargs...)) $("↘" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" -_st_featop_abbr(f::UnivariateSoftMax, ::typeof(≥); kwargs...) = "$(attribute_name(f; kwargs...)) $("↗" * utils.subscriptnumber(rstrip(rstrip(string(alpha(f)*100), '0'), '.')))" - -end From a7b3162e2494ead59c165c0ea550622b6db52b90 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Fri, 4 Aug 2023 03:14:54 +0200 Subject: [PATCH 62/77] Fix dependencies. Add canonicals --- Project.toml | 4 ++-- TODO | 4 ++-- src/logisets/main.jl | 2 +- src/logisets/scalar/dataset-bindings.jl | 24 ++++++++---------------- src/logisets/scalar/main.jl | 18 +++++++++++++----- src/logisets/supported-logiset.jl | 9 --------- 6 files changed, 26 insertions(+), 35 deletions(-) diff --git a/Project.toml b/Project.toml index 136119f..75a07bf 100644 --- a/Project.toml +++ b/Project.toml @@ -36,7 +36,7 @@ ZipFile = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea" BenchmarkTools = "1" CSV = "0.10.11" CategoricalArrays = "0.10.8" -DataFrames = "1.6.0" +DataFrames = "1.3" DataStructures = "0.18" FillArrays = "1" FunctionWrappers = "1" @@ -51,7 +51,7 @@ Revise = "3" SoleBase = "0.11" SoleData = "0.10" SoleLogics = "0.4" -StatsBase = "0.34" +StatsBase = "0.33" Suppressor = "0.2" Tables = "1.10.1" ThreadSafeDicts = "0.1.0" diff --git a/TODO b/TODO index ef65af6..062fbe6 100644 --- a/TODO +++ b/TODO @@ -1,2 +1,2 @@ -- struct EnsambleModel (with parametrized aggregation) and DecisionForest -- PatchModel, a closed model providing a default consequent to an open model +☐ struct EnsambleModel (with parametrized aggregation) and DecisionForest +☐ PatchModel, a closed model providing a default consequent to an open model diff --git a/src/logisets/main.jl b/src/logisets/main.jl index 91894f6..6f75b66 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -53,7 +53,7 @@ export nfeatures include("scalar/main.jl") -# Tables interface for Logiset's, so that it can be integrated with MLJ +# Tables interface for logiset's, so that it can be integrated with MLJ include("MLJ-interface.jl") export initlogiset, ninstances, maxchannelsize, worldtype, dimensionality, allworlds, featvalue diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 7560822..cde01e1 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -234,14 +234,6 @@ function scalarlogiset( end end - -const MixedCondition = Union{ - <:Union{SoleModels.VarFeature,Base.Callable}, # feature (i.e., callables to be associated to all variables, or SoleModels.VarFeature objects); - <:Tuple{Base.Callable,Integer}, # (callable,var_id); - <:Tuple{TestOperator,<:Union{SoleModels.VarFeature,Base.Callable}}, # (test_operator,features); - <:ScalarMetaCondition, # ScalarMetaCondition; -} - function naturalconditions( dataset, mixed_conditions :: AbstractVector, @@ -260,30 +252,30 @@ function naturalconditions( def_test_operators = is_propositional_dataset ? [≥] : [≥, <] - # univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeq) = ([≥],DimensionalDatasets.UnivariateMin{featvaltype}(i_var)) - # univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeq) = ([<],DimensionalDatasets.UnivariateMax{featvaltype}(i_var)) - # univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeqSoft) = ([≥],DimensionalDatasets.UnivariateSoftMin{featvaltype}(i_var, cond.alpha)) - # univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeqSoft) = ([<],DimensionalDatasets.UnivariateSoftMax{featvaltype}(i_var, cond.alpha)) + univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeq) = ([≥],UnivariateMin{featvaltype}(i_var)) + univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeq) = ([<],UnivariateMax{featvaltype}(i_var)) + univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeqSoft) = ([≥],UnivariateSoftMin{featvaltype}(i_var, cond.alpha)) + univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeqSoft) = ([<],UnivariateSoftMax{featvaltype}(i_var, cond.alpha)) function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},typeof(identity)}) V = vareltype(dataset, i_var) if !isconcretetype(V) @warn "Building UnivariateValue with non-concrete feature type: $(V)." end - return (test_ops,DimensionalDatasets.UnivariateValue{V}(i_var)) + return (test_ops,UnivariateValue{V}(i_var)) end function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},typeof(minimum)}) V = vareltype(dataset, i_var) if !isconcretetype(V) @warn "Building UnivariateMin with non-concrete feature type: $(V)." end - return (test_ops,DimensionalDatasets.UnivariateMin{V}(i_var)) + return (test_ops,UnivariateMin{V}(i_var)) end function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},typeof(maximum)}) V = vareltype(dataset, i_var) if !isconcretetype(V) @warn "Building UnivariateMax with non-concrete feature type: $(V)." end - return (test_ops,DimensionalDatasets.UnivariateMax{V}(i_var)) + return (test_ops,UnivariateMax{V}(i_var)) end function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},Base.Callable}) V = featvaltype @@ -293,7 +285,7 @@ function naturalconditions( end # f = function (x) return V(cond(x)) end # breaks because it does not create a closure. f = cond - return (test_ops,DimensionalDatasets.UnivariateFeature{V}(i_var, f)) + return (test_ops,UnivariateFeature{V}(i_var, f)) end univar_condition(i_var,::Any) = throw_n_log("Unknown mixed_feature type: $(cond), $(typeof(cond))") diff --git a/src/logisets/scalar/main.jl b/src/logisets/scalar/main.jl index 7026a9e..469ffee 100644 --- a/src/logisets/scalar/main.jl +++ b/src/logisets/scalar/main.jl @@ -15,12 +15,20 @@ include("random.jl") include("representatives.jl") -include("dataset-bindings.jl") - # # Types for representing common associations between features and operators -# include("canonical-conditions.jl") TODO remove -# const MixedCondition = Union{AbstractFeature,CanonicalFeature,Function,Tuple{TestOperator,Function},Tuple{TestOperator,AbstractFeature}} -# Union{AbstractFeature,CanonicalFeature,Function,Tuple{TestOperator,Function},Tuple{TestOperator,AbstractFeature}} +include("canonical-conditions.jl") # TODO remove + +const MixedCondition = Union{ + CanonicalFeature, + # + <:SoleModels.AbstractFeature, # feature + <:Base.Callable, # feature function (i.e., callables to be associated to all variables); + <:Tuple{Base.Callable,Integer}, # (callable,var_id); + <:Tuple{TestOperator,<:Union{SoleModels.AbstractFeature,Base.Callable}}, # (test_operator,features); + <:ScalarMetaCondition, # ScalarMetaCondition; +} + +include("dataset-bindings.jl") include("memosets.jl") diff --git a/src/logisets/supported-logiset.jl b/src/logisets/supported-logiset.jl index a758b82..38b5aa6 100644 --- a/src/logisets/supported-logiset.jl +++ b/src/logisets/supported-logiset.jl @@ -162,15 +162,6 @@ struct SupportedLogiset{ SupportedLogiset(base, supports) end - - # TODO Helper - # function SupportedLogiset( - # X :: ... AbstractScalarLogiset{W,V,FT,FR}; - # kwargs..., - # ) where {V,FT<:AbstractFeature,W<:AbstractWorld,FR<:AbstractFrame{W}} - # SupportedLogiset(Logiset(X); kwargs...) - # end - end base(X::SupportedLogiset) = X.base From 04b1e55d8eb6835a7d7fa5fb1867a1e4b302cbd4 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sat, 5 Aug 2023 00:26:26 +0200 Subject: [PATCH 63/77] CanonicalFeature->CanonicalCondition, fix dataset bindings --- src/SoleModels.jl | 2 +- .../dataset-bindings.jl | 16 ++- .../representatives/Full1DFrame+RCC.jl | 12 +- src/logisets/scalar/canonical-conditions.jl | 46 ++++---- src/logisets/scalar/dataset-bindings.jl | 104 +++++++++++++++--- src/logisets/scalar/main.jl | 2 +- 6 files changed, 132 insertions(+), 50 deletions(-) diff --git a/src/SoleModels.jl b/src/SoleModels.jl index b537d7b..fa8e43b 100644 --- a/src/SoleModels.jl +++ b/src/SoleModels.jl @@ -60,7 +60,7 @@ export initlogiset, maxchannelsize, worldtype, dimensionality, frame, featvalue, export ScalarMetaCondition -export MixedCondition, CanonicalFeature, canonical_geq, canonical_leq +export MixedCondition, CanonicalCondition, canonical_geq, canonical_leq export canonical_geq_95, canonical_geq_90, canonical_geq_85, canonical_geq_80, canonical_geq_75, canonical_geq_70, canonical_geq_60, canonical_leq_95, canonical_leq_90, canonical_leq_85, canonical_leq_80, canonical_leq_75, canonical_leq_70, canonical_leq_60 diff --git a/src/logisets/dimensional-structures/dataset-bindings.jl b/src/logisets/dimensional-structures/dataset-bindings.jl index 99c1f23..a1b55ea 100644 --- a/src/logisets/dimensional-structures/dataset-bindings.jl +++ b/src/logisets/dimensional-structures/dataset-bindings.jl @@ -5,10 +5,16 @@ using SoleData: AbstractDimensionalDataset, import SoleData: ninstances, nvariables -import SoleModels: scalarlogiset, - initlogiset, initlogiset, frame, +import SoleModels: + islogiseed, initlogiset, frame, featchannel, readfeature, featvalue, vareltype +function islogiseed( + dataset::AbstractDimensionalDataset, +) + true +end + function initlogiset( dataset::AbstractDimensionalDataset, features::AbstractVector{<:VarFeature}, @@ -92,6 +98,12 @@ end using DataFrames +function islogiseed( + dataset::AbstractDataFrame, +) + true +end + function initlogiset( dataset::AbstractDataFrame, features::AbstractVector{<:VarFeature}, diff --git a/src/logisets/dimensional-structures/representatives/Full1DFrame+RCC.jl b/src/logisets/dimensional-structures/representatives/Full1DFrame+RCC.jl index dba70f7..4acdb42 100644 --- a/src/logisets/dimensional-structures/representatives/Full1DFrame+RCC.jl +++ b/src/logisets/dimensional-structures/representatives/Full1DFrame+RCC.jl @@ -3,17 +3,17 @@ #= -computeModalThresholdDual(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::AbstractArray{T,1}) where {T} = begin +computeModalThresholdDual(test_operator::CanonicalConditionGeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::AbstractArray{T,1}) where {T} = begin maxExtrema( map((IA_r)->(yieldReprs(test_operator, enum_acc_repr(test_operator, w, IA_r, length(channel)), channel)), topo2IARelations(r)) ) end -apply_aggregator(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::AbstractArray{T,1}) where {T} = begin +apply_aggregator(test_operator::CanonicalConditionGeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::AbstractArray{T,1}) where {T} = begin maximum( map((IA_r)->(yieldRepr(test_operator, enum_acc_repr(test_operator, w, IA_r, length(channel)), channel)), topo2IARelations(r)) ) end -apply_aggregator(test_operator::CanonicalFeatureLeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::AbstractArray{T,1}) where {T} = begin +apply_aggregator(test_operator::CanonicalConditionLeq, w::Interval{Int}?, r::RCC8RelationFromIA, channel::AbstractArray{T,1}) where {T} = begin mininimum( map((IA_r)->(yieldRepr(test_operator, enum_acc_repr(test_operator, w, IA_r, length(channel)), channel)), topo2IARelations(r)) ) @@ -22,17 +22,17 @@ end enum_acc_repr(fr::Full1DFrame, test_operator::TestOperator, w::Interval{Int}?, ::_Topo_NTPP,) = enum_acc_repr(test_operator, fr, w, IA_D) enum_acc_repr(fr::Full1DFrame, test_operator::TestOperator, w::Interval{Int}?, ::_Topo_NTPPi,) = enum_acc_repr(test_operator, fr, w, IA_Di) -computeModalThresholdDual(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC5Relation, channel::AbstractArray{T,1}) where {T} = begin +computeModalThresholdDual(test_operator::CanonicalConditionGeq, w::Interval{Int}?, r::RCC5Relation, channel::AbstractArray{T,1}) where {T} = begin maxExtrema( map((IA_r)->(yieldReprs(test_operator, enum_acc_repr(test_operator, w, IA_r, size(channel)...), channel)), [IA_r for RCC8_r in RCC52RCC8Relations(r) for IA_r in topo2IARelations(RCC8_r)]) ) end -apply_aggregator(test_operator::CanonicalFeatureGeq, w::Interval{Int}?, r::RCC5Relation, channel::AbstractArray{T,1}) where {T} = begin +apply_aggregator(test_operator::CanonicalConditionGeq, w::Interval{Int}?, r::RCC5Relation, channel::AbstractArray{T,1}) where {T} = begin maximum( map((IA_r)->(yieldRepr(test_operator, enum_acc_repr(test_operator, w, IA_r, size(channel)...), channel)), [IA_r for RCC8_r in RCC52RCC8Relations(r) for IA_r in topo2IARelations(RCC8_r)]) ) end -apply_aggregator(test_operator::CanonicalFeatureLeq, w::Interval{Int}?, r::RCC5Relation, channel::AbstractArray{T,1}) where {T} = begin +apply_aggregator(test_operator::CanonicalConditionLeq, w::Interval{Int}?, r::RCC5Relation, channel::AbstractArray{T,1}) where {T} = begin mininimum( map((IA_r)->(yieldRepr(test_operator, enum_acc_repr(test_operator, w, IA_r, size(channel)...), channel)), [IA_r for RCC8_r in RCC52RCC8Relations(r) for IA_r in topo2IARelations(RCC8_r)]) ) diff --git a/src/logisets/scalar/canonical-conditions.jl b/src/logisets/scalar/canonical-conditions.jl index 8ccfc45..14d9961 100644 --- a/src/logisets/scalar/canonical-conditions.jl +++ b/src/logisets/scalar/canonical-conditions.jl @@ -1,43 +1,43 @@ -abstract type CanonicalFeature end +abstract type CanonicalCondition end # ⪴ and ⪳, that is, "*all* of the values on this world are at least, or at most ..." -struct CanonicalFeatureGeq <: CanonicalFeature end; const canonical_geq = CanonicalFeatureGeq(); -struct CanonicalFeatureLeq <: CanonicalFeature end; const canonical_leq = CanonicalFeatureLeq(); +struct CanonicalConditionGeq <: CanonicalCondition end; const canonical_geq = CanonicalConditionGeq(); +struct CanonicalConditionLeq <: CanonicalCondition end; const canonical_leq = CanonicalConditionLeq(); # ⪴_α and ⪳_α, that is, "*at least α⋅100 percent* of the values on this world are at least, or at most ..." -struct CanonicalFeatureGeqSoft <: CanonicalFeature +struct CanonicalConditionGeqSoft <: CanonicalCondition alpha :: AbstractFloat - function CanonicalFeatureGeqSoft(a::T) where {T<:Real} + function CanonicalConditionGeqSoft(a::T) where {T<:Real} if ! (a > 0 && a < 1) - error("Invalid instantiation of feature: CanonicalFeatureGeqSoft($(a))") + error("Invalid instantiation of feature: CanonicalConditionGeqSoft($(a))") end new(a) end end; -struct CanonicalFeatureLeqSoft <: CanonicalFeature +struct CanonicalConditionLeqSoft <: CanonicalCondition alpha :: AbstractFloat - function CanonicalFeatureLeqSoft(a::T) where {T<:Real} + function CanonicalConditionLeqSoft(a::T) where {T<:Real} if ! (a > 0 && a < 1) - error("Invalid instantiation of feature: CanonicalFeatureLeqSoft($(a))") + error("Invalid instantiation of feature: CanonicalConditionLeqSoft($(a))") end new(a) end end; -const canonical_geq_95 = CanonicalFeatureGeqSoft((Rational(95,100))); -const canonical_geq_90 = CanonicalFeatureGeqSoft((Rational(90,100))); -const canonical_geq_85 = CanonicalFeatureGeqSoft((Rational(85,100))); -const canonical_geq_80 = CanonicalFeatureGeqSoft((Rational(80,100))); -const canonical_geq_75 = CanonicalFeatureGeqSoft((Rational(75,100))); -const canonical_geq_70 = CanonicalFeatureGeqSoft((Rational(70,100))); -const canonical_geq_60 = CanonicalFeatureGeqSoft((Rational(60,100))); +const canonical_geq_95 = CanonicalConditionGeqSoft((Rational(95,100))); +const canonical_geq_90 = CanonicalConditionGeqSoft((Rational(90,100))); +const canonical_geq_85 = CanonicalConditionGeqSoft((Rational(85,100))); +const canonical_geq_80 = CanonicalConditionGeqSoft((Rational(80,100))); +const canonical_geq_75 = CanonicalConditionGeqSoft((Rational(75,100))); +const canonical_geq_70 = CanonicalConditionGeqSoft((Rational(70,100))); +const canonical_geq_60 = CanonicalConditionGeqSoft((Rational(60,100))); -const canonical_leq_95 = CanonicalFeatureLeqSoft((Rational(95,100))); -const canonical_leq_90 = CanonicalFeatureLeqSoft((Rational(90,100))); -const canonical_leq_85 = CanonicalFeatureLeqSoft((Rational(85,100))); -const canonical_leq_80 = CanonicalFeatureLeqSoft((Rational(80,100))); -const canonical_leq_75 = CanonicalFeatureLeqSoft((Rational(75,100))); -const canonical_leq_70 = CanonicalFeatureLeqSoft((Rational(70,100))); -const canonical_leq_60 = CanonicalFeatureLeqSoft((Rational(60,100))); +const canonical_leq_95 = CanonicalConditionLeqSoft((Rational(95,100))); +const canonical_leq_90 = CanonicalConditionLeqSoft((Rational(90,100))); +const canonical_leq_85 = CanonicalConditionLeqSoft((Rational(85,100))); +const canonical_leq_80 = CanonicalConditionLeqSoft((Rational(80,100))); +const canonical_leq_75 = CanonicalConditionLeqSoft((Rational(75,100))); +const canonical_leq_70 = CanonicalConditionLeqSoft((Rational(70,100))); +const canonical_leq_60 = CanonicalConditionLeqSoft((Rational(60,100))); diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index cde01e1..ec99e12 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -1,9 +1,10 @@ using ProgressMeter using SoleData: AbstractMultiModalDataset -import SoleData: ninstances, nvariables, nmodalities, eachmodality +import SoleData: ninstances, nvariables, nmodalities, eachmodality, displaystructure function islogiseed(dataset) - return error("Please, provide method islogiseed(dataset::$(typeof(dataset))).") + false + # return error("Please, provide method islogiseed(dataset::$(typeof(dataset))).") end function initlogiset(dataset, features) return error("Please, provide method initlogiset(dataset::$(typeof(dataset)), features::$(typeof(features))).") @@ -39,6 +40,10 @@ function eachmodality(dataset) return error("Please, provide method eachmodality(dataset::$(typeof(dataset))).") end +function modality(dataset, i_modality) + eachmodality(dataset)[i_modality] +end + function ismultilogiseed(dataset::MultiLogiset) true end @@ -47,14 +52,49 @@ function ismultilogiseed(dataset::AbstractMultiModalDataset) end function ismultilogiseed(dataset::Union{AbstractVector,Tuple}) - true + all(islogiseed, dataset) # && allequal(ninstances, eachmodality(dataset)) end function nmodalities(dataset::Union{AbstractVector,Tuple}) + @assert ismultilogiseed(dataset) "$(typeof(dataset))" length(dataset) end function eachmodality(dataset::Union{AbstractVector,Tuple}) + # @assert ismultilogiseed(dataset) "$(typeof(dataset))" dataset end +function ninstances(dataset::Union{AbstractVector,Tuple}) + @assert ismultilogiseed(dataset) "$(typeof(dataset))" + ninstances(first(dataset)) +end + +function displaystructure(dataset; indent_str = "", include_ninstances = true, kwargs...) + if ismultilogiseed(dataset) + pieces = [] + push!(pieces, "multilogiseed with $(nmodalities(dataset)) modalities ($(humansize(dataset)))") + # push!(pieces, indent_str * "├ # modalities:\t$(nmodalities(dataset))") + if include_ninstances + push!(pieces, indent_str * "├ # instances:\t$(ninstances(dataset))") + end + # push!(pieces, indent_str * "├ modalitytype:\t$(modalitytype(dataset))") + for (i_modality, mod) in enumerate(eachmodality(dataset)) + out = "" + if i_modality == nmodalities(dataset) + out *= "$(indent_str)└" + else + out *= "$(indent_str)├" + end + out *= "{$i_modality} " + # \t\t\t$(humansize(mod))\t(worldtype: $(worldtype(mod)))" + out *= displaystructure(mod; indent_str = indent_str * (i_modality == nmodalities(dataset) ? " " : "│ "), include_ninstances = false, kwargs...) + push!(pieces, out) + end + return join(pieces, "\n") + elseif islogiseed(dataset) + return "logiseed ($(humansize(dataset)))\n$(dataset)" |> x->"$(replace(x, "\n"=>"$(indent_str)\n"))\n" + else + return "?? dataset of type $(typeof(dataset)) ($(humansize(dataset))) ??\n$(dataset)\n" |> x->"$(replace(x, "\n"=>"$(indent_str)\n"))\n" + end +end """ @@ -88,7 +128,7 @@ See also """ function scalarlogiset( dataset, - features::Union{Nothing,AbstractVector{<:VarFeature},AbstractVector{<:Union{Nothing,<:AbstractVector}}} = nothing; + features::Union{Nothing,MixedCondition,AbstractVector{<:VarFeature},AbstractVector{<:Union{Nothing,MixedCondition,<:AbstractVector}}} = nothing; # use_full_memoization :: Union{Bool,Type{<:Union{AbstractOneStepMemoset,AbstractFullMemoset}}} = true, # @@ -98,6 +138,7 @@ function scalarlogiset( onestep_precompute_globmemoset :: Bool = (use_onestep_memoization != false), onestep_precompute_relmemoset :: Bool = false, print_progress :: Bool = false, + # featvaltype = nothing ) some_features_were_specified = !isnothing(features) @@ -111,12 +152,13 @@ function scalarlogiset( ) features = begin - if features isa Union{Nothing,AbstractVector{<:VarFeature}} + if features isa Union{Nothing,MixedCondition,AbstractVector{<:VarFeature}} fill(features, nmodalities(dataset)) - elseif features isa AbstractVector{<:Union{Nothing,AbstractVector}} + elseif features isa AbstractVector{<:Union{Nothing,MixedCondition,AbstractVector}} features else error("Cannot build multimodal scalar logiset with features " * + "$(features), " * "$(displaysyntaxvector(features)).") end end @@ -143,10 +185,25 @@ function scalarlogiset( end end - return MultiLogiset([ - scalarlogiset(_dataset, _features; conditions = _conditions, relations = _relations, kwargs...) - for (_dataset, _features, _conditions, _relations) in - zip(eachmodality(dataset), features, conditions, relations) + if print_progress + p = Progress(nmodalities(dataset), 1, "Computing multilogiset...") + end + return MultiLogiset([begin + # println("Modality $(i_modality)/$(nmodalities(dataset))") + X = scalarlogiset( + _dataset, + _features; + conditions = _conditions, + relations = _relations, + print_progress = false, + kwargs... + ) + if print_progress + next!(p) + end + X + end for (i_modality, (_dataset, _features, _conditions, _relations)) in + enumerate(zip(eachmodality(dataset), features, conditions, relations)) ]) end @@ -163,6 +220,15 @@ function scalarlogiset( unique(feature.(conditions)) end end + else + if isnothing(conditions) + featvaltype = eltype(dataset) + conditions = naturalconditions(dataset, features, featvaltype) + features = unique(feature.(conditions)) + else + error("Unexpected case (TODO)." * + "features = $(typeof(features)), conditions = $(typeof(conditions))") + end end features_ok = filter(f->isconcretetype(SoleModels.featvaltype(f)), features) @@ -237,8 +303,12 @@ end function naturalconditions( dataset, mixed_conditions :: AbstractVector, - featvaltype :: Type = DEFAULT_VARFEATVALTYPE + featvaltype :: Union{Nothing,Type} = nothing ) + if isnothing(featvaltype) + featvaltype = DEFAULT_VARFEATVALTYPE + end + nvars = nvariables(dataset) @assert all(isa.(mixed_conditions, MixedCondition)) "" * @@ -252,10 +322,10 @@ function naturalconditions( def_test_operators = is_propositional_dataset ? [≥] : [≥, <] - univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeq) = ([≥],UnivariateMin{featvaltype}(i_var)) - univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeq) = ([<],UnivariateMax{featvaltype}(i_var)) - univar_condition(i_var,cond::SoleModels.CanonicalFeatureGeqSoft) = ([≥],UnivariateSoftMin{featvaltype}(i_var, cond.alpha)) - univar_condition(i_var,cond::SoleModels.CanonicalFeatureLeqSoft) = ([<],UnivariateSoftMax{featvaltype}(i_var, cond.alpha)) + univar_condition(i_var,cond::SoleModels.CanonicalConditionGeq) = ([≥],UnivariateMin{featvaltype}(i_var)) + univar_condition(i_var,cond::SoleModels.CanonicalConditionLeq) = ([<],UnivariateMax{featvaltype}(i_var)) + univar_condition(i_var,cond::SoleModels.CanonicalConditionGeqSoft) = ([≥],UnivariateSoftMin{featvaltype}(i_var, cond.alpha)) + univar_condition(i_var,cond::SoleModels.CanonicalConditionLeqSoft) = ([<],UnivariateSoftMax{featvaltype}(i_var, cond.alpha)) function univar_condition(i_var,(test_ops,cond)::Tuple{<:AbstractVector{<:TestOperator},typeof(identity)}) V = vareltype(dataset, i_var) if !isconcretetype(V) @@ -297,7 +367,7 @@ function naturalconditions( # single-variable conditions unpackcondition(cond::Any) = cond - # unpackcondition(cond::CanonicalFeature) = cond + # unpackcondition(cond::CanonicalCondition) = cond unpackcondition(cond::Base.Callable) = (def_test_operators, cond) function unpackcondition(cond::Tuple{Base.Callable,Integer}) return univar_condition(cond[2], (def_test_operators, cond[1])) @@ -313,7 +383,7 @@ function naturalconditions( mixed_conditions, ) variable_specific_conditions = filter(x-> - # isa(x, CanonicalFeature) || + isa(x, CanonicalCondition) || # isa(x, Tuple{<:AbstractVector{<:TestOperator},Base.Callable}) || (isa(x, Tuple{AbstractVector,Base.Callable}) && !isa(x, Tuple{AbstractVector,AbstractFeature})), mixed_conditions, diff --git a/src/logisets/scalar/main.jl b/src/logisets/scalar/main.jl index 469ffee..8cbd6a7 100644 --- a/src/logisets/scalar/main.jl +++ b/src/logisets/scalar/main.jl @@ -19,7 +19,7 @@ include("representatives.jl") include("canonical-conditions.jl") # TODO remove const MixedCondition = Union{ - CanonicalFeature, + CanonicalCondition, # <:SoleModels.AbstractFeature, # feature <:Base.Callable, # feature function (i.e., callables to be associated to all variables); From 2b403e4018b36f1574b11afb96579afe91233173 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 6 Aug 2023 00:59:49 +0200 Subject: [PATCH 64/77] Fix Sole deps, minor fix --- Project.toml | 4 ++-- src/logisets/scalar/dataset-bindings.jl | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index 75a07bf..73a25a4 100644 --- a/Project.toml +++ b/Project.toml @@ -49,8 +49,8 @@ ProgressMeter = "1" Reexport = "1" Revise = "3" SoleBase = "0.11" -SoleData = "0.10" -SoleLogics = "0.4" +SoleData = "0.10.1" +SoleLogics = "0.4.8" StatsBase = "0.33" Suppressor = "0.2" Tables = "1.10.1" diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index ec99e12..f049c61 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -379,7 +379,7 @@ function naturalconditions( mixed_conditions = unpackcondition.(mixed_conditions) readymade_conditions = filter(x-> - isa(x, Vector{ScalarMetaCondition}), + isa(x, Vector{<:ScalarMetaCondition}), mixed_conditions, ) variable_specific_conditions = filter(x-> @@ -390,8 +390,8 @@ function naturalconditions( ) @assert length(readymade_conditions) + length(variable_specific_conditions) == length(mixed_conditions) "" * - "Unexpected " * - "mixed_conditions. $(mixed_conditions). " * + "Unexpected mixed_conditions. " * + "$(mixed_conditions). " * "$(filter(x->(! (x in readymade_conditions) && ! (x in variable_specific_conditions)), mixed_conditions)). " * "$(length(readymade_conditions)) + $(length(variable_specific_conditions)) == $(length(mixed_conditions))." From 7bedaeffb4f5f281a0dc86b16fca7b48224cdb96 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 6 Aug 2023 01:00:14 +0200 Subject: [PATCH 65/77] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 73a25a4..d762ef2 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SoleModels" uuid = "4249d9c7-3290-4ddd-961c-e1d3ec2467f8" authors = ["Michele GHIOTTI", "Giovanni PAGLIARINI", "Eduard I. STAN"] -version = "0.2.2" +version = "0.2.3" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" From f29b2271fe919df9fea798c48ac8c771932ae5b3 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 6 Aug 2023 01:55:40 +0200 Subject: [PATCH 66/77] Fix natural conditions, bump --- Project.toml | 2 +- src/logisets/scalar/dataset-bindings.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index d762ef2..3d9a3c5 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SoleModels" uuid = "4249d9c7-3290-4ddd-961c-e1d3ec2467f8" authors = ["Michele GHIOTTI", "Giovanni PAGLIARINI", "Eduard I. STAN"] -version = "0.2.3" +version = "0.2.4" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index f049c61..c1c6cd5 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -396,7 +396,7 @@ function naturalconditions( "$(length(readymade_conditions)) + $(length(variable_specific_conditions)) == $(length(mixed_conditions))." for cond in readymade_conditions - push!(metaconditions, cond) + append!(metaconditions, cond) end for i_var in 1:nvars From 4059141403155a7fe8813495fc5897e3e70d0ca6 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 6 Aug 2023 02:39:04 +0200 Subject: [PATCH 67/77] Fix scalarlogiset --- src/logisets/scalar/dataset-bindings.jl | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index c1c6cd5..a044421 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -98,13 +98,13 @@ end """ - scalarlogiset(dataset, features::AbstractVector{<:VarFeature}) + scalarlogiset(dataset, features) Converts a dataset structure (with variables) to a logiset with scalar-valued features. If `dataset` is not a multimodal dataset, the following methods should be defined: ```julia - initlogiset(dataset, features::AbstractVector{<:VarFeature}) + initlogiset(dataset, features) ninstances(dataset) nvariables(dataset) frame(dataset, i_instance::Integer) @@ -128,7 +128,7 @@ See also """ function scalarlogiset( dataset, - features::Union{Nothing,MixedCondition,AbstractVector{<:VarFeature},AbstractVector{<:Union{Nothing,MixedCondition,<:AbstractVector}}} = nothing; + features::Union{Nothing,AbstractVector} = nothing; # use_full_memoization :: Union{Bool,Type{<:Union{AbstractOneStepMemoset,AbstractFullMemoset}}} = true, # @@ -140,7 +140,16 @@ function scalarlogiset( print_progress :: Bool = false, # featvaltype = nothing ) - some_features_were_specified = !isnothing(features) + is_feature(features) = (f isa MixedCondition) + is_nofeatures(features) = isnothing(features) + is_unifeatures(features) = features isa AbstractVector && all(f->is_feature(f), features) + is_multifeatures(features) = features isa AbstractVector && all(fs->(is_nofeatures(fs) || is_unifeatures(fs)), features) + + @assert (is_nofeatures(features) || + is_unifeatures(features) || + is_multifeatures(features)) "Unexpected features (type: $(typeof(features))).\n" * + "$(features)" * + "Suspects: $(filter(f->!is_feature(f) && !is_nofeatures(f) && !is_unifeatures(f)), features))" if ismultilogiseed(dataset) @@ -152,9 +161,9 @@ function scalarlogiset( ) features = begin - if features isa Union{Nothing,MixedCondition,AbstractVector{<:VarFeature}} + if is_unifeatures(features) || is_nofeatures(features) fill(features, nmodalities(dataset)) - elseif features isa AbstractVector{<:Union{Nothing,MixedCondition,AbstractVector}} + elseif is_multifeatures(features) features else error("Cannot build multimodal scalar logiset with features " * @@ -240,7 +249,7 @@ function scalarlogiset( U = vareltype(dataset, i_variable(f)) eval(nameof(typeof(f))){U}(f) end for f in features_notok] - if some_features_were_specified + if !is_nofeatures(features) @warn "Patching $(length(features_notok)) features using vareltype." end features = [features_ok..., features_notok_fixed...] From 77a558416322e2309676e295085082bfb0db882b Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 6 Aug 2023 03:00:56 +0200 Subject: [PATCH 68/77] Fix --- src/logisets/scalar/dataset-bindings.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index a044421..2b755c0 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -142,14 +142,14 @@ function scalarlogiset( ) is_feature(features) = (f isa MixedCondition) is_nofeatures(features) = isnothing(features) - is_unifeatures(features) = features isa AbstractVector && all(f->is_feature(f), features) - is_multifeatures(features) = features isa AbstractVector && all(fs->(is_nofeatures(fs) || is_unifeatures(fs)), features) + is_unifeatures(features) = (features isa AbstractVector && all(f->is_feature(f), features)) + is_multifeatures(features) = (features isa AbstractVector && all(fs->(is_nofeatures(fs) || is_unifeatures(fs)), features)) @assert (is_nofeatures(features) || is_unifeatures(features) || is_multifeatures(features)) "Unexpected features (type: $(typeof(features))).\n" * "$(features)" * - "Suspects: $(filter(f->!is_feature(f) && !is_nofeatures(f) && !is_unifeatures(f)), features))" + "Suspects: $(filter(f->(!is_feature(f) && !is_nofeatures(f) && !is_unifeatures(f)), features))" if ismultilogiseed(dataset) From 71ccb0b4185c7df9ae9bf91483fc7fd5f502fb60 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 6 Aug 2023 03:14:13 +0200 Subject: [PATCH 69/77] Fix naturalconditions --- src/logisets/multilogiset.jl | 6 ++++-- src/logisets/scalar/dataset-bindings.jl | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/logisets/multilogiset.jl b/src/logisets/multilogiset.jl index 0aaeeb8..4ff824f 100644 --- a/src/logisets/multilogiset.jl +++ b/src/logisets/multilogiset.jl @@ -73,8 +73,10 @@ function instances( end function concatdatasets(Xs::MultiLogiset...) + @assert allequal(nmodalities.(Xs)) "Cannot concatenate MultiLogiset's with mismatching " * + "number of modalities: $(nmodalities.(Xs))" MultiLogiset([ - concatdatasets([eachmodality(X)[i_mod] for X in Xs]...) for i_mod in 1:nmodalities(Xs) + concatdatasets([modality(X, i_mod) for X in Xs]...) for i_mod in 1:nmodalities(first(Xs)) ]) end @@ -162,7 +164,7 @@ end # nrelations(X::MultiLogiset, i_modality::Integer) = nrelations(modality(X, i_modality)) # Base.length(X::MultiLogiset) = length(nmodalities(X)) # Base.push!(X::MultiLogiset, f::AbstractLogiset) = push!(eachmodality(X), f) -# Base.getindex(X::MultiLogiset, i_modality::Integer) = eachmodality(X)[i_modality] +# Base.getindex(X::MultiLogiset, i_modality::Integer) = modality(X, i_modality) # Base.iterate(X::MultiLogiset, state=1) = state > nmodalities(X) ? nothing : (modality(X, state), state+1) ############################################################################################ diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 2b755c0..e6d5d47 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -140,10 +140,10 @@ function scalarlogiset( print_progress :: Bool = false, # featvaltype = nothing ) - is_feature(features) = (f isa MixedCondition) - is_nofeatures(features) = isnothing(features) - is_unifeatures(features) = (features isa AbstractVector && all(f->is_feature(f), features)) - is_multifeatures(features) = (features isa AbstractVector && all(fs->(is_nofeatures(fs) || is_unifeatures(fs)), features)) + is_feature(f) = (f isa MixedCondition) + is_nofeatures(_features) = isnothing(_features) + is_unifeatures(_features) = (_features isa AbstractVector && all(f->is_feature(f), _features)) + is_multifeatures(_features) = (_features isa AbstractVector && all(fs->(is_nofeatures(fs) || is_unifeatures(fs)), _features)) @assert (is_nofeatures(features) || is_unifeatures(features) || @@ -216,6 +216,10 @@ function scalarlogiset( ]) end + @assert is_nofeatures(features) || is_unifeatures(features) "Unexpected features (type: $(typeof(features))).\n" * + "$(features)" * + "Suspects: $(filter(f->(!is_feature(f) && !is_nofeatures(f) && !is_unifeatures(f)), features))" + if isnothing(features) features = begin if isnothing(conditions) From 71823e80650cb75009590657c20769a6466a8a16 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 6 Aug 2023 04:06:27 +0200 Subject: [PATCH 70/77] Minor --- src/machine-learning.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine-learning.jl b/src/machine-learning.jl index 7ce5fdb..c124488 100644 --- a/src/machine-learning.jl +++ b/src/machine-learning.jl @@ -84,7 +84,7 @@ function bestguess( countmap(labels) else @assert length(labels) === length(weights) "Cannot compute " * - "best guess with uneven number of votes " * + "best guess with mismatching number of votes " * "$(length(labels)) and weights $(length(weights))." countmap(labels, weights) end From 40ce6fa705773735673479e4ffa57c9e45d386d7 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 6 Aug 2023 17:51:45 +0200 Subject: [PATCH 71/77] Fix UnivariateFeature and tests --- src/logisets/scalar/dataset-bindings.jl | 8 ++++++-- src/logisets/scalar/var-features.jl | 9 +++++++++ test/logisets/multilogisets.jl | 6 +++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index e6d5d47..f5a4743 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -239,8 +239,12 @@ function scalarlogiset( conditions = naturalconditions(dataset, features, featvaltype) features = unique(feature.(conditions)) else - error("Unexpected case (TODO)." * - "features = $(typeof(features)), conditions = $(typeof(conditions))") + if !all(f->f isa VarFeature, features) # or AbstractFeature + error("Unexpected case (TODO). " * + "features = $(typeof(features)), conditions = $(typeof(conditions)). " * + "Suspects: $(filter(f->!(f isa VarFeature), features))" + ) + end end end diff --git a/src/logisets/scalar/var-features.jl b/src/logisets/scalar/var-features.jl index 4c0a6bb..e18f915 100644 --- a/src/logisets/scalar/var-features.jl +++ b/src/logisets/scalar/var-features.jl @@ -196,6 +196,15 @@ See also [`Interval`](@ref), struct UnivariateFeature{U} <: AbstractUnivariateFeature{U} i_variable::Integer f::Function + function UnivariateFeature{U}(feat::UnivariateFeature) where {U<:Real} + return new{U}(i_variable(f), feat.f) + end + function UnivariateFeature{U}(i_variable::Integer, f::Function) where {U<:Real} + return new{U}(i_variable, f) + end + function UnivariateFeature(i_variable::Integer, f::Function) + return UnivariateFeature{DEFAULT_VARFEATVALTYPE}(i_variable, f) + end end featurename(f::UnivariateFeature) = string(f.f) diff --git a/test/logisets/multilogisets.jl b/test/logisets/multilogisets.jl index 11c27d5..0a116e9 100644 --- a/test/logisets/multilogisets.jl +++ b/test/logisets/multilogisets.jl @@ -19,11 +19,11 @@ multilogiset = @test_nowarn scalarlogiset(multidataset) generic_features = collect(Iterators.flatten([[UnivariateMax(i_var), UnivariateMin(i_var)] for i_var in 1:_nvars])) metaconditions = [ScalarMetaCondition(feature, >) for feature in generic_features] -multilogiset = @test_logs (:warn,) scalarlogiset(multidataset; use_full_memoization = false, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) -multilogiset = @test_logs (:warn,) scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) +multilogiset = @test_logs min_level=Logging.Error scalarlogiset(multidataset; use_full_memoization = false, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) +multilogiset = @test_logs min_level=Logging.Error scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) metaconditions = vcat([[ScalarMetaCondition(feature, >), ScalarMetaCondition(feature, <)] for feature in generic_features]...) -complete_supported_multilogiset = @test_logs (:warn,) scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) +complete_supported_multilogiset = @test_logs min_level=Logging.Error scalarlogiset(multidataset; use_full_memoization = true, use_onestep_memoization = true, conditions = metaconditions, relations = multirelations) @test_nowarn slicedataset(multilogiset, [2,1]) From 0cec45cf98fda010c5c05321eec1d3646c4c67fa Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 6 Aug 2023 18:28:09 +0200 Subject: [PATCH 72/77] Minor --- src/logisets/main.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logisets/main.jl b/src/logisets/main.jl index 6f75b66..da93373 100644 --- a/src/logisets/main.jl +++ b/src/logisets/main.jl @@ -41,7 +41,7 @@ export MultiLogiset, eachmodality, modality, nmodalities export MultiFormula, modforms -# Multiframe version of logisets, for representing multimodal datasets +# Multi-frame version of logisets, for representing multimodal datasets include("multilogiset.jl") export check, AnchoredFormula From 5a77d3fc492b1905e4954bcd8fa4e201748c9513 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Mon, 7 Aug 2023 02:56:14 +0200 Subject: [PATCH 73/77] Add instances and concatdatasets for multilogiseeds --- src/logisets/scalar/dataset-bindings.jl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index f5a4743..1d61268 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -67,6 +67,25 @@ function ninstances(dataset::Union{AbstractVector,Tuple}) ninstances(first(dataset)) end +function instances( + dataset::Union{AbstractVector,Tuple}, + inds::AbstractVector{<:Integer}, + return_view::Union{Val{true},Val{false}} = Val(false); + kwargs... +) + @assert ismultilogiseed(dataset) "$(typeof(dataset))" + map(modality->instances(modality, inds, return_view; kwargs...), eachmodality(dataset)) +end + +function concatdatasets(datasets::Union{AbstractVector,Tuple}...) + @assert all(ismultilogiseed.(datasets)) "$(typeof.(datasets))" + @assert allequal(nmodalities.(datasets)) "Cannot concatenate multilogiseed's of type ($(typeof.(datasets))) with mismatching " * + "number of modalities: $(nmodalities.(datasets))" + MultiLogiset([ + concatdatasets([modality(dataset, i_mod) for dataset in datasets]...) for i_mod in 1:nmodalities(first(datasets)) + ]) +end + function displaystructure(dataset; indent_str = "", include_ninstances = true, kwargs...) if ismultilogiseed(dataset) pieces = [] From 182b5b4daacfd25e4921bc02f27c7345227dee63 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:46:34 +0200 Subject: [PATCH 74/77] Fix --- src/logisets/scalar/dataset-bindings.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 1d61268..403a688 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -1,6 +1,7 @@ using ProgressMeter using SoleData: AbstractMultiModalDataset import SoleData: ninstances, nvariables, nmodalities, eachmodality, displaystructure +import SoleData: instances, concatdatasets function islogiseed(dataset) false From 314b8d4d4a61e951fa84bf72de29df7326f98dcd Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Mon, 7 Aug 2023 16:44:13 +0200 Subject: [PATCH 75/77] Fix test --- test/logisets/MLJ.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/logisets/MLJ.jl b/test/logisets/MLJ.jl index 7fd9e9d..9c344d5 100644 --- a/test/logisets/MLJ.jl +++ b/test/logisets/MLJ.jl @@ -9,7 +9,7 @@ multidataset, multirelations = collect.(zip([ (Array(reshape(1.0:360.0, 3,3,_nvars,n_instances)), [IA2DRelations..., globalrel]), ]...)) -multilogiset = @test_nowarn scalarlogiset(multidataset) +multilogiset = @test_nowarn min_level=Logging.Error scalarlogiset(multidataset) multilogiset = scalarlogiset(multidataset; relations = multirelations, conditions = vcat([[SoleModels.ScalarMetaCondition(UnivariateMin(i), >), SoleModels.ScalarMetaCondition(UnivariateMax(i), <)] for i in 1:_nvars]...)) X = @test_nowarn modality(multilogiset, 1) From 9cb0eefedd94e83ee181ba6ab01ec7906337cb8f Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Wed, 9 Aug 2023 01:46:58 +0200 Subject: [PATCH 76/77] Fix Feature hashes for wrapped functions --- src/logisets/features.jl | 1 + src/logisets/scalar/var-features.jl | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/logisets/features.jl b/src/logisets/features.jl index 4a25a5e..a3782ee 100644 --- a/src/logisets/features.jl +++ b/src/logisets/features.jl @@ -22,6 +22,7 @@ function Base.show(io::IO, f::AbstractFeature) # print(io, "$(syntaxstring(f))") end +# TODO check whether this is this necessary or wanted, and remove Base.hash(a::VarFeature) maybe? Base.isequal(a::AbstractFeature, b::AbstractFeature) = syntaxstring(a) == syntaxstring(b) Base.hash(a::AbstractFeature) = Base.hash(syntaxstring(a)) diff --git a/src/logisets/scalar/var-features.jl b/src/logisets/scalar/var-features.jl index e18f915..822be0e 100644 --- a/src/logisets/scalar/var-features.jl +++ b/src/logisets/scalar/var-features.jl @@ -48,9 +48,11 @@ See also [`AbstractWorld`](@ref). featvaltype(::Type{<:VarFeature{U}}) where {U} = U featvaltype(::VarFeature{U}) where {U} = U -# # TODO Necessary? -# Base.isequal(a::FT, b::FT) where {FT<:VarFeature} = Base.isequal(map(x->getfield(a, x), fieldnames(typeof(a))), map(x->getfield(b, x), fieldnames(typeof(b)))) -# Base.hash(a::VarFeature) = Base.hash(map(x->getfield(a, x), fieldnames(typeof(a)))) + Base.hash("") +# Note this is necessary when wrapping lambda functions or closures: +# f = [UnivariateFeature{Float64}(1, x->[1.,2.,3.][i]) for i in 1:3] |> unique +# map(x->SoleModels.computefeature(x, rand(1,2)), f) +Base.isequal(a::FT, b::FT) where {FT<:VarFeature} = Base.isequal(map(x->getfield(a, x), fieldnames(typeof(a))), map(x->getfield(b, x), fieldnames(typeof(b)))) +Base.hash(a::VarFeature) = Base.hash(map(x->getfield(a, x), fieldnames(typeof(a)))) + Base.hash(typeof(a)) """ computefeature(f::VarFeature{U}, featchannel; kwargs...)::U where {U} From f33875bd36d9f2d1084d0819fa41120cc57ad2c2 Mon Sep 17 00:00:00 2001 From: giopaglia <24519853+giopaglia@users.noreply.github.com> Date: Sun, 13 Aug 2023 02:52:48 +0200 Subject: [PATCH 77/77] Fix --- src/logisets/scalar/dataset-bindings.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/logisets/scalar/dataset-bindings.jl b/src/logisets/scalar/dataset-bindings.jl index 403a688..fb49877 100644 --- a/src/logisets/scalar/dataset-bindings.jl +++ b/src/logisets/scalar/dataset-bindings.jl @@ -258,6 +258,9 @@ function scalarlogiset( featvaltype = eltype(dataset) conditions = naturalconditions(dataset, features, featvaltype) features = unique(feature.(conditions)) + if use_onestep_memoization == false + conditions = nothing + end else if !all(f->f isa VarFeature, features) # or AbstractFeature error("Unexpected case (TODO). " *