11from functools import partial
2+ from operator import length_hint
23from unification import var , isvar
34
45from unification import reify
910from kanren .core import conde , lall
1011from kanren .goals import conso , fail
1112
12- from ..etuple import etuplize , etuple , ExpressionTuple
13+ from ..etuple import etuplize , ExpressionTuple
1314
1415
1516def lapply_anyo (relation , l_in , l_out , null_type = False , skip_op = True ):
@@ -109,28 +110,69 @@ def goal(s):
109110 return goal
110111
111112
112- def reduceo (relation , in_expr , out_expr ):
113+ def reduceo (relation , in_term , out_term ):
113114 """Relate a term and the fixed-point of that term under a given relation.
114115
115116 This includes the "identity" relation.
116117 """
117- expr_rdcd = var ()
118- return conde (
119- # The fixed-point is another reduction step out.
120- [(relation , in_expr , expr_rdcd ), (reduceo , relation , expr_rdcd , out_expr )],
121- # The fixed-point is a single-step reduction.
122- [(relation , in_expr , out_expr )],
123- )
118+
119+ def reduceo_goal (s ):
120+
121+ nonlocal in_term , out_term
122+
123+ in_term_rf , out_term_rf = reify ((in_term , out_term ), s )
124+
125+ # The result of reducing the input graph once
126+ term_rdcd = var ()
127+
128+ # Are we working "backward" and (potentially) "expanding" a graph
129+ # (e.g. when the relation is a reduction rule)?
130+ is_expanding = isvar (in_term_rf )
131+
132+ # One application of the relation assigned to `term_rdcd`
133+ single_apply_g = (relation , in_term , term_rdcd )
134+
135+ # Assign/equate (unify, really) the result of a single application to
136+ # the "output" term.
137+ single_res_g = eq (term_rdcd , out_term )
138+
139+ # Recurse into applications of the relation (well, produce a goal that
140+ # will do that)
141+ another_apply_g = reduceo (relation , term_rdcd , out_term )
142+
143+ # We want the fixed-point value to show up in the stream output
144+ # *first*, but that requires some checks.
145+ if is_expanding :
146+ # When an un-reduced term is a logic variable (e.g. we're
147+ # "expanding"), we can't go depth first.
148+ # We need to draw the association between (i.e. unify) the reduced
149+ # and expanded terms ASAP, in order to produce finite
150+ # expanded graphs first and yield results.
151+ #
152+ # In other words, there's no fixed-point to produce in this
153+ # situation. Instead, for example, we have to produce an infinite
154+ # stream of terms that have `out_term` as a fixed point.
155+ # g = conde([single_res_g, single_apply_g],
156+ # [another_apply_g, single_apply_g])
157+ g = lall (conde ([single_res_g ], [another_apply_g ]), single_apply_g )
158+ else :
159+ # Run the recursion step first, so that we get the fixed-point as
160+ # the first result
161+ g = lall (single_apply_g , conde ([another_apply_g ], [single_res_g ]))
162+
163+ g = goaleval (g )
164+ yield from g (s )
165+
166+ return reduceo_goal
124167
125168
126169def graph_applyo (
127170 relation ,
128171 in_graph ,
129172 out_graph ,
130173 preprocess_graph = partial (etuplize , shallow = True , return_bad_args = True ),
131- inside = False ,
132174):
133- """Relate the fixed-points of two term-graphs under a given relation .
175+ """Apply a relation to a graph and its subgraphs .
134176
135177 Parameters
136178 ----------
@@ -144,8 +186,6 @@ def graph_applyo(
144186 A unary function that produces an iterable upon which `lapply_anyo`
145187 can be applied in order to traverse a graph's subgraphs. The default
146188 function converts the graph to expression-tuple form.
147- inside: boolean (optional)
148- Process the graph or sub-graphs first.
149189 """
150190
151191 if preprocess_graph in (False , None ):
@@ -155,62 +195,39 @@ def preprocess_graph(x):
155195
156196 def _gapplyo (s ):
157197
158- nonlocal in_graph , out_graph , inside
159-
160- in_rdc = var ()
198+ nonlocal in_graph , out_graph
161199
162200 in_graph_rf , out_graph_rf = reify ((in_graph , out_graph ), s )
163201
164- expanding = isvar ( in_graph_rf )
202+ _gapply = partial ( graph_applyo , relation , preprocess_graph = preprocess_graph )
165203
166- _gapply = partial (
167- graph_applyo ,
168- relation ,
169- preprocess_graph = preprocess_graph ,
170- inside = inside , # expanding and (True ^ inside)
171- )
204+ graph_reduce_gl = (relation , in_graph_rf , out_graph_rf )
172205
173- # This goal reduces the entire graph
174- graph_reduce_gl = (relation , in_graph_rf , in_rdc )
206+ # We need to get the sub-graphs/children of the input graph/node
207+ if not isvar (in_graph_rf ):
208+ in_subgraphs = preprocess_graph (in_graph_rf )
209+ in_subgraphs = None if length_hint (in_subgraphs , 0 ) == 0 else in_subgraphs
210+ else :
211+ in_subgraphs = in_graph_rf
175212
176- # This goal reduces children/arguments of the graph
177- subgraphs_reduce_gl = lapply_anyo (
178- _gapply ,
179- preprocess_graph (in_graph_rf ),
180- in_rdc ,
181- null_type = etuple () if expanding else False ,
182- )
213+ if not isvar (out_graph_rf ):
214+ out_subgraphs = preprocess_graph (out_graph_rf )
215+ out_subgraphs = None if length_hint (out_subgraphs , 0 ) == 0 else out_subgraphs
216+ else :
217+ out_subgraphs = out_graph_rf
183218
184- # Take only one step (e.g. reduce the entire graph and/or its
185- # arguments)
186- reduce_once_gl = eq (in_rdc , out_graph_rf )
219+ conde_args = ([graph_reduce_gl ],)
187220
188- # Take another reduction step on top of the one(s) we already did
189- # (i.e. recurse)
190- reduce_again_gl = _gapply (in_rdc , out_graph_rf )
221+ # This goal reduces sub-graphs/children of the graph.
222+ if in_subgraphs is not None and out_subgraphs is not None :
223+ # We will only include it when there actually are children, or when
224+ # we're dealing with a logic variable (e.g. and "generating"
225+ # children).
226+ subgraphs_reduce_gl = lapply_anyo (_gapply , in_subgraphs , out_subgraphs )
191227
192- # We want the fixed-point value first, but that requires
193- # some checks.
194- if expanding :
195- # When the un-reduced expression is a logic variable (i.e. we're
196- # "expanding" expressions), we can't go depth first.
197- # We need to draw the association between (i.e. unify) the reduced
198- # and expanded expressions ASAP, in order to produce finite
199- # expanded graphs first and yield results.
200- g = conde (
201- [reduce_once_gl , graph_reduce_gl ],
202- [reduce_again_gl , graph_reduce_gl ],
203- [reduce_once_gl , subgraphs_reduce_gl ],
204- [reduce_again_gl , subgraphs_reduce_gl ],
205- )
206- else :
207- # TODO: With an explicit simplification order, could we determine
208- # whether or not simplifying the sub-expressions or the expression
209- # itself is more efficient?
210- g = lall (
211- conde ([graph_reduce_gl ], [subgraphs_reduce_gl ]),
212- conde ([reduce_again_gl ], [reduce_once_gl ]),
213- )
228+ conde_args += ([subgraphs_reduce_gl ],)
229+
230+ g = conde (* conde_args )
214231
215232 g = goaleval (g )
216233 yield from g (s )
0 commit comments