Skip to content

Commit cc2f375

Browse files
committed
Substituting on heap ports
1 parent a1d7676 commit cc2f375

6 files changed

Lines changed: 229 additions & 152 deletions

File tree

lib/language/ic/parser.effekt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import lib/language/parser
66
def parsePos(): Pos / Parser = {
77
val name = ident()
88
punct("+")
9-
Pos(name)
9+
Pos(name.ref)
1010
}
1111

1212
def parseNeg(): Neg / Parser = {
1313
val name = ident()
1414
punct("-")
15-
Neg(name)
15+
Neg(name.ref)
1616
}
1717

1818
def parseAbstractor(): Constructor / Parser = {

lib/language/ic/ruler.effekt

Lines changed: 138 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import lib/language/ic/term
77

88
type StepMode { Deterministic(); NonDeterministic(); }
99

10+
record Substitution(from: SuperPort, to: SuperPort)
11+
1012
effect fresh(): Int
1113

1214
def freshener[R] { prog: => R / fresh }: R = {
@@ -18,10 +20,14 @@ def freshener[R] { prog: => R / fresh }: R = {
1820
}
1921
}
2022

21-
interface Rewrite {
23+
interface Interact {
2224
def annihilate(ctor: Constructor): Unit
2325
def construct(ctor: Constructor): Unit
24-
def substitute(from: SuperPort, to: SuperPort): Unit
26+
def substitute(substitution: Substitution): Unit
27+
28+
def potential(port: PortRef): Unit
29+
def target(from: String): SuperPort
30+
def wire(from: Neg, to: Pos): Unit
2531
}
2632

2733
def redexes(program: Program): Unit / emit[Redex] =
@@ -31,44 +37,32 @@ def redexes(program: Program): Unit / emit[Redex] =
3137
if (ctorA.canInteract && ctorB.canInteract && ctorA.pp.name == ctorB.pp.name)
3238
do emit(Redex(ctorA, ctorB))
3339

34-
def apply(port: SuperPort, substitutions: Ref[Map[SuperPort, SuperPort]]): SuperPort =
35-
if (substitutions.get.contains(port)) {
36-
val newPort = substitutions.get.getOrElse(port) { panic("wtf") }
37-
substitutions.map { m => m.delete(port) }
38-
newPort
39-
} else port
40-
41-
// Net...[x^p/y^p]...[z^p/x^p]... --> Net...[z^p/y^p]...
42-
// Net...[z^p/x^p]...[x^p/y^p]... --> Net...[z^p/y^p]...
43-
def apply(substitutees: Ref[Map[SuperPort, SuperPort]]) = {
44-
def go(): Unit = {
45-
var changed = false
46-
substitutees.get.toList.foreach { (t){label} =>
47-
val (to, from) = t
48-
if (substitutees.get.contains(from)) {
49-
changed = true
50-
val newFrom = substitutees.get.getOrElse(from) { panic("wtf") }
51-
substitutees.map { m => m.put(to, newFrom) }
52-
substitutees.map { m => m.delete(from) }
53-
label.break()
54-
}
55-
}
56-
if (changed) go()
57-
}
58-
go()
59-
}
40+
// TODO: this is horribly inefficient for now, but could be very efficient
41+
def redexes(program: Program, potential: List[PortRef]): Unit / emit[Redex] =
42+
with val ref = potential.foreach
43+
val port = ref.get
44+
with val ctorA = for { program() }
45+
with val ctorB = for { program() }
46+
if (ctorA.term::pp is Left(Neg(_)) and ctorB.term::pp is Right(Pos(_)))
47+
if (ctorA.canInteract && ctorB.canInteract && ctorA.pp.name == port && ctorB.pp.name == port)
48+
do emit(Redex(ctorA, ctorB))
6049

61-
/// applies one-shot substitutions on constructor
62-
def apply(ctor: Constructor, substitutions: Ref[Map[SuperPort, SuperPort]]): Constructor =
63-
ctor.map { p => p.apply(substitutions).name }
50+
def freshWires(n: Int): (List[Neg], List[Pos]) / { Interact, fresh } = n.list::build { i =>
51+
val x = do fresh().show
52+
val from = Neg(x.ref)
53+
val to = Pos(x.ref)
54+
do wire(from, to)
55+
(from, to)
56+
}.unzip
6457

65-
def interact(redex: Redex): Unit / { Rewrite, fresh } = redex match {
66-
// @(f-, a-, k+) <> λ(x+, b-, f+) ~> [x+/a+][b-/k-]
58+
def interact(redex: Redex): Unit / { Interact, fresh } = redex match {
59+
// @(f-, a-, k+) <> λ(x+, b-, f+) ~> [a-/x-][b-/k-]
6760
// beta-reduction: APP-LAM
6861
case Redex(Applicator(Neg(f1), Neg(a), Pos(k)), Abstractor(Pos(x), Neg(b), Pos(f2))) and f1 == f2 => {
69-
// do substitute(Pos(x).super, Pos(a).super)
70-
do substitute(Neg(a).super, Neg(x).super)
71-
do substitute(Neg(b).super, Neg(k).super)
62+
do substitute(Substitution(Neg(a).super, do target(Pos(x).show)))
63+
do substitute(Substitution(Neg(b).super, do target(Pos(k).show)))
64+
do potential(a) // TODO: theoretically, we'd only need to somehow check if Neg(a) is at a principal port
65+
do potential(b)
7266
}
7367

7468
// δ(k-, k1+, ..., kn+) <> λ(x+, b-, k+) ~> σ(x+, x1-, ..., xn-), δ(b-, b1+, ..., bn+),
@@ -77,13 +71,16 @@ def interact(redex: Redex): Unit / { Rewrite, fresh } = redex match {
7771
case Redex(Duplicator(Neg(k1), ks, l), Abstractor(Pos(x), Neg(b), Pos(k2))) and k1 == k2 => {
7872
with on[OutOfBounds].panic() // can not happen
7973
val n = ks.size
80-
val xs = n.list::build { i => do fresh().show }
81-
val bs = n.list::build { i => do fresh().show }
82-
do construct(Superposer(Pos(x), xs.map { xi => Neg(xi) }, l))
83-
do construct(Duplicator(Neg(b), bs.map { bi => Pos(bi) }, l))
74+
val (xsNeg, xsPos) = n.freshWires()
75+
val (bsNeg, bsPos) = n.freshWires()
76+
do construct(Superposer(Pos(x), xsNeg, l))
77+
do construct(Duplicator(Neg(b), bsPos, l))
8478
0.each(n) { i =>
85-
do construct(Abstractor(Pos(xs.get(i)), Neg(bs.get(i)), ks.get(i)))
79+
do construct(Abstractor(xsPos.get(i), bsNeg.get(i), ks.get(i)))
8680
}
81+
do potential(x)
82+
do potential(b)
83+
ks.foreach { k => do potential(k.port) }
8784
}
8885

8986
// @(f-, a-, k+) <> σ(f+, f1-, ..., fn-) ~> σ(k+, k1-, ..., kn-), δ(a-, a1+, ..., an+),
@@ -92,20 +89,24 @@ def interact(redex: Redex): Unit / { Rewrite, fresh } = redex match {
9289
case Redex(Applicator(Neg(f1), Neg(a), Pos(k)), Superposer(Pos(f2), fs, l)) and f1 == f2 => {
9390
with on[OutOfBounds].panic() // can not happen
9491
val n = fs.size
95-
val ks = n.list::build { i => do fresh().show }
96-
val as = n.list::build { i => do fresh().show }
97-
do construct(Superposer(Pos(k), ks.map { ki => Neg(ki) }, l))
98-
do construct(Duplicator(Neg(a), as.map { ai => Pos(ai) }, l))
92+
val (ksNeg, ksPos) = n.freshWires()
93+
val (asNeg, asPos) = n.freshWires()
94+
do construct(Superposer(Pos(k), ksNeg, l))
95+
do construct(Duplicator(Neg(a), asPos, l))
9996
0.each(n) { i =>
100-
do construct(Applicator(fs.get(i), Neg(as.get(i)), Pos(ks.get(i))))
97+
do construct(Applicator(fs.get(i), asNeg.get(i), ksPos.get(i)))
10198
}
99+
fs.foreach { f => do potential(f.port) }
100+
do potential(a)
101+
do potential(k)
102102
}
103103

104104
// δ(k-) <> σ(k+, i1-, ..., im-) ~> δ(i1-), ..., δ(in-)
105105
// asymmetric eraser annihilation: DUP-SUP
106106
case Redex(Duplicator(Neg(k1), Nil(), l1), Superposer(Pos(k2), ins, l2)) and k1 == k2 => {
107107
ins.foreach { i =>
108108
do construct(Duplicator(i, Nil(), l1))
109+
do potential(i.port)
109110
}
110111
}
111112

@@ -114,15 +115,17 @@ def interact(redex: Redex): Unit / { Rewrite, fresh } = redex match {
114115
case Redex(Duplicator(Neg(k1), out, l1), Superposer(Pos(k2), Nil(), l2)) and k1 == k2 => {
115116
out.foreach { o =>
116117
do construct(Superposer(o, Nil(), l2))
118+
do potential(o.port)
117119
}
118120
}
119121

120122
// δ(k-, o1+, ..., on+) <> σ(k+, i1-, ..., im-) ~> [i1-/o1-]...[in-/on-]
121123
// symmetric duplicator annihilation: DUP-SUP
122124
case Redex(Duplicator(Neg(k1), out, l1), Superposer(Pos(k2), ins, l2)) and k1 == k2 and l1 == l2 and out.size == ins.size => {
123125
list::zip(out, ins).foreach { case (o, i) =>
124-
do substitute(i.super, o.negate.super)
125-
// do substitute(o.super, i.negate.super)
126+
// TODO: pre-substitute
127+
do substitute(Substitution(i.super, do target(o.show)))
128+
do potential(i.port)
126129
}
127130
}
128131

@@ -133,15 +136,20 @@ def interact(redex: Redex): Unit / { Rewrite, fresh } = redex match {
133136
with on[OutOfBounds].panic() // can not happen
134137
val n = out.size
135138
val m = ins.size
136-
val fresh = max(n, m).list::build { _ => do fresh().show }
137-
0.each(m) { i => // duplicators
138-
val outM = n.list::build { j => Pos(s"${fresh.get(i)}_${j.show}") }
139-
do construct(Duplicator(ins.get(i), outM, l1))
140-
}
141-
0.each(n) { i => // superposers
142-
val insN = m.list::build { j => Neg(s"${fresh.get(j)}_${i.show}") }
143-
do construct(Superposer(out.get(i), insN, l2))
139+
val d = max(n, m)
140+
141+
0.each(d) { i =>
142+
val (insN, outM) = d.freshWires() // TODO: this constructs additional wires in unbalanced dups
143+
if (i < m) {
144+
do construct(Duplicator(ins.get(i), outM.take(m), l1))
145+
}
146+
if (i < n) {
147+
do construct(Superposer(out.get(i), insN.take(n), l2))
148+
}
144149
}
150+
151+
ins.foreach { i => do potential(i.port) }
152+
out.foreach { o => do potential(o.port) }
145153
}
146154

147155
case _ => {
@@ -150,43 +158,92 @@ def interact(redex: Redex): Unit / { Rewrite, fresh } = redex match {
150158
}
151159
}
152160

153-
def step_(program: Program, redex: Redex): Unit / { emit[Constructor], fresh } = {
161+
/// build up a static, doubly linked relationship between positive and negative ports
162+
/// the relationship is from SuperPort.show to SuperPort
163+
def wiring(program: Program): Map[String, SuperPort] = {
164+
var wiring: Map[String, SuperPort] = emptyGeneric() // pname -> target port
165+
var map: Map[String, SuperPort] = emptyGeneric() // name -> own port
166+
167+
def link(port: SuperPort): Unit = {
168+
map.get(port.name) match {
169+
case Some(target) =>
170+
wiring = wiring.put(port.show, target).put(target.show, port)
171+
case None() =>
172+
map = map.put(port.name, port)
173+
}
174+
}
175+
176+
try program()
177+
with emit[Constructor] { ctor =>
178+
ctor.ports.foreach { port => link(port) }
179+
resume(())
180+
}
181+
wiring
182+
}
183+
184+
def substitute(substitution: Substitution, wiring: Ref[Map[String, SuperPort]]) = {
185+
val Substitution(from, to) = substitution
186+
println(s"[${from.show}/${to.show}]")
187+
// val x = to.port.get
188+
to.port.set(from.port.get)
189+
// from.port.set(x)
190+
wiring.map { w => w.put(to.show, from).put(from.show, to) }
191+
}
192+
193+
def step_(program: Program, redex: Redex): List[PortRef] / { emit[Constructor], fresh } = {
154194
val annihilated = ref[Set[Constructor]](emptyGeneric())
155-
val constructed = ref[Set[Constructor]](emptyGeneric())
156-
val substitutions = ref[Map[SuperPort, SuperPort]](emptyGeneric())
195+
val constructed = ref[List[Constructor]](Nil())
196+
val potential = ref[List[PortRef]](Nil())
197+
val wiring = ref(program.wiring())
198+
199+
println(program.show)
157200

158201
try {
159202
redex.interact()
160203
do annihilate(redex.left)
161204
do annihilate(redex.right)
162-
} with Rewrite {
205+
} with Interact {
163206
def annihilate(ctor) = resume(annihilated.map { s => s.insert(ctor) })
164207
def construct(ctor) = resume(do emit(ctor))
165-
def substitute(from, to) = resume(substitutions.map { m => m.put(to, from) })
208+
def substitute(substitution) = resume(substitution.substitute(wiring))
209+
def potential(port) = resume(potential.map { l => Cons(port, l) })
210+
def target(from) = resume(wiring.get.getOrElse(from) { panic(s"invalid target: ${from.show}") })
211+
def wire(from, to) = resume(wiring.map { w => w.put(from.show, to.super).put(to.show, from.super) })
166212
}
167213

168-
// print("\nsubst before: ")
169-
// substitutions.get.foreach { (to, from) => print("${from.show}/${to.show}, ") }
170-
substitutions.apply()
171-
// print("\n subst after: ")
172-
// substitutions.get.foreach { (to, from) => print("${from.show}/${to.show}, ") }
173-
// println("\n")
174-
214+
println("\nemitting:")
175215
for { program() } { ctor =>
176-
if (not(annihilated.get.contains(ctor)))
177-
do emit(ctor.apply(substitutions))
216+
if (not(annihilated.get.contains(ctor))) {
217+
println(ctor.show)
218+
do emit(ctor)
219+
}
178220
}
221+
222+
potential.get
223+
}
224+
225+
def pop[A] { stream: => Unit / emit[A] }: A / { fail, emit[A] } = {
226+
try {
227+
def body(): Option[A] = { stream(); None() }
228+
body()
229+
} with emit[A] { a =>
230+
val next = resume(())
231+
if (next is Some(a)) do emit(a)
232+
Some(a)
233+
}.getOrFail
179234
}
180235

181-
def choose(mode: StepMode, redexes: RedexBag): Redex / fail = mode match {
182-
case Deterministic() => first[Redex] { redexes() }
183-
case NonDeterministic() => choose { redexes() }
236+
def pop(mode: StepMode, redexes: RedexBag): Redex / { fail, emit[Redex] } = mode match {
237+
case Deterministic() => pop[Redex] { redexes() }
238+
case NonDeterministic() => choose { redexes() } // TODO
184239
}
185240

186-
// TODO: emit new redexes directly during interactions (wires get reconnected, check if same port)
187241
def step(program: Program, mode: StepMode, redexes: RedexBag): Unit / { emit[Constructor], emit[Redex], fresh, fail } = {
188-
val redex = choose(mode, redexes)
189-
val program: Program = collect { program.step_(redex) }
242+
//val redex = pop(mode, redexes) // TODO!
243+
val redex = pop(NonDeterministic(), redexes)
244+
println(s"\n=== REDEX: ${redex.show} ===")
245+
val tup: (List[PortRef], Program) = returning::collect { program.step_(redex) }
246+
val (potential, program) = tup
190247

191248
// println(list::collect[Constructor] { program() }.show { ctor => ctor.show })
192249

@@ -195,10 +252,12 @@ def step(program: Program, mode: StepMode, redexes: RedexBag): Unit / { emit[Con
195252
val negPorts = collect[Constructor] { program() }.flatMap { ctor => ctor.ports }.filter { p => p.isNeg }.map { p => p.name }.set::fromListGeneric
196253
if (posPorts.difference(negPorts).size != 0) {
197254
println("\n!!! FATAL " ++ posPorts.toList.show ++ " vs " ++ negPorts.toList.show)
198-
println("\nREDEX: " ++ redex.left.show ++ " - " ++ redex.right.show)
255+
// println("\nREDEX: " ++ redex.left.show ++ " - " ++ redex.right.show)
199256
println(posPorts.difference(negPorts).toList.show)
200257
} else {
201-
program.redexes() // TODO: this could be an expensive call, we should not do it at every step.
258+
// println(potential)
259+
//program.redexes(potential)
260+
program.redexes()
202261
program()
203262
}
204263
}
@@ -212,7 +271,7 @@ def normalize(program: Program, redexes: RedexBag, max: Int): (Program, Int) = {
212271
with exhaustively((empty(), 0))
213272
count = count + 1
214273
state = returning::collect {
215-
collect { state.first.step(NonDeterministic(), state.second) } }
274+
collect { state.first.step(Deterministic(), state.second) } }
216275
if (count > max) do fail()
217276
(state.first, count)
218277
}

0 commit comments

Comments
 (0)