I am simulating inputs which lead through logic gates. I want to get active paths. The approach I have at the moment is to create a link for every active logic gate, to say which sim it is open in. But this isn't so neat. Additionally, there is the problem of recursion, there may be more levels to this. And, when Out1 & Out2 are active, only Out2 is reported.
Is there a way to recurse this type of logic?
Sample data
CREATE
(s1:Sim {id:1}),(s2:Sim{id:2}),(s3:Sim{id:3}),(s4:Sim{id:4}),
(e1:Inputt {name: 'In1'}),(e2:Inputt {name: 'In2'}),(e3:Inputt{name: 'In3'}),(e4:Inputt{name: 'In4'}),(e5:Inputt{name: 'In5'}),(e6:Inputt{name: 'In6'}),
(x1:Outputt {name: 'Out1'}),(x2:Outputt {name: 'Out2'}),
(a1:GateAND),(a2:GateAND),(a3:GateAND),
(o1:GateOR),(o2:GateOR),
(e1)<-[:ACTIVE]-(s1),
(e3)<-[:ACTIVE]-(s1),
(e2)<-[:ACTIVE]-(s2),
(e4)<-[:ACTIVE]-(s2),
(e4)<-[:ACTIVE]-(s3),
(e5)<-[:ACTIVE]-(s3),
(e4)<-[:ACTIVE]-(s4),
(e5)<-[:ACTIVE]-(s4),
(e6)<-[:ACTIVE]-(s4),
(e1)-[:INTO]->(o1),
(e2)-[:INTO]->(o1),
(o1)-[:INTO]->(a1),
(e3)-[:INTO]->(a1),
(e4)-[:INTO]->(a2),
(e5)-[:INTO]->(a2),
(a1)-[:INTO]->(o2),
(a2)-[:INTO]->(o2),
(o2)-[:INTO]->(a3),
(e6)-[:INTO]->(a3),
(o2)-[:OUT]->(x1),
(a3)-[:OUT]->(x2)
Add:
// First level OR gates only need one active input
MATCH (sim:Sim)-[:ACTIVE]->(inp:Inputt)-[:INTO]->(rule:GateOR)
WITH sim, rule, COUNT(inp) AS sfrEventCount
WHERE sfrEventCount >=1
MERGE (sim)-[:ACTIVE]->(rule)
// Add active to and gates
MATCH (inp:Inputt|GateOR)-[:INTO]->(gate:GateAND)
WITH ELEMENTID(gate) as gateId, COUNT(inp) AS inpCount
WITH apoc.map.fromPairs(COLLECT([gateId, inpCount])) AS gateIdCountMap
MATCH (sim:Sim)-[:ACTIVE]->(inp2:Inputt|GateOR)-[:INTO]->(rule)
WITH sim, rule, gateIdCountMap, COUNT(inp2) AS sinpCount
WHERE gateIdCountMap[ELEMENTID(rule)] = sinpCount
MERGE (sim) -[:ACTIVE]->(rule)
// Next level OR
MATCH (sim:Sim)-[:ACTIVE]->(inp:GateOR|GateAND)-[:INTO]->(rule:GateOR)
WITH sim, rule, COUNT(inp) AS sfrEventCount
WHERE sfrEventCount >=1
MERGE (sim)-[:ACTIVE]->(rule)
// DO the and again
// Outputs
MATCH (s:Sim)-[:ACTIVE]->(gate:GateAND|GateOR)-[:OUT]->(x:Outputt), (g2:GateAND|GateOR)<--(s2:Sim)
WHERE NOT EXISTS ((gate)-->(g2))
MERGE (s)-[:ACTIVE]->(x)
Results of interest. I would expect Out1 to be active in Sim 1 but somewhere Sim 4 took priority.
MATCH (s:Sim)-->(x:Outputt) return s, x
This query will find the legal paths:
MATCH path = (sim:Sim)-[:ACTIVE]->(input:Inputt)-[:INTO|OUT*0..]->(output:Outputt)
WHERE ALL(node IN nodes(path)[2..-1]
WHERE
(node:GateAND AND ALL(incoming IN [(other)-[:INTO]->(node) | other] WHERE (sim)-[:ACTIVE|INTO*]->(incoming)))
OR
node:GateOR
)
RETURN sim, output, COLLECT(path) AS paths
But note that the query will be slow or even run out of memory if you have a lot of relevant data or long paths. If acceptable, you can ameliorate some of that by imposing a reasonable upper bound on the variable-length path patterns. For example:
MATCH path = (sim:Sim)-[:ACTIVE]->(input:Inputt)-[:INTO|OUT*0..8]->(output:Outputt)
WHERE ALL(node IN nodes(path)[2..-1]
WHERE
(node:GateAND AND ALL(incoming IN [(other)-[:INTO]->(node) | other] WHERE (sim)-[:ACTIVE|INTO*..9]->(incoming)))
OR
node:GateOR
)
RETURN sim, output, COLLECT(path) AS paths
This is the output, given your sample data:
╒══════════════╤═════════════════════════╤════════════════════════════════╕
│sim │output │paths │
╞══════════════╪═════════════════════════╪════════════════════════════════╡
│(:Sim {id: 1})│(:Outputt {name: "Out1"})│[(:Sim {id: 1})-[:ACTIVE]->(:Inp│
│ │ │utt {name: "In3"})-[:INTO]->(:Ga│
│ │ │teAND)-[:INTO]->(:GateOR)-[:OUT]│
│ │ │->(:Outputt {name: "Out1"}), (:S│
│ │ │im {id: 1})-[:ACTIVE]->(:Inputt │
│ │ │{name: "In1"})-[:INTO]->(:GateOR│
│ │ │)-[:INTO]->(:GateAND)-[:INTO]->(│
│ │ │:GateOR)-[:OUT]->(:Outputt {name│
│ │ │: "Out1"})] │
├──────────────┼─────────────────────────┼────────────────────────────────┤
│(:Sim {id: 3})│(:Outputt {name: "Out1"})│[(:Sim {id: 3})-[:ACTIVE]->(:Inp│
│ │ │utt {name: "In4"})-[:INTO]->(:Ga│
│ │ │teAND)-[:INTO]->(:GateOR)-[:OUT]│
│ │ │->(:Outputt {name: "Out1"}), (:S│
│ │ │im {id: 3})-[:ACTIVE]->(:Inputt │
│ │ │{name: "In5"})-[:INTO]->(:GateAN│
│ │ │D)-[:INTO]->(:GateOR)-[:OUT]->(:│
│ │ │Outputt {name: "Out1"})] │
├──────────────┼─────────────────────────┼────────────────────────────────┤
│(:Sim {id: 4})│(:Outputt {name: "Out1"})│[(:Sim {id: 4})-[:ACTIVE]->(:Inp│
│ │ │utt {name: "In4"})-[:INTO]->(:Ga│
│ │ │teAND)-[:INTO]->(:GateOR)-[:OUT]│
│ │ │->(:Outputt {name: "Out1"}), (:S│
│ │ │im {id: 4})-[:ACTIVE]->(:Inputt │
│ │ │{name: "In5"})-[:INTO]->(:GateAN│
│ │ │D)-[:INTO]->(:GateOR)-[:OUT]->(:│
│ │ │Outputt {name: "Out1"})] │
├──────────────┼─────────────────────────┼────────────────────────────────┤
│(:Sim {id: 4})│(:Outputt {name: "Out2"})│[(:Sim {id: 4})-[:ACTIVE]->(:Inp│
│ │ │utt {name: "In6"})-[:INTO]->(:Ga│
│ │ │teAND)-[:OUT]->(:Outputt {name: │
│ │ │"Out2"}), (:Sim {id: 4})-[:ACTIV│
│ │ │E]->(:Inputt {name: "In4"})-[:IN│
│ │ │TO]->(:GateAND)-[:INTO]->(:GateO│
│ │ │R)-[:INTO]->(:GateAND)-[:OUT]->(│
│ │ │:Outputt {name: "Out2"}), (:Sim │
│ │ │{id: 4})-[:ACTIVE]->(:Inputt {na│
│ │ │me: "In5"})-[:INTO]->(:GateAND)-│
│ │ │[:INTO]->(:GateOR)-[:INTO]->(:Ga│
│ │ │teAND)-[:OUT]->(:Outputt {name: │
│ │ │"Out2"})] │
└──────────────┴─────────────────────────┴────────────────────────────────┘