Code Monkey home page Code Monkey logo

stenographs.jl's Introduction

StenoGraphs.jl ― A concise language to write meta graphs

Build Status Coverage

Stenography: a quick way of writing using special signs or abbreviations

StenoGraphs.jl lets you quickly write meta graphs. As with shorthand, it is optimized for writing quickly (by humans) but is less quickly read (by computers).

To install StenoGraphs.jl:

import Pkg; Pkg.add("StenoGraphs")

Your first @StenoGraph using StenoGraphs:

using StenoGraphs
@StenoGraph a  b
a → b

By the way, typing arrows can be done quickly on Linux by using Alt Gr + I resulting in and Alt GR + I resulting in . All other platforms must use \leftarrow + Tab or \rightarrow + Tab.

Multiple Nodes

Multiple nodes on one side lead to multiple edges:

@StenoGraph [a b]  c
a → c
b → c

There are two desirable outcomes for multiple edges on both sides, either element-wise edges or cross-product. The single line arrow () means element-wise and double line arrow () means cross-product (don't tell anyone but for a single node on one side is converted to for convinience).

@StenoGraph [a b]  [c d]
a → c
b → d

@StenoGraph [a b]  [c d]
a → c
a → d
b → c
b → d

Modification

Modification is done by overloading * for types of Modifier.

Let's define a Modifier:

struct Weight <: EdgeModifier
    w::Number
end

An EdgeModifier can be directly applied to edges:

@StenoGraph (a  b) * Weight(1)
a → b * Main.Weight(1)

Multiplying a Node with an EdgeModifier leads to a ModifyingNode.

:b * Weight(1)
b * Main.Weight(1)

A ModifyingNode will modify its edges:

@StenoGraph a  b * Weight(1)
a → b * Main.Weight(1)

To modify Nodes directly with a NodeModifier to create a ModifiedNode (instead of ModifyingNode) we overload ^:

struct NodeLabel <: NodeModifier
    l
end

@StenoGraph a  b^NodeLabel("Dickes B")
a → b^Main.NodeLabel("Dickes B")

Related Software

The R programming language has formulas of the form a ~ b to specify regressions. This inspired Yves Rosseel to create a very concise, yet expressive syntax for Structural Equation Models for lavaan. Stenographs.jl tries to maintain the best features of this syntax while creating Julia Objects that represent a graph (i.e., similar to MetaGraphs).

stenographs.jl's People

Contributors

aaronpeikert avatar leoniehagitte avatar lkosanke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

stenographs.jl's Issues

Int are not converted to Symbols

To specify meanstructures, I would like write do something like
1 → x1 + x2

However, this gives a method error:
ERROR: LoadError: MethodError: no method matching keep(::Int64, ::Type{StenoGraphs.Right}, ::Type{Node})

This however works:
Symbol("1") → x1 + x2

maybe we could also enable the other, shorter and more readable syntax?

NaN is parsed as a Symbol

I would like to allow the Syntax f1 → fixed(1.0, NaN)*x1 to fix the loading in the first group, but not the second. However, this fails unexpectedly:

While typeof(NaN) is Float64, and typeof(fixed(1.0, NaN)) results in Fixed{Tuple{Float64, Float64}}, using it inside the @StenoGraphs macro results in the Modifier Fixed{Tuple{Float64, Symbol}}((1.0, :NaN)).

MWE:

struct Fixed{N} <: EdgeModifier
    value::N
end
fixed(args...) = Fixed(args)

@StenoGraph begin
  f1   fixed(1.0, NaN)*x1
end

Have a `StenoGraph` or `AbstractStenoGraph` type

I think this would be nice for packages building on StenoGraphs, because multiple dispatch has to use something like AbstractArray{T, 1} where {T <: StenoGraphs.AbstractEdge} at the moment.

entering unicode characters?

how are users supposed to input the unicode characters for the various arrows actually? It would be useful to add a list of shortcuts or something for beginners? Otherwise, they end up copy&pasting...?!

⇔ should return only unique combinations

Current behaviour is:

graph = @StenoGraph begin
    [x1, x2, x3]  [x1, x2, x3]
end

x1  x1
x1  x2
x1  x3
x2  x1
x2  x2
x2  x3
x3  x1
x3  x2
x3  x3

Which adds every undirected edge between differing variables twice. This prevents me from specifying saturated models this way. I think it should be

graph = @StenoGraph begin
    [x1, x2, x3]  [x1, x2, x3]
end

x1  x1
x1  x2
x1  x3
x2  x2
x2  x3
x3  x3

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

Convenient construction of multiple nodes

I suggest adding a function Nodes that takes a variable number of arguments and returns a vector of nodes, allowing to write big5 = Nodes(:O, :C, :E, :A, :N) instead of big5 = Node.([:O, :C, :E, :A, :N]).

Type conversion of a vector of `StenoGraphs`

@aaronpeikert : I ran into a question concerning the resulting type of the sampled StenoGraph-Vector:

using StenoGraphs

graph_1 = StenoGraphs.@StenoGraph begin
    # latent regressions
    fac1  fac2
end

struct NodeLabel_2 <: NodeModifier
    l
end
graph_2 = StenoGraphs.@StenoGraph begin
    # latent regressions
    fac1  fac2^NodeLabel_2("Home an der Spree")
end

# These two graphs have slightly different types:
typeof(graph_1)
typeof(graph_2)

# When putting them into a vector, the Vector gets the type `Vector{Vector}`:
[graph_1, graph_2]

I would think, something like Vector{AbstractEdge} would be more appropriate? However, i'm not quite sure how to achive that. I played around with convert() a bit, but wasn't successfull.

allow real broadcast on arrow

For the moment we use → ← ↔ for broadcasting/elementwise definition of edges and ⇒ ⇐ ⇔for cross product.
But ideally .→ (dot broadcast) would be interpreted as elementwise and →as cross product.

Based on the documentation (broadcast and iteration I started playing around but really nothing more:

struct Edge
    src
    dst
end

struct Arrow
    edges
    lhs
    rhs
end


Edges(lhs, rhs) = [Edge(lhs, rhs)]
Edges(lhs::VecOrMat, rhs::VecOrMat) = Edge.(lhs, rhs)
Edges(lhs, rhs::VecOrMat) = Edge.(Ref(lhs), rhs)
Edges(lhs::VecOrMat, rhs) = Edge.(lhs, Ref(rhs))

Arrow(lhs, rhs) = Arrow(Edges(lhs, rhs), lhs, rhs)

test = Arrow([1; 2; 3], [4; 5; 6])

emptyvec(x::Vector{Eltype}) where Eltype = Vector{Eltype}()
Base.iterate(A::Arrow, state=1) = state > length(A.edges) ? nothing : (Arrow(emptyvec(A.edges), A.lhs[state], A.rhs[state]), state + 1)

for a in test
    println(a)
end

Base.getindex(A::ArrayAndChar{T,N}, inds::Vararg{Int,N}) where {T,N} = A.data[inds...]
Base.setindex!(A::ArrayAndChar{T,N}, val, inds::Vararg{Int,N}) where {T,N} = A.data[inds...] = val
Base.showarg(io::IO, A::ArrayAndChar, toplevel) = print(io, typeof(A), " with char '", A.char, "'")
Base.BroadcastStyle(::Type{<:ArrayAndChar}) = Broadcast.Style{MyType}()

struct MyStyle <: Broadcast.BroadcastStyle end
Base.BroadcastStyle(::Type{<:ArrayAndChar}) = MyStyle()

function Base.similar(bc::Broadcast.Broadcasted{Broadcast.Style{ArrayAndChar}}, ::Type{ElType}) where ElType
    # Scan the inputs for the ArrayAndChar:
    A = find_aac(bc)
    # Use the char field of A to create the output
    ArrayAndChar(similar(Array{ElType}, axes(bc)), A.char)
end

"`A = find_aac(As)` returns the last (!) ArrayAndChar among the arguments."
find_aac(bc::Base.Broadcast.Broadcasted) = find_aac(bc.args)
find_aac(args::Tuple) = find_aac(args[1], find_aac(Base.tail(args)))
find_aac(x) = x
find_aac(::Tuple{}) = nothing
find_aac(::Any, a::ArrayAndChar) = a
find_aac(::Any, rest) = find_aac(rest)

a = ArrayAndChar([1 2; 3 4], 'x')
b = ArrayAndChar([1 1; 1 1], 'b')
c = ArrayAndChar([2 2; 2 2], 'c')
a .+ 1
length
@run a .+ b .+ c

@run 1 .+ 2 .+ c

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.