Skip to content

Commit 4ec63c5

Browse files
committed
Standard filters for listVariables and getVariables
1 parent 9987a98 commit 4ec63c5

7 files changed

Lines changed: 159 additions & 44 deletions

File tree

src/Common.jl

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,63 @@ function sortDFG(vars::Vector{<:AbstractGraphNode}; by = getTimestamp, kwargs...
6969
end
7070
sortDFG(vars::Vector{Symbol}; lt = natural_lt, kwargs...) = sort(vars; lt = lt, kwargs...)
7171

72+
##==============================================================================
73+
## Filtering
74+
##==============================================================================
75+
76+
# std_numeric_predicates = [==, <, <=, >, >=, in]
77+
# std_string_predicates = [==, in, contains, startswith, endswith]
78+
79+
# # full list
80+
# tags_includes(tag::Symbol) = Base.Fix1(in, tag)
81+
# solvable_eq(x::Int) = ==(x)
82+
# solvable_in(x::Vector{Int}) = in(x)
83+
# solvable_lt(x::Int) = <(x)
84+
# solvable_lte(x::Int) = <=(x)
85+
# solvable_gt(x::Int) = >(x)
86+
# solvable_gte(x::Int) = >=(x)
87+
# label_in(x::Vector{Symbol}) = in(x)
88+
# label_contains(x::String) = contains(x)
89+
# label_startswith(x::String) = startswith(x)
90+
# label_endswith(x::String) = endswith(x)
91+
# type_eq(x::AbstractVariableStateType) = ==(x)
92+
# type_in(x::Vector{<:AbstractVariableStateType}) = in(x)
93+
# type_contains(x::String) = contains(x)
94+
# type_startswith(x::String) = startswith(x)
95+
# type_endswith(x::String) = endswith(x)
96+
97+
# Set predicates
98+
# collection_includes(item) = Base.Fix1(in, item) # collection includes item (item in collection)
99+
100+
# not supported helper
101+
# collection_overlap(collection) = !isdisjoint(collection) # collection overlaps with another collection
102+
103+
"""
104+
$SIGNATURES
105+
Filter nodes in a DFG based on a predicate function.
106+
This function modifies the input `nodes` vector in place, removing nodes that do not satisfy the predicate.
107+
108+
- **For cross-backend compatibility:**
109+
Use only the standard predicates (`==`, `<`, `<=`, `>`, `>=`, `in`, `contains`, `startswith`, `endswith`)
110+
when you need your code to work with both in-memory and database-backed DFGs.
111+
These are likely to be supported by database query languages and are defined in `std_numeric_predicates` and `std_string_predicates`.
112+
113+
- **For in-memory only operations:**
114+
You can use any Julia predicate, since you have full access to the data and Julia's capabilities.
115+
This is more flexible but will not work if you later switch to a database backend or another programming language.
116+
117+
Standard predicates
118+
- Numeric predicates: `==`, `<`, `<=`, `>`, `>=`, `in`
119+
- String predicates: `==`, `contains`, `startswith`, `endswith`, `in`
120+
"""
121+
function filterDFG! end
122+
123+
filterDFG!(nodes, predicate::Nothing, by::Function = identity) = nodes
124+
# function filterDFG!(nodes, predicate::Base.Fix2, by=identity)
125+
function filterDFG!(nodes, predicate::Function, by::Function = identity)
126+
return filter!(predicate by, nodes)
127+
end
128+
72129
##==============================================================================
73130
## Validation of session, robot, and user labels.
74131
##==============================================================================
@@ -83,10 +140,8 @@ $(SIGNATURES)
83140
Returns true if the label is valid for node.
84141
"""
85142
function isValidLabel(id::Union{Symbol, String})
86-
if typeof(id) == Symbol
87-
id = String(id)
88-
end
89-
return !in(uppercase(id), _invalidIds) && !isnothing(match(_validLabelRegex, id))
143+
_id = string(id)
144+
return occursin(_validLabelRegex, _id) && !in(uppercase(_id), _invalidIds)
90145
end
91146

92147
"""

src/DistributedFactorGraphs.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ export pack, unpack, packDistribution, unpackDistribution
337337
export natural_lt, sortDFG
338338

339339
# Validation
340-
export isValidLabel
340+
# export isValidLabel
341341

342342
## List
343343
export ls, lsf, ls2
@@ -431,6 +431,9 @@ include("DataBlobs/services/BlobStores.jl")
431431
include("DataBlobs/services/BlobPacking.jl")
432432
include("DataBlobs/services/HelpersDataWrapEntryBlob.jl")
433433

434+
# To be moved as necessary.
435+
include("Common.jl")
436+
434437
# In Memory Types
435438
include("GraphsDFG/GraphsDFG.jl")
436439
using .GraphsDFGs
@@ -453,9 +456,6 @@ include("FileDFG/FileDFG.jl")
453456
# Custom show and printing for variable factor etc.
454457
include("services/CustomPrinting.jl")
455458

456-
# To be moved as necessary.
457-
include("Common.jl")
458-
459459
include("weakdeps_prototypes.jl")
460460

461461
#TODO start off as just an alias before deprecating

src/GraphsDFG/GraphsDFG.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ using ...DistributedFactorGraphs:
1515
FactorgraphRoot,
1616
AbstractGraphVariable,
1717
AbstractGraphFactor,
18-
NoSolverParams
18+
NoSolverParams,
19+
filterDFG!
1920

2021
# import DFG functions to extend
2122
import ...DistributedFactorGraphs:

src/GraphsDFG/entities/GraphsDFG.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ function DFG.setMetadata!(dfg::GraphsDFG, metadata::Dict{Symbol, SmallDataTypes}
3030
return merge!(dfg.graph.metadata, metadata)
3131
end
3232

33-
DFG.setDescription!(dfg::GraphsDFG, description::String) = dfg.graph.description = description
33+
function DFG.setDescription!(dfg::GraphsDFG, description::String)
34+
return dfg.graph.description = description
35+
end
3436

3537
"""
3638
$(SIGNATURES)
@@ -75,8 +77,8 @@ function GraphsDFG{T, V, F}(
7577
) where {T <: AbstractDFGParams, V <: AbstractGraphVariable, F <: AbstractGraphFactor}
7678

7779
# Validate the graphLabel and agentLabel
78-
!isValidLabel(graphLabel) && error("'$graphLabel' is not a valid label")
79-
!isValidLabel(agentLabel) && error("'$agentLabel' is not a valid label")
80+
!DFG.isValidLabel(graphLabel) && error("'$graphLabel' is not a valid label")
81+
!DFG.isValidLabel(agentLabel) && error("'$agentLabel' is not a valid label")
8082

8183
return GraphsDFG{T, V, F}(
8284
g,

src/GraphsDFG/services/GraphsDFG.jl

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -128,25 +128,56 @@ function deleteFactor!(dfg::GraphsDFG, label::Symbol; suppressGetFactor::Bool =
128128
return 1
129129
end
130130

131+
# """
132+
# tagsFilter = ⊇([:x1])
133+
# tagsFilter([:x1, :x2])
134+
# true
135+
# tagsFilter = Base.Fix1(in, :x1)
136+
# tagsFilter([:x1, :x2])
137+
# true
138+
# """
139+
131140
function getVariables(
132141
dfg::GraphsDFG,
133-
regexFilter::Union{Nothing, Regex} = nothing;
142+
regex::Union{Nothing, Regex} = nothing;
134143
tags::Vector{Symbol} = Symbol[],
135-
solvable::Int = 0,
136-
solvableFilter::Union{Nothing, Base.Fix2} = nothing,
144+
solvable::Union{Nothing, Int} = nothing,
145+
solvableFilter::Union{Nothing, Function} = nothing,
146+
labelFilter::Union{Nothing, Function} = nothing,
147+
tagsFilter::Union{Nothing, Function} = nothing,
148+
typeFilter::Union{Nothing, Function} = nothing,
137149
)
138-
139-
# variables = map(v -> v.dfgNode, filter(n -> n.dfgNode isa VariableCompute, vertices(dfg.g)))
140150
variables = collect(values(dfg.g.variables))
141151

142-
!isnothing(regexFilter) &&
143-
filter!(v -> occursin(regexFilter, String(v.label)), variables)
144-
145-
solvable != 0 && filter!(v -> _isSolvable(dfg, v.label, solvable), variables)
146-
147-
!isempty(tags) && filter!(v -> !isempty(intersect(v.tags, tags)), variables)
152+
if !isnothing(regex)
153+
# NOTE that contains(regex::Regex) is not supported by the NvaDFG.
154+
Base.depwarn(
155+
"The regex filter argument is deprecated, use kwarg `labelFilter=contains(regex)` instead", #v0.28
156+
:getVariable,
157+
)
158+
filterDFG!(variables, contains(regex), (String getLabel))
159+
end
160+
if !isempty(tags)
161+
# NOTE that !isdisjoint is not supported by NvaDFG.
162+
Base.depwarn(
163+
"tags kwarg is deprecated, use kwarg `tagsFilter = !isdisjoint(tags)`` instead", #v0.28
164+
:getVariable,
165+
)
166+
filterDFG!(variables, !isdisjoint(tags), getTags)
167+
end
168+
if !isnothing(solvable)
169+
#TODO review. just one solvableFilter or keep solvable as well.
170+
Base.depwarn(
171+
"solvable kwarg is deprecated, use kwarg `solvableFilter = (>=solvable)`` instead", #v0.28
172+
:getVariable,
173+
)
174+
filterDFG!(variables, >=(solvable), getSolvable)
175+
end
148176

149-
!isnothing(solvableFilter) && filter!(v -> solvableFilter(getSolvable(v)), variables)
177+
filterDFG!(variables, labelFilter, (String getLabel))
178+
filterDFG!(variables, solvableFilter, getSolvable)
179+
filterDFG!(variables, tagsFilter, getTags)
180+
filterDFG!(variables, typeFilter, getVariableType)
150181

151182
return variables
152183
end
@@ -155,23 +186,36 @@ function listVariables(
155186
dfg::GraphsDFG,
156187
regexFilter::Union{Nothing, Regex} = nothing;
157188
tags::Vector{Symbol} = Symbol[],
158-
solvable::Int = 0,
159-
solvableFilter::Union{Nothing, Base.Fix2} = nothing,
189+
solvable::Union{Nothing, Int} = nothing,
190+
solvableFilter::Union{Nothing, Function} = nothing,
191+
tagsFilter::Union{Nothing, Function} = nothing,
192+
typeFilter::Union{Nothing, Function} = nothing,
193+
labelFilter::Union{Nothing, Function} = nothing,
160194
)
161-
162-
# variables = map(v -> v.dfgNode, filter(n -> n.dfgNode isa VariableCompute, vertices(dfg.g)))
163-
if length(tags) > 0
195+
if !isnothing(solvableFilter) ||
196+
!isnothing(tagsFilter) ||
197+
!isnothing(typeFilter) ||
198+
!isnothing(regexFilter) || #TODO deprecated
199+
!isempty(tags) || #TODO deprecated
200+
!isnothing(solvable) #TODO Maybe deprecated?
164201
return map(
165-
v -> v.label,
166-
getVariables(dfg, regexFilter; tags = tags, solvable = solvable),
167-
)::Vector{Symbol}
202+
getLabel,
203+
getVariables(
204+
dfg,
205+
regexFilter;
206+
tags,
207+
solvable,
208+
solvableFilter,
209+
tagsFilter,
210+
typeFilter,
211+
labelFilter,
212+
),
213+
)
168214
else
169-
variables = copy(dfg.g.variables.keys)
170-
!isnothing(regexFilter) && filter!(v -> occursin(regexFilter, String(v)), variables)
171-
solvable != 0 && filter!(vId -> _isSolvable(dfg, vId, solvable), variables)
172-
!isnothing(solvableFilter) &&
173-
filter!(v -> solvableFilter(getSolvable(dfg, v)), variables)
174-
return variables::Vector{Symbol}
215+
# Is it ok to continue using the internal keys property? collect(keys(dfg.g.variables)) allowcates a lot.
216+
labels = copy(dfg.g.variables.keys)
217+
filterDFG!(labels, labelFilter, string)
218+
return labels
175219
end
176220
end
177221

src/services/AbstractDFG.jl

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -393,9 +393,22 @@ function deleteFactor! end
393393

394394
"""
395395
$(SIGNATURES)
396-
List the DFGVariables in the DFG.
397-
Optionally specify a label regular expression to retrieves a subset of the variables.
398-
Tags is a list of any tags that a node must have (at least one match).
396+
Get the variables in the DFG as a Vector, supporting various filters.
397+
398+
Arguments
399+
- `regexFilt`: Optional Regex to filter variable labels (deprecated, use `labelFilter` instead).
400+
Keyword arguments
401+
- `tags`: Vector of tags; only variables with at least one matching tag are returned.
402+
- `solvable`: Optional Int; only variables with `solvable >= solvable` are returned.
403+
- `solvableFilter`: Optional function to filter on the `solvable` property, eg `>=(1)`.
404+
- `labelFilter`: Optional function to filter on label e.g., `contains(r"x1")`.
405+
- `tagsFilter`: Optional function to filter on tags, eg. `⊇([:x1])`.
406+
- `typeFilter`: Optional function to filter on the variable type.
407+
408+
Returns
409+
- `Vector{<:AbstractGraphVariable}` matching the filters.
410+
411+
See also: [`listVariables`](@ref), [`ls`](@ref)
399412
"""
400413
function getVariables end
401414

@@ -665,11 +678,11 @@ Notes:
665678
- Returns `Vector{Symbol}`
666679
"""
667680
function ls(
668-
dfg::G,
681+
dfg::AbstractDFG,
669682
regexFilter::Union{Nothing, Regex} = nothing;
670683
tags::Vector{Symbol} = Symbol[],
671684
solvable::Int = 0,
672-
) where {G <: AbstractDFG}
685+
)
673686
return listVariables(dfg, regexFilter; tags = tags, solvable = solvable)
674687
end
675688

src/services/CompareUtils.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ function compareFactorGraphs(
544544
:solverParams,
545545
:factorOperationalMemoryType,
546546
:agent,
547-
:graph
547+
:graph,
548548
]
549549
skiplist = union(skiplist, skip)
550550
@warn "compareFactorGraphs will skip comparisons on: $skiplist"

0 commit comments

Comments
 (0)