Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feet dsl update #88

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
45fd886
Created Stratification DSL. Required adding getter methods for lss, …
neonWhiteout Aug 31, 2023
7638748
Replaced StockAndFlowF with AbstractStockAndFlowF in get_lss, get_lss…
neonWhiteout Aug 31, 2023
593ac60
Global flags are now kwargs. Fixed a typo where sfstratify was @stra…
neonWhiteout Sep 1, 2023
725b9ea
Added tests for flags. Changed capitalization of some names in docst…
neonWhiteout Sep 1, 2023
5161893
homomorphisms in test now defined from SfA -> sfA with attributes map…
neonWhiteout Sep 1, 2023
ae267f5
Created print_unmapped to stratification, which prints all values whi…
neonWhiteout Sep 1, 2023
96347b2
Factored out iteration over stratification lines. Removed some unnec…
neonWhiteout Sep 1, 2023
4e98b9f
Factored out converting mappings to Vector{Int}
neonWhiteout Sep 1, 2023
1813275
Minor docstring and formatting fixes
neonWhiteout Sep 1, 2023
0e92fee
Fixed an unfinished docstring in Syntax.jl. Created a @stratify macr…
neonWhiteout Sep 2, 2023
46c1a48
Changed @stratify so it now uses a begin end block instead of a quote…
neonWhiteout Sep 6, 2023
7080868
Redid Stratification to use a struct, SNames, to group together relev…
neonWhiteout Sep 6, 2023
6733279
got n_stratify working with l_type with itself 4 times. Had to chang…
neonWhiteout Sep 6, 2023
cb1ae3b
Updated some assert statements for n_stratify
neonWhiteout Sep 6, 2023
8c0fc74
Added a test for n_stratify
neonWhiteout Sep 6, 2023
d3e693d
Merge branch 'master' into nstratify_good
neonWhiteout Sep 7, 2023
ad06146
Fixed duplicate typo from merge error in syntax test
neonWhiteout Sep 7, 2023
1e54672
Moved stratification test to a syntax folder. Realized it didn't act…
neonWhiteout Sep 7, 2023
07f21d4
Added additional tests for n_stratify. Fixed a few assert statements.
neonWhiteout Sep 7, 2023
74e3efe
fixed a typo in syntax.jl test causing a test to fail
neonWhiteout Sep 7, 2023
6b94a42
Updated n_stratify docstring
neonWhiteout Sep 7, 2023
3c436f4
Minor comment touchups.
neonWhiteout Sep 26, 2023
86adbd2
Merge branch 'AlgebraicJulia:master' into nstratify_good
neonWhiteout Sep 27, 2023
16cde76
Updates to integrate composition with stratification tests.
neonWhiteout Sep 27, 2023
2d5b2f8
create_foot can accept a tuple of one element. (@feet ) returns an e…
neonWhiteout Sep 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/StockFlow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ funcDynam, flowVariableIndex, funcFlow, funcFlows, funcSV, funcSVs, TransitionMa
vectorfield, funcFlowsRaw, funcFlowRaw, inflowsAll, outflowsAll,instock,outstock, stockssv, stocksv, svsv, svsstock,
vsstock, vssv, svsstockAllF, vsstockAllF, vssvAllF, StockAndFlowUntyped, StockAndFlowFUntyped, StockAndFlowStructureUntyped, StockAndFlowStructureFUntyped, StockAndFlowUntyped0, Open, snames, fnames, svnames, vnames,
object_shift_right, foot, leg, lsnames, OpenStockAndFlow, OpenStockAndFlowOb, fv, fvs, nlvv, nlpv, vtgt, vsrc, vpsrc, vptgt, pname, pnames, make_v_expr,
vop, lvvposition, lvtgtposition, lsvvposition, lpvvposition, recreate_stratified, set_snames!, set_fnames!, set_svnames!, set_vnames!, set_pnames!, set_sname!, set_fname!, set_svname!, set_vname!, set_pname!
vop, lvvposition, lvtgtposition, lsvvposition, lpvvposition, recreate_stratified, set_snames!, set_fnames!, set_svnames!, set_vnames!, set_pnames!, set_sname!, set_fname!, set_svname!, set_vname!, set_pname!,
get_lss, get_lssv, get_lsvsv, get_lsvv, get_lvs, get_lvv, get_is, get_ifn, get_os, get_ofn, get_lpvp, get_lpvv, get_lvsrc, get_lvtgt, get_links


using Catlab
using Catlab.CategoricalAlgebra
Expand Down Expand Up @@ -268,6 +270,20 @@ nlpv(p::AbstractStockAndFlowStructureF) = nparts(p,:LPV) #links from dynamic var
np(p::AbstractStockAndFlowStructureF) = nparts(p,:P) #parameters


get_lss(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:lss].m))
get_lssv(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:lssv].m))
get_lsvsv(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:lsvsv].m))
get_lsvv(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:lsvv].m))
get_lvs(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:lvs].m))
get_lvv(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:lvv].m))
get_is(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:is].m))
get_ifn(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:ifn].m))
get_os(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:os].m))
get_ofn(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:ofn].m))
get_lpvp(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:lpvp].m))
get_lpvv(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:lpvv].m))
get_lvsrc(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:lvsrc].m))
get_lvtgt(sf::AbstractStockAndFlowF) = collect(values(sf.subparts[:lvtgt].m))


#EXAMPLE:
Expand Down
240 changes: 230 additions & 10 deletions src/Syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,13 @@ end
```
"""
module Syntax
export @stock_and_flow, @foot, @feet
export @stock_and_flow, @foot, @feet, infer_links

using ..StockFlow
using MLStyle

import Base: ==, Iterators.flatmap

"""
stock_and_flow(block :: Expr)

Expand Down Expand Up @@ -949,8 +951,10 @@ Create a foot with S => N syntax, where S is stock, N is sum variable.
```
"""
macro foot(block::Expr)
Base.remove_linenums!(block)
return create_foot(block)
escaped_block = Expr(:quote, block)
quote
create_foot($(escaped_block))
end
end


Expand All @@ -974,14 +978,23 @@ end
"""
macro feet(block::Expr)
Base.remove_linenums!(block)
@match block begin
quote
$((block...))
end => map(create_foot, block) # also matches empty
Expr(e, _...) => [create_foot(block)] # this also matches the above, so it's necessary this comes second.
escaped_block = Expr(:quote, block)
quote
inner_block = $escaped_block
@match inner_block begin
quote
$((inner_block...))
end => map(create_foot, inner_block) # also matches empty
Expr(e, _...) => [create_foot(inner_block)] # this also matches the above, so it's necessary this comes second.
end
end
end

macro feet()
quote
Vector{StockAndFlow0}()
end
end

"""
create_foot(block :: Expr)
Expand All @@ -1002,10 +1015,9 @@ multiple_links = @foot A => B, A => B # will have two links from A to B.
"""
function create_foot(block::Expr)
@match block.head begin

:tuple => begin
if isempty(block.args) # case for create_foot(:())
error("Cannot create foot with no arguments.")
error("Cannot create foot with zero arguments.")
end
foot_s = Vector{Symbol}()
foot_sv = Vector{Symbol}()
Expand Down Expand Up @@ -1044,7 +1056,215 @@ function match_foot_format(footblock::Expr)
end
end

#############################################

"""
infer_particular_link!(sfsrc, sftgt, f1, f2, map1, map2, destination_vector, posf=nothing)

infer_particular_link!(sfsrc, sftgt, get_lvs, get_lvv, stockmaps, dyvarmaps, lvmaps, get_lvvposition) # LV

If we're mapping the same value to multiple positions, it doesn't matter which one goes where.
We have a few options, on how we want to distribute mappings. Way it's done here, always goes to the last position.

"""
function infer_particular_link!(sfsrc, sftgt, f1, f2, map1, map2, destination_vector)

hom1′_mappings = f1(sftgt)
hom2′_mappings = f2(sftgt)

tgt::Dict{Tuple{Int, Int}, Int} = Dict((hom1′, hom2′) => i for (i, (hom1′, hom2′)) in enumerate(zip(hom1′_mappings, hom2′_mappings))) # ISSUE: If there are two matches, second one overwrites the first.
# SOLUTION: Who cares. Just map to the last.
for (i, (hom1, hom2)) in enumerate(zip(f1(sfsrc), f2(sfsrc)))
mapped_index1 = map1[hom1]
mapped_index2 = map2[hom2]

linkmap = tgt[(mapped_index1, mapped_index2)]



destination_vector[i] = linkmap # updated
end
return destination_vector

end


"""
infer_links(sfsrc :: StockAndFlowF, sftgt :: StockAndFlowF, NecMaps :: Dict{Symbol, Vector{Int64}})

Infer LS, I, O, LV, LSV, LVV, LPV mappings for an ACSetTransformation.
Returns dictionary of Symbols to lists of indices, corresponding to an ACSetTransformation argument.
If there exist no such mappings (eg, no LVV), that pairing will not be included in the returned dictionary.

If A <- C -> B, and we have A -> A' and B -> B' and a unique C' such that A' <- C' -> B', we can assume C -> C'.

:S => [2,4,1,3], :F => [1,2,4,3], ...

NecMaps must contain keys S, F, SV, P, V, each pointing to a (possibly empty) array of indices
"""
function infer_links(sfsrc :: StockAndFlowF, sftgt :: StockAndFlowF, NecMaps :: Dict{Symbol, Vector{Int64}})


stockmaps = NecMaps[:S]
flowmaps = NecMaps[:F]
summaps = NecMaps[:SV]
parammaps = NecMaps[:P]
dyvarmaps = NecMaps[:V]

lsmaps = zeros(Int, nls(sfsrc))
imaps = zeros(Int, ni(sfsrc))
omaps = zeros(Int, no(sfsrc))
lvmaps = zeros(Int, nlv(sfsrc))
lsvmaps = zeros(Int, nlsv(sfsrc))
lvvmaps = zeros(Int, nlvv(sfsrc))
lpvmaps = zeros(Int, nlpv(sfsrc))
# After the following calls, there should be no zeroes.


infer_particular_link!(sfsrc, sftgt, get_lss, get_lssv, stockmaps, summaps, lsmaps) # LS
infer_particular_link!(sfsrc, sftgt, get_ifn, get_is, flowmaps, stockmaps, imaps) # I
infer_particular_link!(sfsrc, sftgt, get_ofn, get_os, flowmaps, stockmaps, omaps) # O
infer_particular_link!(sfsrc, sftgt, get_lvs, get_lvv, stockmaps, dyvarmaps, lvmaps) # LV
infer_particular_link!(sfsrc, sftgt, get_lsvsv, get_lsvv, summaps, dyvarmaps, lsvmaps) # LSV
infer_particular_link!(sfsrc, sftgt, get_lvsrc, get_lvtgt, dyvarmaps, dyvarmaps, lvvmaps) # LVV
infer_particular_link!(sfsrc, sftgt, get_lpvp, get_lpvv, parammaps, dyvarmaps, lpvmaps) # LPV

return Dict(:LS => lsmaps, :LSV => lsvmaps, :LV => lvmaps, :I => imaps, :O => omaps, :LPV => lpvmaps, :LVV => lvvmaps)

include("syntax/Composition.jl")

end


struct DSLArgument
key::Symbol
value::Symbol
flags::Set{Symbol} # At present, the only flag that exists is ~
DSLArgument(kv::Pair{Union{Expr, Symbol}, Symbol}) = begin # this constructor seemed to fail... need to figure out why. Maybe it can't call other constructors.
key, flags = unwrap_expression(first(kv))
new(key, second(kv), flags)
end
DSLArgument(k::Union{Expr, Symbol}, v::Symbol) = begin
key, flags = unwrap_expression(k)
new(key, v, flags)
end
DSLArgument(k::Symbol, v::Symbol, f::Set{Symbol}) = new(k, v, f)
end

==(a::DSLArgument, b::DSLArgument) = a.key == b.key && a.value == b.value && a.flags == b.flags


function unwrap_expression(x::Union{Symbol, Expr}, flags::Set{Symbol}=Set{Symbol}())::Tuple{Symbol, Set{Symbol}} # No mutable default arguments.
if typeof(x) == Symbol
return (x, flags)
else
return unwrap_expression(x.args[2], push!(flags, x.args[1]))
end
end


"""
S₁ => I₁
S₂ => I₂
S₁ => S₂


I₁ => I₂

Determine what index an element e maps to based upon what f we have in the mapping such that e -> f
"""
function connect_by_value(; src::Dict{T,U}, mapping::Dict{T,T}, tgt::Dict{T,U})::Dict{U, U} where {T, U}
@assert allunique(values(src))

@assert all(x -> x ∈ keys(mapping), keys(src))
@assert all(x -> x ∈ keys(tgt), values(mapping))

return Dict(src[key] => tgt[value] for (key, value) in mapping)

end


"""
Filter a vector for all elements with substr as a substring.
"""
function substring_matches(v::Vector, substr::String)::Vector
return filter(x -> occursin(substr, string(x)), v)
end


"""
Takes a symbol 'key', applys flags, finds matches in s, and returns a vector of matching keys.
Currently, there are two options: no flags, in which case [key] is returned, or ~ is the only flag, in which case all Symbols with matching substrings are returned.
"""
function apply_flags(key::Symbol, flags::Set{Symbol}, s::Vector{Symbol})::Vector{Symbol} # Could make this a generator?

if isempty(flags)
@assert (key ∈ s) "$s does not contain key $key ! Did you forget to prefix ~?"
return [key] # potentially inefficient
elseif :~ ∈ flags

matches = collect(substring_matches(s, string(key)))

new_flags = copy(flags) # copy isn't necessary, probably
pop!(new_flags, :~)

return collect(flatmap(x -> apply_flags(x, new_flags, s), matches)) # this is just in case we add additional flags. As is, the recursion is unnecessary.
else
error("Unknown flag found! $(flags)")
end
end

"""
substitute_symbols(s::Dict{Symbol, Int}, t::Dict{Symbol, Int}, m::Vector{DSLArgument} ; use_flags::Bool=true)::Dict{Int, Int}

Convert Dict(SymA => IntA), Dict(SymB => IntB), Dict(SymA => SymB) into Dict{IntA => IntB}
Using original sf defintions, and the user defined mappings, transform user defined symbol mappings to index mappings.
"""
function substitute_symbols(s::Dict{Symbol, Int}, t::Dict{Symbol, Int}, m::Vector{DSLArgument} ; use_flags::Bool=true)::Dict{Int, Int}
if !use_flags
mapping = Dict(arg.key => arg.value for arg in m)
return connect_by_value(src=s, mapping=mapping, tgt=t)
else
master_dict::Dict{Int, Int} = Dict()
for statement in m
key_matches = apply_flags(statement.key, statement.flags, collect(keys(s))) # Vector of Symbol
if isempty(key_matches)
println("WARNING! No matches on $(statement.key) with flags $(statement.flags)")
else
mergewith!((x...) -> first(x), master_dict, Dict(s[match] => t[statement.value] for match ∈ key_matches))
end
end
return master_dict
end
end


"""
Convert a vector of unique elements to a dictionary with each element pointing to their original index.
"""
function invert_vector(v::Vector{K})::Dict{K, Int} where {K} # Elements of v must be hashable
new_dict = Dict(val => i for (i, val) ∈ enumerate(v))
@assert length(new_dict) == length(v) "Nonunique key in vector v: $v"
return new_dict
end


"""
Takes any arguments and returns nothing.
Used so we can maintain equality when making ACSetTransformations.
"""
NothingFunction(x...)::Nothing = nothing;




include("syntax/Composition.jl")
include("syntax/Stratification.jl")

end





2 changes: 1 addition & 1 deletion src/SystemStructure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ extracVAndAttrStructureAndFlatten(p::AbstractStockAndFlowF) = begin
if nvb(p)>0
for v in 1:nvb(p)
vn = flattenTupleNames(vname(p,v))
v_op = foldr(==,vop(p,v)) ? vop(p,v)[1] : error("operators $(vop(p,v)) in the stratified model's auxiliary variable: $(join(vname(p,v))) should be the same!")
v_op = allequal(vop(p,v)) ? vop(p,v)[1] : error("operators $(vop(p,v)) in the stratified model's auxiliary variable: $(join(vname(p,v))) should be the same!")
vnp = vn=>(args(p,v)=>v_op)
vs = vcat(vs,vnp)
end
Expand Down
Loading
Loading