Skip to content

Commit

Permalink
Use new GATlab sugar
Browse files Browse the repository at this point in the history
.
  • Loading branch information
Kris Brown committed Jan 29, 2025
1 parent d84a25b commit 1bbea8d
Show file tree
Hide file tree
Showing 85 changed files with 456 additions and 481 deletions.
2 changes: 1 addition & 1 deletion src/ACSetsGATsInterop.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
""" Compatibility module that integrates ACSets with GATs.
"""
module ACSetsGATsInterop
export ThSchema, FreeSchema, Presentation, @present
export ThSchema, FreeSchema, Presentation, @present, getvalue

using DataStructures: OrderedDict

Expand Down
22 changes: 11 additions & 11 deletions src/basic_sets/finfunctions/FinFnDict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ using StructEquality
using GATlab

using ...Sets: AbsSet, SetOb
using ...SetFunctions: ThSetFunction, SetFunction, dom, codom
using ...SetFunctions: AbsFunction, ThSetFunction, SetFunction, dom, codom
using ...FinSets: FinSet

import ..FinFunctions: FinFunction, FinDomFunction
Expand Down Expand Up @@ -52,37 +52,37 @@ end
# SetFunction implementation
############################

@instance ThSetFunction{Any, SetFunction, FinSet, T
} [model::FinFunctionDict{T}] where T begin
@instance ThSetFunction [model::FinFunctionDict{T}] where T begin

dom()::AbsSet = model.dom

codom()::T = model.codom

app(i::Any, )::Any = getvalue(model)[i]

postcompose(g::SetFunction)::SetFunction = FinDomFunction(
FinFunctionDict(Dict(k => g(v) for (k,v) in getvalue(model)), codom(g)))

function postcompose(g::AbsFunction)::AbsFunction
C = codom(g)
(C isa FinSet ? FinFunction : FinDomFunction)(SetFunction(
FinFunctionDict(Dict(k => g(v) for (k,v) in getvalue(model)), C)))
end
end

""" Default `FinFunction` from a `AbstractDict`"""
FinFunction(f::AbstractDict) = FinFunction(f, FinSet(Set(values(f))))

""" Default `FinFunction` from a `AbstractDict` and codom"""
FinFunction(f::AbstractDict, cod::FinSet) =
FinFunction(FinFunctionDict(f, cod))
FinFunction(SetFunction(FinFunctionDict(f, cod)))

""" Default `FinFunction` from a `AbstractDict` and codom"""
FinFunction(f::AbstractDict, dom::FinSet, cod::FinSet) =
FinFunction(FinFunctionDict(f, dom, cod))
FinFunction(SetFunction(FinFunctionDict(f, dom, cod)))


FinDomFunction(f::AbstractDict, cod::AbsSet) =
FinDomFunction(FinFunctionDict(f, cod))
FinDomFunction(SetFunction(FinFunctionDict(f, cod)))

FinDomFunction(f::AbstractDict, dom::FinSet, cod::AbsSet) =
FinDomFunction(FinFunctionDict(f, dom, cod))

FinDomFunction(SetFunction(FinFunctionDict(f, dom, cod)))

end # module
34 changes: 19 additions & 15 deletions src/basic_sets/finfunctions/FinFnVector.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,34 @@ using GATlab
import ACSets.Columns: preimage

using ...Sets: AbsSet
using ...SetFunctions: ThSetFunction, SetFunction, codom
using ...SetFunctions: AbsFunction, ThSetFunction, SetFunction, codom
using ...FinSets: FinSet
import ..FinFunctions: FinFunction, FinDomFunction, is_indexed

"""
There are two kinds of FinFunctionVector: `FinFunctionVector` and
`IndexedFinFunctionVector`.
"""
abstract type AbsFinFunctionVector{T} end
abstract type AbsFinFunctionVector end

""" Implicitly domain is `FinSet(length(v))` """
@struct_hash_equal struct FinFunctionVector{T<:AbsSet} <: AbsFinFunctionVector{T}
@struct_hash_equal struct FinFunctionVector <: AbsFinFunctionVector
val::AbstractVector
codom::T
codom::AbsSet
end

""" Implicitly domain is `FinSet(length(v))` """
@struct_hash_equal struct IndexedFinFunctionVector{T<:AbsSet} <: AbsFinFunctionVector{T}
@struct_hash_equal struct IndexedFinFunctionVector <: AbsFinFunctionVector
val::AbstractVector
codom::T
codom::AbsSet
index::DefaultDict
""" Create the index cache upon creating the vector """
function IndexedFinFunctionVector(v, c::T) where T
function IndexedFinFunctionVector(v, c::AbsSet)
index = DefaultDict{eltype(c), Vector{Int}}(()->[])
for (i, x) in enumerate(v)
push!(index[x], i)
end
new{T}(v, c, index)
new(v, c, index)
end
end

Expand Down Expand Up @@ -64,32 +64,36 @@ end
# SetFunction implementation
############################

@instance ThSetFunction{Any, SetFunction, FinSet, T} [model::AbsFinFunctionVector{T}] where {
T} begin
@instance ThSetFunction [model::AbsFinFunctionVector] begin

dom()::AbsSet = FinSet(length(getvalue(model)))

codom()::T = model.codom
codom()::AbsSet = model.codom

app(i::Any)::Any = getvalue(model)[i]

function postcompose(f::SetFunction)::SetFunction
FinDomFunction(FF(is_indexed(model))(f.(getvalue(model)), codom(f)))
function postcompose(f::AbsFunction)::AbsFunction
C = codom(f)
(C isa FinSet ? FinFunction : FinDomFunction)(SetFunction(
FF(is_indexed(model))(f.(getvalue(model)), C)))
end

end

# Default constructors
######################
FinFunction(f::AbsFinFunctionVector) = FinFunction(SetFunction(f))

FinDomFunction(f::AbsFinFunctionVector) = FinDomFunction(SetFunction(f))

"""
Default `FinFunction` or `FinDomFunction` from a `AbstractVector` and codom
"""
FinFunction(f::AbstractVector, cod::FinSet; index=false) =
FinFunction(FF(index)(f, cod))
FinFunction(SetFunction(FF(index)(f, cod)))

function FinDomFunction(f::AbstractVector, cod::AbsSet; index=false)
FinDomFunction(FF(index)(f, cod))
FinDomFunction(SetFunction(FF(index)(f, cod)))
end

FinFunction(f::AbstractVector, dom::FinSet, cod::FinSet; index=false) =
Expand Down
11 changes: 8 additions & 3 deletions src/basic_sets/finfunctions/FinForce.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
"""
`force` behaves differently when the domain is finite because we can compute
a normal form. This should really be called "normalize". It coerces the
function to a `FinFnVector` (if dom is `FinSetInt`) or `FinFnDict` (otherwise).
"""
module FinForce

using GATlab: getvalue
Expand All @@ -12,12 +17,12 @@ using ..FinFunctions, ..FinFnVector, ..FinFnDict
Special `force` method for FinDomFunction - we know we can normalize to a
FinFunctionDict or FinFunctionVect
"""
function force(s::Fin_FinDom)
i = getvalue(s)
function force(s::AbsFinDomFunction)
i = getvalue(getvalue(s))
F = s isa FinFunction ? FinFunction : FinDomFunction
if getvalue(dom(s)) isa FinSetInt
i isa AbsFinFunctionVector && return s
return F([s(elem) for elem in dom(s)], dom(s), codom(s); index=is_indexed(s))
return F([s(elem) for elem in dom(s)], dom(s), codom(s); index=is_indexed(i))
else
i isa FinFunctionDict && return s
return F(Dict(k => s(k) for k in dom(s)), dom(s), codom(s))
Expand Down
134 changes: 103 additions & 31 deletions src/basic_sets/finfunctions/FinFunctions.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
export FinFunction, FinDomFunction, preimage, is_indexed,
is_monic, is_epic, is_iso, Fin_FinDom, ensure_indexed

export FinFunction, FinDomFunction, preimage, is_indexed, specialize,
is_monic, is_epic, is_iso, AbsFinDomFunction, ensure_indexed

using StructEquality
import ACSets.Columns: preimage, Column
using GATlab: getvalue
using GATlab

using ..FinSets: AbsSet, FinSet, FinSetInt, SetOb
using ..SetFunctions: SetFunction, dom, codom
using ..SetFunctions
import ..SetFunctions: SetFunction
import .ThSetFunction: dom, codom, app, postcompose

# Finite functions
##################
""" Common type for `FinFunction` and `FinDomFunction` """
abstract type AbsFinDomFunction <: AbsFunction end

GATlab.getvalue(f::AbsFinDomFunction) = f.fun

Base.show(io::IO, f::AbsFinDomFunction) = show(io, getvalue(f))

SetFunction(f::AbsFinDomFunction) = getvalue(f)

""" Function between finite sets.
Expand All @@ -23,32 +33,55 @@ If a codomain is provided, by default the constructor checks it is valid.
This type is mildly generalized by [`FinDomFunction`](@ref).
"""
const FinFunction = SetFunction{Any, SetFunction, FinSet, FinSet}
@struct_hash_equal struct FinFunction <: AbsFinDomFunction
fun::SetFunction
function FinFunction(s::SetFunction)
dom(s) isa FinSet || error("Bad dom $(dom(s))")
codom(s) isa FinSet || error("Bad codom $(codom(s))")
new(s)
end
end

const FinDomFunction = SetFunction{Any, SetFunction, FinSet, SetOb}
@struct_hash_equal struct FinDomFunction <: AbsFinDomFunction
fun::SetFunction
function FinDomFunction(s::SetFunction)
dom(s) isa FinSet || error("Bad dom $(dom(s))")
new(s)
end
end

dom(model::AbsFinDomFunction)::AbsSet = dom(getvalue(model))

codom(model::AbsFinDomFunction)::AbsSet = codom(getvalue(model))

app(model::AbsFinDomFunction,i::Any) = app(getvalue(model), i)

postcompose(model::AbsFinDomFunction,f::AbsFunction)::AbsFunction =
postcompose(getvalue(model), f)

(f::AbsFinDomFunction)(i) = app(f, i)

const Fin_FinDom = Union{FinFunction, FinDomFunction}

# These could be made to fail early if ever used in performance-critical areas
is_epic(f::FinFunction) = length(codom(f)) == length(Set(values(collect(f))))

is_monic(f::Fin_FinDom) = length(dom(f)) == length(Set(values(collect(f))))
is_monic(f::AbsFinDomFunction) = length(dom(f)) == length(Set(values(collect(f))))

is_iso(f::Fin_FinDom) = is_monic(f) && is_epic(f)
is_iso(f::AbsFinDomFunction) = is_monic(f) && is_epic(f)

""" Iterate over image of function """
Base.iterate(f::Fin_FinDom, xs...) = iterate(f.(dom(f)), xs...)
Base.iterate(f::AbsFinDomFunction, xs...) = iterate(f.(dom(f)), xs...)

Base.length(f::Fin_FinDom) = length(dom(f))
Base.length(f::AbsFinDomFunction) = length(dom(f))

Base.eltype(f::Fin_FinDom) = eltype(codom(f))
Base.eltype(f::AbsFinDomFunction) = eltype(codom(f))

# Indexing
##########

""" Preimage of a FinDomFunction """
preimage(f::Fin_FinDom, x) = if x codom(f)
is_indexed(f) && return preimage(getvalue(f), x) # use cached value
preimage(f::AbsFinDomFunction, x) = if x codom(f)
is_indexed(f) && return preimage(getvalue(getvalue(f)), x) # use cached value
filter(y -> f(y) == x, collect(dom(f)))
else
error("Cannot take preimage: $x not found in codomain of $f")
Expand All @@ -57,36 +90,75 @@ end
""" A SetFunction is indexed iff its implementation is """
is_indexed(f::SetFunction) = is_indexed(getvalue(f))

is_indexed(f::AbsFinDomFunction) = is_indexed(getvalue(f))


""" If an implementation specifically comes with its own `preimage` method, we
consider the SetFunction to be indexed """
is_indexed(::T) where T = !isempty(methods(preimage, (T, Any)))

""" Try to index the function, if it isn't already """
function ensure_indexed(f::T) where {T<:Fin_FinDom}
function ensure_indexed(f::T) where {T<:AbsFinDomFunction}
is_indexed(f) && return f
if getvalue(f) isa FinFunctionVector
if getvalue(getvalue(f)) isa FinFunctionVector
return T(collect(f), codom(f); index=true)
end
f # error("Cannot index $(getvalue(f))")
end

# Force
#######
ensure_indexed(f::SetFunction) = f

"""
`force` behaves differently when the domain is finite because we can compute
a normal form. This should really be called "normalize". It coerces the
function to a `FinFnVector` (if dom is `FinSetInt`) or `FinFnDict` (otherwise).
"""
# function force(s::Union{FinFunction, FinDomFunction})::FinDomFunction
# if getvalue(dom(s)) isa FinSetInt
# FinDomFunction(collect(s), codom(s))
# else
# FinDomFunction(Dict(k=>s(k) for k in dom(s)), dom(s), codom(s))
# end
# end
# Specializing
###############
specialize(f::FinFunction) = f
specialize(f::FinDomFunction) =
codom(f) isa FinSet ? FinFunction(getvalue(f)) : f

function specialize(f::SetFunction)
if dom(f) isa FinSet
(codom(f) isa FinSet ? FinFunction : FinDomFunction)(getvalue(f))
else
f
end
end

# Identity
##########
FinFunction(s::FinSet) = FinFunction(SetFunction(s))
FinDomFunction(s::FinSet) = FinDomFunction(SetFunction(s))

# Const
##########
FinFunction(s::ConstantFunction) = FinFunction(SetFunction(s))
FinDomFunction(s::ConstantFunction) = FinDomFunction(SetFunction(s))

# Callables
###########
FinFunction(s::SetFunctionCallable) = FinFunction(SetFunction(s))

FinDomFunction(s::SetFunctionCallable) = FinDomFunction(SetFunction(s))

FinFunction(f::Function, d::FinSet, c::FinSet) =
FinFunction(SetFunction(f,d,c))

FinDomFunction(f::Function, d::FinSet, c::AbsSet) =
FinDomFunction(SetFunction(f,d,c))

# Composites
############
FinFunction(f::CompositeFunction) = FinFunction(SetFunction(f))
FinDomFunction(f::CompositeFunction) = FinDomFunction(SetFunction(f))

function FinFunction(f::AbsFunction, g::AbsFunction)
dom(f) isa FinSet || error("Cannot create composite Finfunction $f $g")
codom(g) isa FinSet || error("Cannot create composite Finfunction $f $g")
FinFunction(SetFunction(SetFunction(f), SetFunction(g)))
end

function FinDomFunction(f::AbsFunction, g::AbsFunction)
dom(f) isa FinSet || error("Cannot create composite Finfunction $f $g")
FinDomFunction(SetFunction(SetFunction(f), SetFunction(g)))
end

# ACSet Interface
#################
Expand Down
2 changes: 1 addition & 1 deletion src/basic_sets/finsets/EitherFinSet.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ right(e::EitherFinSet) = e.right
# FinSet Implementation
#######################

@instance ThFinSet{Bool, Any, Int} [model::EitherFinSet] begin
@instance ThFinSet [model::EitherFinSet] begin

in′(i::Any)::Bool = i left(model) || i right(model)

Expand Down
2 changes: 1 addition & 1 deletion src/basic_sets/finsets/EmptySets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Base.show(io::IO, ::EmptySet) = print(io, "Empty()")
# FinSet Implementation
#######################

@instance ThFinSet{Bool, Any, Int} [model::EmptySet] begin
@instance ThFinSet [model::EmptySet] begin
in′(i::Any)::Bool = false
eltype()::Any = Union{}
length()::Int = 0
Expand Down
2 changes: 1 addition & 1 deletion src/basic_sets/finsets/FinSetHash.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ end
# FinSet Implementation
#######################

@instance ThFinSet{Bool, Any, Int} [model::FinSetHash{T}] where T begin
@instance ThFinSet [model::FinSetHash{T}] where T begin
in′(i::Any)::Bool = i getvalue(model)
eltype() = T
length()::Int = length(getvalue(model))
Expand Down
Loading

0 comments on commit 1bbea8d

Please sign in to comment.