We directly test the nodes for generating terms by recursive and inductive definitions on natural numbers, obtained by choosing codomain/target family and filling in recursion data.
import $ivy.`io.github.siddhartha-gadgil::provingground-core-jvm:0.1.0`
import provingground._ , interface._, HoTT._
import provingground.{FiniteDistribution => FD}
import learning._
We define the inductive structure on the natural numbers.
val Nat = Type.sym
val zero = Nat.sym
val succ = (Nat ->: Nat).sym
val natIntros = Vector(zero, succ)
import induction._
val natStr = provingground.induction.ExstInducStrucs.get(Nat, natIntros)
val natDef = ExstInducDefn(Nat, natIntros, natStr)
Next, we set up for generation
val ts = TermState(FD.unif(Nat, zero, succ), FD.unif(Nat, Type), inds = FD.unif(natDef))
val tg = TermGenParams()
val mfd = tg.monixFD
A check that inductive types are picked up from the initial distribution.
import TermRandomVars._
val idT = mfd.varDist(ts)(InducDefns, 0.1)
import monix.execution.Scheduler.Implicits.global
repl.pprinter.bind(translation.FansiShow.fansiPrint)
val inducFD = idT.runSyncUnsafe()
We generate the domain from the inductive type. Nothing to do in the simple case.
val domT = mfd.varDist(ts)(domForInduc(natDef), 0.1)
val domFD = domT.runSyncUnsafe()
We next define functions recursively
val ndT = mfd.nodeDist(ts)(tg.Gen.recFuncFoldedNode, 0.001)
val nfd = ndT.runSyncUnsafe()
val nnT = mfd.varDist(ts)(termsWithTyp(Nat ->: Nat), 0.1)
val nnD = nnT.runSyncUnsafe()
nfd.map(_.typ).flatten
We make our first attempt at inductive definitions. We get some, but of only one type. We verify that this is because of the lack of richness in our initial state.
val nidT = mfd.nodeDist(ts)(tg.Gen.inducFuncFoldedNode, 0.00001)
val nifd = nidT.runSyncUnsafe()
nifd.map(_.typ).flatten
termsWithTyp(typFamilyTarget(Nat).get)
val tfT = mfd.varDist(ts)(termsWithTyp(typFamilyTarget(Nat).get), 0.001)
val tfD = tfT.runSyncUnsafe()
We now enhance our generation state and see that we get inductive definitions including a simple proof.
val n = Nat.sym
val rel = (Nat ->: Type).sym
val allRel = (n ~>: rel(n)).sym
val ts1 = TermState(FD.unif(Nat, zero, succ, rel, allRel), FD.unif(Nat, Type), inds = FD.unif(natDef))
val tfT1 = mfd.varDist(ts1)(termsWithTyp(typFamilyTarget(Nat).get), 0.001)
val tfD1 = tfT1.runSyncUnsafe()
val nidT1 = mfd.nodeDist(ts1)(tg.Gen.inducFuncFoldedNode, 0.000005)
val nifd1 = nidT1.runSyncUnsafe()
nifd1.map(_.typ).flatten
val indF = natStr.inducOpt(Nat, n :-> rel(n)).get
indF.typ
nifd1.filter(_.typ == n ~>: rel(n))
val A = "A" :: Type
val List = "List" :: Type ->: Type
val nil = "nil" :: (A ~>: List(A))
val cons = "cons" :: A ~>: (A ->: List(A) ->: List(A))
val listAIntros = Vector(nil(A), cons(A))
val listAStr = ExstInducStrucs.get(List(A), listAIntros)
val listStr = ExstInducStrucs.LambdaInduc(A, listAStr)
val listDef = ExstInducDefn(List, Vector(nil, cons) ,listStr)
val tsL = TermState(FD.unif(Nat, zero, succ, List, nil, cons), FD.unif(Nat, Type), inds = FD.unif(natDef, listDef))
val domLT = mfd.varDist(ts)(domForInduc(listDef), 0.00001)
val domLD = domLT.runSyncUnsafe()
val ndLT = mfd.nodeDist(tsL)(tg.Gen.recFuncFoldedNode, 0.0001)
val ndLD = ndLT.runSyncUnsafe()
ndLD.map(_.typ).flatten
ndLD.support.size
val fns = ndLD.support.groupBy(_.typ)
val lToL = fns(List(Nat) ->: List(Nat))
nil(Nat).typ
cons(Nat).typ
val l0 = cons(Nat)(zero)(nil(Nat))
val l1 = cons(Nat)(succ(zero))(l0)
import Fold._
lToL.map(fn => fn(l1))
val Vec = "Vec" :: Type ->: Nat ->: Type
val vnil = "vnil" :: (A ~>: Vec(A)(zero))
val vcons = "vcons" :: A ~>: (n ~>: (A ->: Vec(A)(n) ->: Vec(A)(succ(n))))
val vecAIntros = Vector(vnil(A), vcons(A))
val vecAStr = ExstInducStrucs.getIndexed(Vec(A), vecAIntros)
val vecStr = ExstInducStrucs.LambdaInduc(A, vecAStr)
val vecDef = ExstInducDefn(Vec, Vector(vnil, vcons) ,vecStr)
val tsV = TermState(FD.unif(Nat, zero, succ, Vec, vnil, vcons), FD.unif(Nat, Type), inds = FD.unif(vecDef))
val domVT = mfd.varDist(ts)(domForInduc(vecDef), 0.00001)
val domVD = domVT.runSyncUnsafe()
val ndVT = mfd.nodeDist(tsV)(tg.Gen.recFuncFoldedNode, 0.0001)
val ndVD = ndVT.runSyncUnsafe()
ndVD.map(_.typ)
val vfn = vecStr.recOpt(Vec(Nat), A).get
vfn.typ
vfn("a" :: A)
val a = "a" :: A
vfn(a).typ
val B = "B" :: Type
vecAStr.recOpt(Vec(A), B)
vecStr.recOpt(Vec(A), B)
vecStr.recOpt(Vec(B), Nat)
val ok = vecStr.recOpt(Vec(A), Nat).get
val bok = ok.replace(A, B)
bok.typ
val nok = vecStr.recOpt(Vec(B), Nat).get
vecAStr.subs(A, B).recOpt(Vec(B), Nat)
vecAStr
vecAStr.subs(A, B)
val ndViT = mfd.nodeDist(tsV)(tg.Gen.inducFuncFoldedNode, 0.0001)
val ndViD = ndViT.runSyncUnsafe()
ndViD.map(_.typ).flatten
val tgVT = mfd.nodeDist(tsV)(tg.Gen.codomainNode(Vec(Nat)(succ(zero))), 0.0001)
val tgVD = tgVT.runSyncUnsafe()
tgVD.map(_.typ).flatten
Unify.targetCodomain(vcons, Vec(Nat)(succ(zero)))
Unify.unify(vcons.typ, Vec(Nat)(succ(zero)), (t) => false)
val tgtVT = mfd.nodeDist(tsV)(tg.Gen.foldedTargetFunctionNode(Vec(Nat)(succ(zero))), 0.001)
val tgtVD = tgtVT.runSyncUnsafe()
val tgtNT = mfd.nodeDist(tsV)(tg.Gen.foldedTargetFunctionNode(Nat ->: Nat), 0.001)
val tgtND = tgtNT.runSyncUnsafe()
val node = tg.Gen.foldFuncTarget(succ, Nat, Terms).get
val fd = mfd.nodeDist(tsV)(node, 0.0001).runSyncUnsafe()
val nfd = mfd.nodeDist(tsV)(tg.Gen.codomainNode(Nat ->: Nat), 0.001).runSyncUnsafe()
val ffd = mfd.varDist(tsV)(funcForCod(Nat ->: Nat), 0.0001).runSyncUnsafe()
val indTargT = mfd.nodeDist(ts1)(tg.Gen.targetInducNode(Nat ->: Nat), 0.0001)
indTargT.runSyncUnsafe()