def sudoku_solver(hints): variables = vars(hints) rows = get_rows(variables) cols = get_columns(rows) sqs = get_squares(rows) return run( 1, variables, lall(*(all_numbers(r) for r in rows)), lall(*(all_numbers(c) for c in cols)), lall(*(all_numbers(s) for s in sqs)), )
def non_obs_graph_applyo(relation, a, b): """Construct a goal that applies a relation to all nodes above an observed random variable. This is useful if you don't want to apply relations to an observed random variable, but you do want to apply them to every term above one and ultimately reproduce the entire graph (observed RV included). Parameters ---------- relation: function A binary relation/goal constructor function a: lvar or meta graph The left-hand side of the relation. b: lvar or meta graph The right-hand side of the relation """ obs_lv, obs_rv_lv = var(), var() rv_op_lv, rv_args_lv, obs_rv_lv = var(), var(), var() new_rv_args_lv, new_obs_rv_lv = var(), var() return lall( # Indicate the observed term (i.e. observation and RV) eq(a, mt.observed(obs_lv, obs_rv_lv)), # Deconstruct the observed random variable (buildo, rv_op_lv, rv_args_lv, obs_rv_lv), # Apply relation to the RV's inputs seq_apply_anyo( partial(tt_graph_applyo, relation), rv_args_lv, new_rv_args_lv, skip_op=False ), # Reconstruct the random variable (buildo, rv_op_lv, new_rv_args_lv, new_obs_rv_lv), # Reconstruct the observation (buildo, mt.observed, etuple(obs_lv, new_obs_rv_lv), b), )
def reduceo_goal(s): nonlocal in_term, out_term in_term_rf, out_term_rf = reify((in_term, out_term), s) # The result of reducing the input graph once term_rdcd = var() # Are we working "backward" and (potentially) "expanding" a graph # (e.g. when the relation is a reduction rule)? is_expanding = isvar(in_term_rf) # One application of the relation assigned to `term_rdcd` single_apply_g = (relation, in_term, term_rdcd) # Assign/equate (unify, really) the result of a single application to # the "output" term. single_res_g = eq(term_rdcd, out_term) # Recurse into applications of the relation (well, produce a goal that # will do that) another_apply_g = reduceo(relation, term_rdcd, out_term) # We want the fixed-point value to show up in the stream output # *first*, but that requires some checks. if is_expanding: # When an un-reduced term is a logic variable (e.g. we're # "expanding"), we can't go depth first. # We need to draw the association between (i.e. unify) the reduced # and expanded terms ASAP, in order to produce finite # expanded graphs first and yield results. # # In other words, there's no fixed-point to produce in this # situation. Instead, for example, we have to produce an infinite # stream of terms that have `out_term` as a fixed point. # g = conde([single_res_g, single_apply_g], # [another_apply_g, single_apply_g]) g = lall(conde([single_res_g], [another_apply_g]), single_apply_g) else: # Run the recursion step first, so that we get the fixed-point as # the first result g = lall(single_apply_g, conde([another_apply_g], [single_res_g])) g = goaleval(g) yield from g(s)
def binopo_goal(S: ConstrainedState): nonlocal x, y, v, op uuid = str(uuid4())[:4] t = var("type_" + uuid) x_rf, y_rf, v_rf, op_rf = reify((x, y, v, op), S) isground_all = [ isground(reified_value, S) for reified_value in [x_rf, y_rf, v_rf, op_rf] ] if not all(isground_all): # We can only fix one LV at a time if len([uv for uv in isground_all if uv is False]) == 1: # TODO: Revop the other vars if not isground(v_rf, S): try: g = lall( isinstanceo(x_rf, t), isinstanceo(y_rf, t), isinstanceo(v_rf, t), eq(op_rf(x_rf, y_rf), v_rf), ) except Exception as e: logger.debug( "Got exception during binop with args {}: {}". format([x_rf, y_rf, v_rf, op_rf], e)) return yield from g(S) else: # TODO: Why isnt this covered by the `typeo` goal? try: g = lall( isinstanceo(x_rf, t), isinstanceo(y_rf, t), isinstanceo(v_rf, t), eq(op_rf(x_rf, y_rf), v_rf), ) except Exception as e: logger.debug( "Got exception during ungrounded binop with args {}: {}". format([x_rf, y_rf, v_rf, op_rf], e)) return yield from g(S)
def mul_trans(in_expr, out_expr): """Equate `2 * x` with `5 * x` in a Theano `scan`. I.e. from left-to-right, replace `2 * x[t-1]` with `5 * x[t-1]`. """ arg_lv = var() inputs_lv, info_lv = var(), var() in_scan_lv = mt.Scan(inputs_lv, [mt.mul(2, arg_lv)], info_lv) out_scan_lv = mt.Scan(inputs_lv, [mt.mul(5, arg_lv)], info_lv) return lall(eq(in_expr, in_scan_lv), eq(out_expr, out_scan_lv))
def distributes(in_lv, out_lv): return lall( # lhs == A * (x + b) eq(etuple(mt.dot, var("A"), etuple(mt.add, var("x"), var("b"))), etuplize(in_lv)), # rhs == A * x + A * b eq( etuple(mt.add, etuple(mt.dot, var("A"), var("x")), etuple(mt.dot, var("A"), var("b"))), out_lv, ), )
def distributes(in_lv, out_lv): A_lv, x_lv, y_lv, z_lv = vars(4) return lall( # lhs == A * (x + y + z) eq_assoccomm( etuple(_dot, A_lv, etuple(at.add, x_lv, etuple(at.add, y_lv, z_lv))), in_lv, ), # This relation does nothing but provide us with a means of # generating associative-commutative matches in the `kanren` # output. eq((A_lv, x_lv, y_lv, z_lv), out_lv), )
def normal_normal_regression(Y, X, beta, Y_args_tail=None, beta_args=None): """Create a goal for a normal-normal regression of the form `Y ~ N(X * beta, sd**2)`.""" Y_args_tail = Y_args_tail or var() beta_args = beta_args or var() Y_args, Y_mean_lv = var(), var() res = lall( # `Y` is a `NormalRV` applyo(mt.NormalRV, Y_args, Y), # `beta` is also a `NormalRV` applyo(mt.NormalRV, beta_args, beta), # Obtain its mean parameter and remaining args applyo(Y_mean_lv, Y_args_tail, Y_args), # Relate it to a dot product of `X` and `beta` applyo(mt.dot, etuple(X, beta), Y_mean_lv), ) return res
def test_ifa(): x, y = var(), var() assert run(0, (x, y), ifa(lall(eq(x, True), eq(y, 1)), eq(y, 2))) == ((True, 1), ) assert run(0, y, eq(x, False), ifa(lall(eq(x, True), eq(y, 1)), lall(eq(y, 2)))) == (2, ) assert (run( 0, y, eq(x, False), ifa(lall(eq(x, True), eq(y, 1)), lall(eq(x, True), eq(y, 2))), ) == ()) assert run( 0, y, eq(x, True), ifa(lall(eq(x, True), eq(y, 1)), lall(eq(x, True), eq(y, 2))), ) == (1, )
accounts = (Account('Adam', 'Smith', 1, 20), Account('Carl', 'Marx', 2, 3), Account('John', 'Rockefeller', 3, 1000)) # optional name strings are helpful for debugging first = var('first') last = var('last') ident = var('ident') balance = var('balance') newbalance = var('newbalance') # Describe a couple of transformations on accounts source = Account(first, last, ident, balance) target = Account(first, last, ident, newbalance) theorists = ('Adam', 'Carl') # Give $10 to theorists theorist_bonus = lall((membero, source, accounts), (membero, first, theorists), (add, 10, balance, newbalance)) # Take $10 from anyone with more than $100 tax_the_rich = lall((membero, source, accounts), (gt, balance, 100), (sub, balance, 10, newbalance)) print("Take $10 from anyone with more than $100") print((run(0, target, tax_the_rich))) print("Give $10 to theorists") print((run(0, target, theorist_bonus)))
houses = var() rules_zebraproblem = lall( (eq, (var(), var(), var(), var(), var()), houses), (membero, ('Englishman', var(), var(), var(), 'red'), houses), (membero, ('Swede', var(), var(), 'dog', var()), houses), (membero, ('Dane', var(), 'tea', var(), var()), houses), (left, (var(), var(), var(), var(), 'green'), (var(), var(), var(), var(), 'white'), houses), (membero, (var(), var(), 'coffee', var(), 'green'), houses), (membero, (var(), 'Pall Mall', var(), 'birds', var()), houses), (membero, (var(), 'Dunhill', var(), var(), 'yellow'), houses), (eq, (var(), var(), (var(), var(), 'milk', var(), var()), var(), var()), houses), (eq, (('Norwegian', var(), var(), var(), var()), var(), var(), var(), var()), houses), (next, (var(), 'Blend', var(), var(), var()), (var(), var(), var(), 'cats', var()), houses), (next, (var(), 'Dunhill', var(), var(), var()), (var(), var(), var(), 'horse', var()), houses), (membero, (var(), 'Blue Master', 'beer', var(), var()), houses), (membero, ('German', 'Prince', var(), var(), var()), houses), (next, ('Norwegian', var(), var(), var(), var()), (var(), var(), var(), var(), 'blue'), houses), (next, (var(), 'Blend', var(), var(), var()), (var(), var(), 'water', var(), var()), houses), (membero, (var(), var(), var(), 'zebra', var()), houses)) #run solver with preceeding constraints solutions = run(0, houses, rules_zebraproblem)
) # optional name strings are helpful for debugging first = var("first") last = var("last") ident = var("ident") balance = var("balance") newbalance = var("newbalance") # Describe a couple of transformations on accounts source = Account(first, last, ident, balance) target = Account(first, last, ident, newbalance) theorists = ("Adam", "Carl") # Give $10 to theorists theorist_bonus = lall( membero(source, accounts), membero(first, theorists), add(10, balance, newbalance), ) # Take $10 from anyone with more than $100 tax_the_rich = lall(membero(source, accounts), gt(balance, 100), sub(balance, 10, newbalance)) print("Take $10 from anyone with more than $100") print(run(0, target, tax_the_rich)) print("Give $10 to theorists") print(run(0, target, theorist_bonus))
# optional name strings are helpful for debugging first = var(prefix="first") last = var(prefix="last") ident = var(prefix="ident") balance = var(prefix="balance") newbalance = var(prefix="newbalance") # Describe a couple of transformations on accounts source = Account(first, last, ident, balance) target = Account(first, last, ident, newbalance) theorists = ("Adam", "Carl") # Give $10 to theorists theorist_bonus = lall( membero(source, accounts), membero(first, theorists), applyo(add, (10, balance), newbalance), ) # Take $10 from anyone with more than $100 a = var(prefix="a") tax_the_rich = lall( membero(source, accounts), applyo(gt, (balance, 100), a), eq(a, True), applyo(sub, (balance, 10), newbalance), ) print("Take $10 from anyone with more than $100") print(run(0, target, tax_the_rich))
# Define the rules rules = lall( # There are 4 people (eq, (var(), var(), var(), var()), people), # Steve's car is blue (membero, ('Steve', var(), 'blue', var()), people), # Person who owns the cat lives in Canada (membero, (var(), 'cat', var(), 'Canada'), people), # Matthew lives in USA (membero, ('Matthew', var(), var(), 'USA'), people), # The person who has a black car lives in Australia (membero, (var(), var(), 'black', 'Australia'), people), # Jack has a cat (membero, ('Jack', 'cat', var(), var()), people), # Alfred lives in Australia (membero, ('Alfred', var(), var(), 'Australia'), people), # Person who owns the dog lives in France (membero, (var(), 'dog', var(), 'France'), people), # Who is the owner of the rabbit? (membero, (var(), 'rabbit', var(), var()), people) ) # Run the solver
clues = lall( # Solution with 4 fields: Title, LastName, Product and Priority (eq, (var(), var(), var(), var()), customer), # All Values (membero, ('Baroness', var(), var(), var()), customer), (membero, ('Sergeant', var(), var(), var()), customer), (membero, ('Reverend', var(), var(), var()), customer), (membero, ('Doctor', var(), var(), var()), customer), (membero, (var(), 'Bevens', var(), var()), customer), (membero, (var(), 'Shadwell', var(), var()), customer), (membero, (var(), 'Dimmadome', var(), var()), customer), (membero, (var(), 'Rotzenheimer', var(), var()), customer), (membero, (var(), var(), 'Rose Bar', var()), customer), (membero, (var(), var(), 'Lemon Gel', var()), customer), (membero, (var(), var(), 'Chamomile Bar', var()), customer), (membero, (var(), var(), 'Hibiscus Soap-on-a-Rope', var()), customer), (membero, (var(), var(), var(), '1'), customer), (membero, (var(), var(), var(), '2'), customer), (membero, (var(), var(), var(), '3'), customer), (membero, (var(), var(), var(), '4'), customer), # Priority 1 > 2 > 3 > 4 (higher, (var(), var(), var(), '1'), (var(), var(), var(), '2'), customer), (higher, (var(), var(), var(), '2'), (var(), var(), var(), '3'), customer), (higher, (var(), var(), var(), '3'), (var(), var(), var(), '4'), customer), # Clue1a customer with the highest priority has a title and last name that begin with the same letter (any4, ('Baroness', 'Bevens', var(), '1'), ('Reverend', 'Rotzenheimer', var(), '1'), ('Sergeant', 'Shadwell', var(), '1'), ('Doctor', 'Dimmadome', var(), '1'), customer), # Clue2a Bevens' priority is directly after Dimmadome (higher, (var(), 'Dimmadome', var(), var()), (var(), 'Bevens', var(), var()), customer), # Clue6 The priority of the person who ordered the Rose Bar is directly after the person who ordered the Lemon Gel. (higher, (var(), var(), 'Lemon Gel', var()), (var(), var(), 'Rose Bar', var()), customer), # Clue2b Neither of these people ordered the Chamomile Bar or the Hibiscus Soap-on-a-Rope. (any2, (var(), 'Dimmadome', 'Rose Bar', var()), (var(), 'Dimmadome', 'Lemon Gel', var()), customer), (any2, (var(), 'Bevens', 'Rose Bar', var()), (var(), 'Bevens', 'Lemon Gel', var()), customer), # Clue3 The Sergeant and the person who ordered Lemon Gel are either 1st priority or 3rd priority (any2, ('Sergeant', var(), var(), '1'), ('Sergeant', var(), var(), '3'), customer), (any2, (var(), var(), 'Lemon Gel', '1'), (var(), var(), 'Lemon Gel', '3'), customer), # Clue4a The Reverend didn't order the Rose Bar. (any3, ('Reverend', var(), 'Lemon Gel', var()), ('Reverend', var(), 'Chamomile Bar', var()), ('Reverend', var(), 'Hibiscus Soap-on-a-Rope', var()), customer), # Clue4b And the Reverend isn't 2nd priority. (any3, ('Reverend', var(), var(), '1'), ('Reverend', var(), var(), '3'), ('Reverend', var(), var(), '4'), customer), # Clue5 The Sergeant either ordered Hibiscus Soap-on-a-Rope or is 4th priority. (any2, ('Sergeant', var(), 'Hibiscus Soap-on-a-Rope', var()), ('Sergeant', var(), var(), '4'), customer), # Clue7a Dimmadome is not a Doctor (any3, ('Baroness', 'Dimmadome', var(), var()), ('Sergeant', 'Dimmadome', var(), var()), ('Reverend', 'Dimmadome', var(), var()), customer), # Clue7b the Baroness didn't order the Hibiscus Soap-on-a-Rope. (any3, ('Baroness', var(), 'Rose Bar', var()), ('Baroness', var(), 'Lemon Gel', var()), ('Baroness', var(), 'Chamomile Bar', var()), customer))
def solve(n, expr, env, value, maxdepth=3): goals = eval_expro(expr, env, value, depth=0, maxdepth=maxdepth) results = map(partial(reify, expr), goaleval(lall(goals))({})) return take(n, unique(results, key=multihash))
def normal_qr_transform(in_expr, out_expr): """Produce a relation for normal-normal regression and its QR-reduced form. TODO XXX: This isn't entirely correct (e.g. it needs to also transform the variance terms), but it demonstrates all the requisite functionality for this kind of model reformulation. """ y_lv, Y_lv, X_lv, beta_lv = var(), var(), var(), var() Y_args_lv, beta_args_lv = var(), var() QR_lv, Q_lv, R_lv = var(), var(), var() beta_til_lv, beta_new_lv = var(), var() beta_mean_lv, beta_sd_lv = var(), var() beta_size_lv, beta_rng_lv = var(), var() Y_new_lv = var() X_op_lv = var() in_expr = etuplize(in_expr) res = lall( # Only applies to regression models on observed RVs eq(in_expr, etuple(mt.observed, y_lv, Y_lv)), # Relate the model components normal_normal_regression(Y_lv, X_lv, beta_lv, Y_args_lv, beta_args_lv), # Let's not do all this to an already QR-reduce graph; # otherwise, we'll loop forever! applyo(X_op_lv, var(), X_lv), # XXX: This type of dis-equality goal isn't the best, # but it will definitely work for now. neq(mt.nlinalg.qr_full, X_op_lv), # Relate terms for the QR decomposition eq(QR_lv, etuple(mt.nlinalg.qr_full, X_lv)), eq(Q_lv, etuple(itemgetter(0), QR_lv)), eq(R_lv, etuple(itemgetter(1), QR_lv)), # The new `beta_tilde` eq(beta_args_lv, (beta_mean_lv, beta_sd_lv, beta_size_lv, beta_rng_lv)), eq( beta_til_lv, etuple( mt.NormalRV, # Use these `tt.[ones|zeros]_like` functions to preserve the # correct shape (and a valid `tt.dot`). etuple(mt.zeros_like, beta_mean_lv), etuple(mt.ones_like, beta_sd_lv), beta_size_lv, beta_rng_lv, ), ), # Relate the new and old coeffs eq(beta_new_lv, etuple(mt.dot, etuple(mt.nlinalg.matrix_inverse, R_lv), beta_til_lv)), # Use the relation the other way to produce the new/transformed # observation distribution normal_normal_regression(Y_new_lv, Q_lv, beta_til_lv, Y_args_lv), eq( out_expr, [ ( in_expr, etuple(mt.observed, y_lv, etuple(update_name_suffix, Y_new_lv, Y_lv, "")), ), (beta_lv, beta_new_lv), ], ), ) return res
def test_lall(): x = var() assert results(lall(eq(x, 2))) == ({x: 2}, ) assert results(lall(eq(x, 2), eq(x, 3))) == () assert results(lall()) == ({}, ) assert run(0, x, lall()) == (x, )
3), Account('John', 'Rockefeller', 3, 1000)) # optional name strings are helpful for debugging first = var('first') last = var('last') ident = var('ident') balance = var('balance') newbalance = var('newbalance') # Describe a couple of transformations on accounts source = Account(first, last, ident, balance) target = Account(first, last, ident, newbalance) theorists = ('Adam', 'Carl') # Give $10 to theorists theorist_bonus = lall((membero, source, accounts), (membero, first, theorists), (add, 10, balance, newbalance)) # Take $10 from anyone with more than $100 tax_the_rich = lall((membero, source, accounts), (gt, balance, 100), (sub, balance, 10, newbalance)) print("Take $10 from anyone with more than $100") print((run(0, target, tax_the_rich))) print("Give $10 to theorists") print((run(0, target, theorist_bonus))) stimulus = (Account('Donald', 'Trump', 4, 362000000), Account('Jon', 'Snow', 5, 65000), Account('Lebron James', 'Smith', 6, 37000000), Account('Naruto', 'Uzumaki', 7, 43000))
Account('Carl', 'Marx', 2, 3), Account('John', 'Rockefeller', 3, 1000)) # optional name strings are helpful for debugging first = var('first') last = var('last') ident = var('ident') balance = var('balance') newbalance = var('newbalance') # Describe a couple of transformations on accounts source = Account(first, last, ident, balance) target = Account(first, last, ident, newbalance) theorists = ('Adam', 'Carl') # Give $10 to theorists theorist_bonus = lall((membero, source, accounts), (membero, first, theorists), (add, 10, balance, newbalance)) # Take $10 from anyone with more than $100 tax_the_rich = lall((membero, source, accounts), (gt, balance, 100), (sub, balance, 10, newbalance)) print("Take $10 from anyone with more than $100") print((run(0, target, tax_the_rich))) print("Give $10 to theorists") print((run(0, target, theorist_bonus)))
def bad_relation(): def _bad_relation(s): raise SomeException("some exception") return lall(_bad_relation)