Download CORE_0_4.patch from vynly/CAEL-0.4.0: direct link, hf CLI and curl.
- Browser
- Download file 41.6 kB
-
https://huggingface.co/vynly/CAEL-0.4.0/resolve/main/CORE_0_4.patch
- Command line
-
hf download hf://vynly/CAEL-0.4.0/CORE_0_4.patch
-
curl -L -o CORE_0_4.patch https://huggingface.co/vynly/CAEL-0.4.0/resolve/main/CORE_0_4.patch
41.6 kB
| --- a/cael_unified/__init__.py | |
| +++ b/cael_unified/__init__.py | |
| -"""CAEL 0.3.9.1: non-neural conversation runtime with the unchanged 0.3.7 resource bundle.""" | |
| -__version__ = '0.3.9.1' | |
| +"""CAEL 0.4.0 core-engine candidate; not a published or deployed release.""" | |
| +__version__ = '0.4.0' | |
| def __getattr__(name): | |
| --- a/cael_unified/_bounded_python_help.py | |
| +++ b/cael_unified/_bounded_python_help.py | |
| verify.sandbox_function = denied | |
| apply = verify.apply | |
| - verify.apply = lambda text, answer: apply(text, answer, record=False) | |
| + def unrecorded_apply(text, answer, record=False): | |
| + # Preserve the checker's call contract for orchestrators that explicitly | |
| + # request no recording. Keep the bounded policy's no-record behaviour. | |
| + return apply(text, answer, record=False) | |
| + verify.apply = unrecorded_apply | |
| --- a/cael_unified/assistant.py | |
| +++ b/cael_unified/assistant.py | |
| # spelling repair, fact route or web lookup can read "CAEL" as someone else (self_identity.py, 0.3.8 fix). | |
| from . import self_identity | |
| answer = self_identity.reply(messages) if mode in ('auto', 'chat') else None | |
| + if (answer is None and mode in ('auto', 'chat') and isinstance(messages, list) | |
| + and messages and len(messages)<=MAX_MESSAGES | |
| + and all(isinstance(m,dict) and m.get('role') in ('user','assistant','system') | |
| + and isinstance(m.get('content'),str) for m in messages) | |
| + and sum(len(m['content']) for m in messages)<=MAX_CHARACTERS | |
| + and messages[-1].get('role') == 'user' and type(n) is int and 1<=n<=8 | |
| + and (seed is None or type(seed) is int and 0<=seed<2**31)): | |
| + from .local_automata import reply as automaton_reply | |
| + answer = automaton_reply(messages[-1].get('content')) | |
| # A complete, single-request symbolic document owns its literal | |
| # symbols before dialogue acts, spelling and memory heuristics. | |
| if (answer is None and mode in ('auto','chat') and isinstance(messages,list) | |
| --- a/cael_unified/bundle.py | |
| +++ b/cael_unified/bundle.py | |
| import zipfile | |
| ROOT = Path(__file__).resolve().parents[1] | |
| -VERSION = '0.3.9.1' | |
| -# 0.3.9.1 adds native interpretation; the public resource bundle remains byte-identical 0.3.7. | |
| +VERSION = '0.4.0' | |
| +# Core algorithms evolve in source; inherited public assets remain immutable. | |
| BUNDLE_VERSION = '0.3.7' | |
| SUPPORTED = {'0.2.1', '0.2.2', '0.2.3', '0.2.4', '0.2.5', '0.2.6', '0.2.7', '0.2.8', '0.2.9', '0.3', '0.3.1', '0.3.2', '0.3.3', '0.3.4', '0.3.5', '0.3.6', '0.3.7', '0.3.8'} | |
| DEFAULT_BUNDLE = ROOT / 'models' / f'cael-{BUNDLE_VERSION}.cael' | |
| --- a/cael_unified/checked_tasks.py | |
| +++ b/cael_unified/checked_tasks.py | |
| m=re.fullmatch(r'(?is)([^:.\n]{1,140}):\s*([^:.\n]+)\.\s*(.+)',text) | |
| if not m or not re.match(r'(?i)^(?:items|objects|participants|on |in |at |there (?:are|stand) )',m[1]):return None | |
| names=[clean(p.strip()) for p in re.split(r',\s*(?:and\s+)?|\s+and\s+',m[2])] | |
| - if not 2<=len(names)<=8 or len(set(names))!=len(names):raise Unclear('Use two to eight distinct explicitly listed objects.') | |
| + if not 2<=len(names)<=12 or len(set(names))!=len(names):raise Unclear('Use two to twelve distinct explicitly listed objects.') | |
| declared=re.search(r'(?i)\b('+ '|'.join(NUMBERS)+r'|\d+)\b',m[1]) | |
| if declared and _number(declared[1])!=len(names):raise Unclear('The stated object count and complete item list disagree.') | |
| parts=[p.strip() for p in re.split(r'[.!?]\s*|\n+',m[3]) if p.strip()] | |
| --- a/cael_unified/constraint_automaton.py | |
| +++ b/cael_unified/constraint_automaton.py | |
| +"""Finite-domain constraint propagation with exhaustive bounded witnesses. | |
| + | |
| +Only explicit trusted predicates are called. No generated code, neural model, | |
| +training answers or benchmark-dependent rule selection. | |
| +""" | |
| +from itertools import product | |
| + | |
| + | |
| +class SearchLimit(ValueError): | |
| + pass | |
| + | |
| + | |
| +def enumerate_orders(names, constraints, holds, max_nodes=250000, max_checks=3000000, | |
| + max_solutions=50000): | |
| + names = tuple(names) | |
| + if not 2 <= len(names) <= 12 or len(set(names)) != len(names): | |
| + raise ValueError('Use 2 to 12 distinct items') | |
| + scopes = [tuple(dict.fromkeys(c[1:2] + (() if c[0].removeprefix('not_') == 'at' else c[2:]))) | |
| + for c in constraints] | |
| + if any(any(n not in names for n in scope) for scope in scopes): | |
| + raise ValueError('A constraint references an unlisted item') | |
| + stats = dict(nodes=0, predicate_checks=0, propagations=0, complete=False) | |
| + solutions = [] | |
| + | |
| + def propagate(domains): | |
| + while True: | |
| + before = tuple(tuple(sorted(domains[n])) for n in names) | |
| + singles = [next(iter(v)) for v in domains.values() if len(v) == 1] | |
| + if len(singles) != len(set(singles)): | |
| + return False | |
| + used = set(singles) | |
| + for name, values in domains.items(): | |
| + if len(values) > 1: values.difference_update(used) | |
| + if not values: return False | |
| + # Hall's union necessary condition, alongside singleton all-different. | |
| + if len(set().union(*domains.values())) < len(names): return False | |
| + for constraint, scope in zip(constraints, scopes): | |
| + supported = {name: set() for name in scope} | |
| + for values in product(*(sorted(domains[n]) for n in scope)): | |
| + stats['predicate_checks'] += 1 | |
| + if stats['predicate_checks'] > max_checks: | |
| + raise SearchLimit('Constraint propagation work budget reached') | |
| + if len(values) != len(set(values)): continue | |
| + if holds(constraint, dict(zip(scope, values))): | |
| + for name, value in zip(scope, values): supported[name].add(value) | |
| + for name in scope: | |
| + domains[name].intersection_update(supported[name]) | |
| + if not domains[name]: return False | |
| + stats['propagations'] += 1 | |
| + if before == tuple(tuple(sorted(domains[n])) for n in names): return True | |
| + | |
| + def visit(domains): | |
| + stats['nodes'] += 1 | |
| + if stats['nodes'] > max_nodes: raise SearchLimit('Ordering search node budget reached') | |
| + if not propagate(domains): return | |
| + pending = [n for n in names if len(domains[n]) > 1] | |
| + if not pending: | |
| + positions = {n: next(iter(domains[n])) for n in names} | |
| + order = tuple(sorted(names, key=positions.get)) | |
| + solutions.append(order) | |
| + if len(solutions) > max_solutions: | |
| + raise SearchLimit('Too many valid orders for complete enumeration') | |
| + return | |
| + name = min(pending, key=lambda n: (len(domains[n]), names.index(n))) | |
| + for value in sorted(domains[name]): | |
| + child = {n: set(v) for n, v in domains.items()} | |
| + child[name] = {value} | |
| + visit(child) | |
| + | |
| + visit({name: set(range(len(names))) for name in names}) | |
| + # Preserve the old public ordering of witnesses, independent of MRV order. | |
| + rank = {name: i for i, name in enumerate(names)} | |
| + solutions.sort(key=lambda row: tuple(rank[n] for n in row)) | |
| + stats['complete'] = True | |
| + stats['solutions'] = len(solutions) | |
| + return tuple(solutions), stats | |
| --- a/cael_unified/count_language.py | |
| +++ b/cael_unified/count_language.py | |
| def neighbours(self,context,limit=128): | |
| np=self.np | |
| - if context in self.cache: | |
| - self.cache.move_to_end(context) | |
| - return self.cache[context] | |
| + if type(limit) is not int or not 1<=limit<=2048: | |
| + raise ValueError('Neighbour limit must be an integer from 1 to 2048') | |
| + cache_key=(context,limit) | |
| + if cache_key in self.cache: | |
| + self.cache.move_to_end(cache_key) | |
| + return self.cache[cache_key] | |
| query=self.query_features(context) | |
| # Both stored/query rows are L2 normalized; this is sparse cosine recall. | |
| from sklearn.preprocessing import normalize | |
| hits=(query@self.contexts.T).tocsr() | |
| if hits.nnz: | |
| take=min(limit,hits.nnz) | |
| - selection=np.argpartition(hits.data,-take)[-take:] | |
| + threshold=np.partition(hits.data,-take)[-take] | |
| + greater=np.flatnonzero(hits.data>threshold) | |
| + equal=np.flatnonzero(hits.data==threshold) | |
| + equal=equal[np.argsort(hits.indices[equal],kind='stable')] | |
| + selection=np.concatenate((greater,equal[:take-len(greater)])) | |
| order=np.lexsort((hits.indices[selection],-hits.data[selection])) | |
| selection=selection[order] | |
| result=(hits.indices[selection],hits.data[selection]) | |
| else: | |
| result=(np.array([],dtype=np.int64),np.array([],dtype=np.float64)) | |
| - self.cache[context]=result | |
| + self.cache[cache_key]=result | |
| if len(self.cache)>32: | |
| self.cache.popitem(last=False) | |
| return result | |
| def conditional(self,context,settings=None): | |
| np=self.np | |
| settings=settings or self.settings | |
| - indices,similarities=self.neighbours(context) | |
| + indices,similarities=self.neighbours(context,limit=max(128,settings['neighbours'])) | |
| count=min(len(indices),settings['neighbours']) | |
| if count: | |
| weights=similarities[:count].astype(np.float64)**settings['recall_power'] | |
| --- a/cael_unified/event_memory.py | |
| +++ b/cael_unified/event_memory.py | |
| def key(entity,attribute):return clean(entity).casefold()+'\x1f'+clean(attribute).casefold() | |
| def resolve(state,cell,seen=None): | |
| - seen=set() if seen is None else seen | |
| - if cell in seen or len(seen)>128:raise ValueError('Circular or too-deep reference') | |
| - seen.add(cell) | |
| - value=state.get(cell,UNKNOWN) | |
| - if isinstance(value,dict): | |
| - if 'ref' in value:return resolve(state,value['ref'],seen) | |
| - raise ValueError('Unknown or conflicting value') | |
| - return value | |
| + # Iterative traversal supports the stated cell budget, instead of an | |
| + # unrelated 128-link or Python recursion ceiling. References remain lazy: | |
| + # changing a source affects live links but never earlier copied values. | |
| + seen=set() if seen is None else set(seen) | |
| + while True: | |
| + if cell in seen:raise ValueError('Circular live dependency') | |
| + if len(seen)>=MAX_CELLS:raise ValueError('Reference traversal exceeds the state-cell budget') | |
| + seen.add(cell) | |
| + value=state.get(cell,UNKNOWN) | |
| + if not isinstance(value,dict):return value | |
| + if 'ref' not in value:raise ValueError('Unknown or conflicting value') | |
| + cell=value['ref'] | |
| def transition(state,slots,operation): | |
| """The candidate algebra used by both the trainer and runtime. | |
| boundary=versions[0][0] if past=='initial' else versions[-1][0]-1 | |
| state={} | |
| for k,history in self.history.items(): | |
| - prior=[v for i,v in history if i<=boundary] | |
| - if prior:state[k]=prior[-1] | |
| + offset=bisect_right(history,boundary,key=lambda entry:entry[0]) | |
| + if offset:state[k]=history[offset-1][1] | |
| return resolve(state,cell) | |
| def reply(messages): | |
| --- a/cael_unified/interpretation_relations.py | |
| +++ b/cael_unified/interpretation_relations.py | |
| if not any(re.search(r'(?i)\b(?:before|after|ahead of|behind|precedes|follows)\b',p) for p in parts[:-1]):return None | |
| boundary(parts[-1]) | |
| if query[0]=='unsupported':raise TaskError('The complete ordering question contains an unsupported qualifier.') | |
| + # Richer explicit constraints belong to the same finite-domain engine used | |
| + # by ordinary reasoning. Do not reject them in the precedence-only reader. | |
| + if re.match(r'(?i)^(?:items|people|objects|participants|runners|books|cars|order)\s*:', parts[0]) and any( | |
| + re.search(r'(?i)\b(?:immediately|directly|adjacent|next to|between|not|first|last|second|third|fourth|fifth)\b', p) | |
| + for p in parts[1:-1]): | |
| + from .reasoning_order import ordering | |
| + try: result = ordering('. '.join(parts[:-1]), parts[-1]) | |
| + except ValueError as exc: raise TaskError(str(exc)) from exc | |
| + return Result(result['answer'], 'ordering', {'coverage': spans(text, parts), | |
| + 'engine': 'finite-domain-constraint-automaton', 'proof': result}) | |
| names=[];edges=[] | |
| for raw in parts[:-1]: | |
| p=raw.strip() | |
| words=set(re.findall(r'[a-z]+',p.casefold())) | |
| if words and words<=header_words and words&{'log','events','entries','chronological','updates'}:continue | |
| boundary(p) | |
| + m=re.fullmatch(rf'(?i)({ATOM}?) is either in (?:the )?({ATOM}?) or (?:the )?({ATOM})',p) | |
| + if m:events.append(('uncertain',symbol(m[1]),symbol(m[2]),symbol(m[3])));continue | |
| + m=re.fullmatch(rf'(?i)({ATOM}?) is (?:not|no longer) in (?:the )?({ATOM})',p) | |
| + if m:events.append(('exclude',symbol(m[1]),symbol(m[2])));continue | |
| m=re.fullmatch(rf'(?i)({ATOM}?) (?:went|moved|relocated|walked|travelled|traveled|journeyed|returned)(?: back)? (?:to|into) (?:the )?({ATOM})',p) | |
| if m:events.append(('move',symbol(m[1]),symbol(m[2])));continue | |
| m=re.fullmatch(rf'(?i)({ATOM}?) (?:entered|visited) (?:the )?({ATOM})',p) | |
| if cell and cell.locations and cell.locations!={name(value)}:raise TaskError('A departure location conflicts with the preceding event state.') | |
| event_text=f'{name(actor)} went to {name(extra[0])}' | |
| elif op=='move':event_text=f'{name(actor)} went to {name(value)}' | |
| + elif op=='uncertain':event_text=f'{name(actor)} is either in {name(value)} or {name(extra[0])}' | |
| + elif op=='exclude':event_text=f'{name(actor)} is not in {name(value)}' | |
| elif op=='take':event_text=f'{name(actor)} got {name(value)}' | |
| elif op=='drop':event_text=f'{name(actor)} dropped {name(value)}' | |
| else: | |
| old=world.cells.get(name(value)) | |
| if old and old.owner not in (None,name(actor)):raise TaskError('The giver is not the established holder of the object.') | |
| event_text=f'{name(actor)} gave {name(value)} to {name(extra[0])}' | |
| - world.apply(world.event(event_text),index);canonical.append(event_text+'.') | |
| + try: world.apply(world.event(event_text),index) | |
| + except ValueError as exc: raise TaskError(str(exc)) from exc | |
| + canonical.append(event_text+'.') | |
| target_value=query[1] | |
| if target_value in ('he','she','they','it'): | |
| referents=known_objects if query[0]=='owner' else known_actors | |
| --- a/cael_unified/local_automata.py | |
| +++ b/cael_unified/local_automata.py | |
| +"""Example-induced finite local transition models, without neural inference. | |
| + | |
| +The inductive bias is explicit: stationary 1-D discrete updates, a bounded | |
| +neighbourhood, exact lookup consistency and aggregate/symmetry hypotheses. | |
| +Example-pair cross-validation ranks hypotheses. Query answers never enter fit. | |
| +Unseen neighbourhoods use labelled empirical guesses, never claimed proofs. | |
| +""" | |
| +from collections import Counter, defaultdict | |
| +from dataclasses import dataclass | |
| +import json | |
| +import math | |
| + | |
| +MAX_RADIUS = 3 | |
| +MAX_EXAMPLES = 32 | |
| +MAX_WIDTH = 512 | |
| +MAX_SYMBOL = 31 | |
| + | |
| + | |
| +class OutsideDomain(ValueError): | |
| + pass | |
| + | |
| + | |
| +def row(value): | |
| + if type(value) is not list or not 1 <= len(value) <= MAX_WIDTH: | |
| + raise OutsideDomain('Expected a nonempty discrete row of at most 512 cells') | |
| + if any(type(v) is not int or not 0 <= v <= MAX_SYMBOL for v in value): | |
| + raise OutsideDomain('Cells must be integer symbols from 0 through 31') | |
| + return tuple(value) | |
| + | |
| + | |
| +def neighbourhood(source, position, radius, boundary): | |
| + if boundary == 'wrap': | |
| + return tuple(source[(position+d) % len(source)] for d in range(-radius, radius+1)) | |
| + if boundary == 'zero': | |
| + return tuple(source[position+d] if 0 <= position+d < len(source) else 0 | |
| + for d in range(-radius, radius+1)) | |
| + raise OutsideDomain('Boundary must be wrap or zero') | |
| + | |
| + | |
| +def signature(window, radius, kind): | |
| + if kind == 'left': return (window[0],) | |
| + if kind == 'right': return (window[-1],) | |
| + if kind == 'ordered': return window | |
| + if kind == 'multiset': return tuple(sorted(window)) | |
| + if kind == 'sum': return (sum(window),) | |
| + outside = window[:radius] + window[radius+1:] | |
| + if kind == 'center_sum': return window[radius], sum(outside) | |
| + if kind == 'center_multiset': return window[radius], tuple(sorted(outside)) | |
| + if kind == 'outer_sum': return (sum(outside),) | |
| + raise OutsideDomain('Unknown feature family') | |
| + | |
| + | |
| +@dataclass | |
| +class Hypothesis: | |
| + radius: int | |
| + kind: str | |
| + table: dict | |
| + counts: dict | |
| + validation_lnp: float | |
| + validation_seen: int | |
| + validation_cells: int | |
| + cost: float | |
| + | |
| + | |
| +def fit_map(pairs, radius, kind, boundary): | |
| + table = {}; counts = Counter() | |
| + for source, target in pairs: | |
| + for position, value in enumerate(target): | |
| + key = signature(neighbourhood(source, position, radius, boundary), radius, kind) | |
| + if key in table and table[key] != value: return None | |
| + table[key] = value; counts[key] += 1 | |
| + return table, counts | |
| + | |
| + | |
| +def learn_transition(examples, query, boundary='wrap'): | |
| + """Predict a new row from demonstration pairs, retaining coverage/ambiguity. | |
| + | |
| + All fitting is ephemeral and episode-local. This function accepts only | |
| + demonstrations and query, never a rule-table/family/label metadata object. | |
| + """ | |
| + if type(examples) is not list or not 2 <= len(examples) <= MAX_EXAMPLES: | |
| + raise OutsideDomain('Provide 2 to 32 independent example pairs') | |
| + pairs = [] | |
| + for example in examples: | |
| + if type(example) is not dict or set(example) != {'input', 'output'}: | |
| + raise OutsideDomain('Each example needs only input and output') | |
| + source, target = row(example['input']), row(example['output']) | |
| + if len(source) != len(target): raise OutsideDomain('Input/output widths must match') | |
| + pairs.append((source, target)) | |
| + query = row(query) | |
| + if sum(len(x) for x, _ in pairs) > 16384: | |
| + raise OutsideDomain('Demonstration cell budget reached') | |
| + if boundary not in ('wrap', 'zero'): raise OutsideDomain('Unknown boundary') | |
| + # Contradictory full-row evidence is invalid even if a larger neighbourhood | |
| + # could hide a collision. Retain ambiguity instead of fitting the last row. | |
| + seen_rows = {} | |
| + for source, target in pairs: | |
| + if source in seen_rows and seen_rows[source] != target: | |
| + raise OutsideDomain('The demonstrations give conflicting outputs for the same input') | |
| + seen_rows[source] = target | |
| + symbols = max([*query, *(v for x, y in pairs for v in (*x, *y))]) + 1 | |
| + symbols = max(2, symbols) | |
| + global_counts = Counter(v for _, target in pairs for v in target) | |
| + fallback = max(range(symbols), key=lambda v: (global_counts[v], -v)) | |
| + models = [] | |
| + families = [(0, 'ordered')] | |
| + families += [(r, kind) for r in range(1, MAX_RADIUS+1) | |
| + for kind in ('left', 'right', 'sum', 'outer_sum', 'center_sum', 'multiset', 'center_multiset', 'ordered')] | |
| + for radius, kind in families: | |
| + fitted = fit_map(pairs, radius, kind, boundary) | |
| + if fitted is None: continue | |
| + table, counts = fitted | |
| + lnp = 0.0; covered = 0; total = 0 | |
| + # Hold out entire pairs, not individual neighbouring cells, to avoid | |
| + # scoring an example against its own transitions. | |
| + for excluded, (source, target) in enumerate(pairs): | |
| + rest = pairs[:excluded] + pairs[excluded+1:] | |
| + partial, witnesses = fit_map(rest, radius, kind, boundary) | |
| + prior = Counter(v for _, output in rest for v in output) | |
| + prior_total = sum(prior.values()) | |
| + for position, value in enumerate(target): | |
| + key = signature(neighbourhood(source, position, radius, boundary), radius, kind) | |
| + if key in partial: | |
| + covered += 1 | |
| + probability = (witnesses[key] + 0.5) / (witnesses[key] + 0.5*symbols) | |
| + else: | |
| + probability = (prior[value]+0.5) / (prior_total+0.5*symbols) | |
| + lnp += math.log(probability); total += 1 | |
| + description = (2*radius+1 if kind == 'ordered' else radius+2 if 'multiset' in kind else 2) | |
| + cost = math.log2(2+len(table)) + 0.25*description | |
| + models.append(Hypothesis(radius, kind, table, counts, lnp, covered, total, cost)) | |
| + models.sort(key=lambda m: (-(m.validation_lnp-0.10*m.cost), m.cost, m.radius, m.kind)) | |
| + # No silent switch to an external model or query-label-derived rule. If | |
| + # local stationarity cannot fit, emit an explicit empirical-only result. | |
| + if not models: | |
| + return dict(prediction=[fallback]*len(query), supported=[False]*len(query), | |
| + alternatives=[list(range(symbols)) for _ in query], | |
| + status='empirical-only', fitted_hypotheses=0, selected=None, | |
| + coverage=0.0, neural=False, persistent_learning=False) | |
| + selected = models[0] | |
| + prediction = []; supported = []; alternatives = [] | |
| + for position in range(len(query)): | |
| + key = signature(neighbourhood(query, position, selected.radius, boundary), selected.radius, selected.kind) | |
| + known = key in selected.table | |
| + # Back off through independently consistent hypotheses when the chosen | |
| + # compressed representation lacks a query signature. | |
| + choices = [] | |
| + for model in models: | |
| + signature_key = signature(neighbourhood(query, position, model.radius, boundary), model.radius, model.kind) | |
| + if signature_key in model.table: choices.append(model.table[signature_key]) | |
| + value = selected.table[key] if known else choices[0] if choices else fallback | |
| + prediction.append(value); supported.append(known) | |
| + alternatives.append(sorted(set(choices)) if choices else list(range(symbols))) | |
| + return dict(prediction=prediction, supported=supported, alternatives=alternatives, | |
| + status='inferred' if all(supported) else 'partially-supported', | |
| + coverage=sum(supported)/len(query), fitted_hypotheses=len(models), | |
| + selected=dict(radius=selected.radius, features=selected.kind, | |
| + observed_signatures=len(selected.table), | |
| + held_pair_coverage=selected.validation_seen/max(1, selected.validation_cells), | |
| + held_pair_log_likelihood=selected.validation_lnp), | |
| + disagreeing_cells=sum(len(v)>1 for v in alternatives), | |
| + neural=False, persistent_learning=False, | |
| + guarantee='Prediction under an inferred finite local hypothesis, not a uniquely identified general rule') | |
| + | |
| + | |
| +def reply(text): | |
| + prefix = 'Infer discrete transition:' | |
| + if not isinstance(text, str) or not text.lstrip().startswith(prefix): return None | |
| + payload = text.lstrip()[len(prefix):].strip() | |
| + try: | |
| + if len(payload) > 200000: raise OutsideDomain('Structured task length limit') | |
| + task = json.loads(payload) | |
| + if type(task) is not dict or not {'examples', 'query'} <= set(task) or set(task)-{'examples','query','boundary'}: | |
| + raise OutsideDomain('Use examples, query, and optional boundary only') | |
| + result = learn_transition(task['examples'], task['query'], task.get('boundary', 'wrap')) | |
| + except (ValueError, TypeError, RecursionError) as exc: | |
| + return dict(route='chat/automaton-clarify', response='Please clarify the transition task: '+str(exc), | |
| + sources=[], automaton={'status':'invalid-or-unsupported', 'neural':False}) | |
| + return dict(route='chat/automaton-induction', response=json.dumps(result['prediction']), sources=[], | |
| + automaton=result, details='Example-induced discrete local transitions. '+result.get('guarantee', | |
| + 'Only an empirical output-frequency guess; no supported local rule was found.')) | |
| --- a/cael_unified/reasoning_logic.py | |
| +++ b/cael_unified/reasoning_logic.py | |
| """Bounded Horn-rule graph automaton with explicit proof support and open-world answers.""" | |
| from dataclasses import dataclass | |
| +from collections import defaultdict, deque | |
| import re | |
| def close(self, context): | |
| facts, rules = self.parse(context) | |
| entities = sorted({fact[0] for fact, _ in facts}) | |
| - if len(entities)*len(rules)>4096: | |
| + if len(entities)*len(rules)>32768: | |
| raise Unparsed('Too many grounded rule cells') | |
| - active = {fact for fact, _ in facts} | |
| - proof = {fact: {index} for fact, index in facts} | |
| - depths = {fact: 0 for fact, _ in facts} | |
| + active, proof, depths = set(), {}, {} | |
| + for fact, index in facts: | |
| + if fact not in active: | |
| + active.add(fact); proof[fact] = {index}; depths[fact] = 0 | |
| grounded = [(tuple((entity, p, neg) for p, neg in body), tuple((entity, p, neg) for p, neg in head), index) | |
| for entity in entities for body, head, index in rules] | |
| - # Bipartite graph CA: rule cells read only connected premise fact cells; | |
| - # conclusion fact cells read only connected rule cells. No neural weights. | |
| + # Sparse graph CA: changes wake only connected rule cells. Proof | |
| + # improvements propagate too; truth and evidence share the fixed point. | |
| + readers = defaultdict(list) | |
| + for rule_id, (body, head, index) in enumerate(grounded): | |
| + for atom in set(body): readers[atom].append(rule_id) | |
| + agenda = deque(sorted(active)); queued = set(active) | |
| table = self.model.get('and_table', {}) | |
| - for tick in range(24): | |
| - next_active = set(active) | |
| - next_proof, next_depth = dict(proof), dict(depths) | |
| - for body, head, index in grounded: | |
| + work = 0 | |
| + def rank(depth, support): | |
| + return depth, len(support), tuple(sorted(support)) | |
| + while agenda: | |
| + changed = agenda.popleft(); queued.remove(changed) | |
| + for rule_id in readers[changed]: | |
| + work += 1 | |
| + if work > 250000: | |
| + raise Unparsed('The sparse graph CA exceeded its rule-transition budget') | |
| + body, head, index = grounded[rule_id] | |
| mask = sum((1 << i) for i, atom in enumerate(body) if atom in active) | |
| if not table.get(f'{len(body)}:{mask}', 0): | |
| continue | |
| support = {index}.union(*(proof[atom] for atom in body)) | |
| depth = 1 + max(depths[atom] for atom in body) | |
| for atom in head: | |
| - next_active.add(atom) | |
| - if atom not in next_depth or depth < next_depth[atom]: | |
| - next_proof[atom], next_depth[atom] = support, depth | |
| - if next_active == active: | |
| - return active, proof, depths | |
| - active, proof, depths = next_active, next_proof, next_depth | |
| - raise Unparsed('The proof exceeded 24 rule-application rounds') | |
| + if atom not in active or rank(depth, support) < rank(depths[atom], proof[atom]): | |
| + active.add(atom); proof[atom], depths[atom] = support, depth | |
| + if atom not in queued: | |
| + agenda.append(atom); queued.add(atom) | |
| + self.last_stats = dict(engine='sparse-horn-graph-ca', grounded_rules=len(grounded), | |
| + rule_transitions=work, active_fact_cells=len(active), | |
| + maximum_proof_depth=max(depths.values(), default=0)) | |
| + return active, proof, depths | |
| def query(self, context, question): | |
| active, proofs, depths = self.close(context) | |
| --- a/cael_unified/reasoning_order.py | |
| +++ b/cael_unified/reasoning_order.py | |
| """Bounded integer constraints over one explicit, tie-free ordering. | |
| -Checks all permutations, distinguishes possibility from necessity, and requires | |
| +Propagates finite domains, distinguishes possibility from necessity, and requires | |
| complete surface clauses. No learned weights, network calls or benchmark data. | |
| """ | |
| from functools import lru_cache | |
| -from itertools import permutations | |
| import re | |
| from .reasoning_logic import Unparsed, sentences | |
| -ORDINAL = dict(zip('first second third fourth fifth sixth seventh eighth'.split(), range(8))) | |
| +ORDINAL = dict(zip('first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelfth'.split(), range(12))) | |
| LIST = r'(?:items|people|objects|participants|runners|books|cars|order)\s*:' | |
| if not parts or ':' not in parts[0]:raise Unparsed('An ordering puzzle needs an explicit item list') | |
| items = re.sub(r'\s+and\s+', ',', parts[0].split(':',1)[1],flags=re.I) | |
| names = tuple(clean(item) for item in items.split(',') if clean(item)) | |
| - if len(set(names)) != len(names) or not 2 <= len(names) <= 8:raise Unparsed('Use 2 to 8 distinct items') | |
| + if len(set(names)) != len(names) or not 2 <= len(names) <= 12:raise Unparsed('Use 2 to 12 distinct items') | |
| if any(not re.fullmatch(r'[\w]+(?:[ -][\w]+)*',name) for name in names):raise Unparsed('Unsupported item name') | |
| constraints = tuple(condition(part,names) for part in parts[1:]) | |
| if len(dimensions(' '.join(parts[1:]),names))>1: | |
| raise Unparsed('Use one ordering dimension; age, price and spatial orders are separate') | |
| - solutions = [] | |
| - for order in permutations(names): | |
| - positions = {name:i for i,name in enumerate(order)} | |
| - if all(holds(c,positions) for c in constraints):solutions.append(order) | |
| - return names,tuple(solutions) | |
| + from .constraint_automaton import enumerate_orders, SearchLimit | |
| + try: solutions, stats = enumerate_orders(names, constraints, holds) | |
| + except (ValueError, SearchLimit) as exc: raise Unparsed(str(exc)) from exc | |
| + return names, solutions | |
| def proposition(question, names): | |
| --- a/cael_unified/reasoning_world.py | |
| +++ b/cael_unified/reasoning_world.py | |
| """Local packet automaton for bounded chronological fact/ownership scenarios.""" | |
| from dataclasses import dataclass, field | |
| +from collections import defaultdict | |
| import re | |
| from .reasoning_logic import Unparsed, sentences | |
| self.model = model | |
| self.cells = {} | |
| self.gifts = [] | |
| + self.carried = defaultdict(set) | |
| + self.transition_visits = 0 | |
| def event(self, statement): | |
| statement = statement.lower().strip(' .') | |
| def apply(self, event, index): | |
| op, actor, value, *extra = event | |
| + if op not in ('move', 'uncertain', 'exclude', 'take', 'drop', 'give'): | |
| + raise Unparsed('Unknown world transition') | |
| + # Validate the complete transaction before mutating any cell. | |
| + old_actor = self.cells.get(actor) | |
| + old_object = self.cells.get(value) if op in ('take', 'drop', 'give') else None | |
| + if old_object and old_object.owner not in (None, actor): | |
| + raise Unparsed('The acting person is not the established holder of this object') | |
| + if op == 'give' and (len(extra) != 1 or extra[0] == actor): | |
| + raise Unparsed('A transfer requires one distinct recipient') | |
| + if op in ('take', 'give') and old_actor and old_actor.locations: | |
| + other = old_object if op == 'take' else self.cells.get(extra[0]) | |
| + if other and other.locations and old_actor.locations.isdisjoint(other.locations): | |
| + raise Unparsed('The interaction conflicts with the stated locations') | |
| if actor not in self.cells: | |
| self.cells[actor] = Cell(actor, 'actor') | |
| if op in ('take', 'drop','give') and value not in self.cells: | |
| self.cells[extra[0]]=Cell(extra[0],'actor') | |
| self.gifts.append((actor,value,extra[0],index)) | |
| actor_cell = self.cells[actor] | |
| - packet = dict(op=op, actor=actor, value=value, extra=extra, support={index} | actor_cell.support, | |
| - locations=set(actor_cell.locations)) | |
| - rows = list(self.cells.values()) | |
| - carriers = [None] * len(rows) | |
| - carriers[0] = packet | |
| - # A packet moves one neighbouring cell per synchronous tick. Each cell | |
| - # reads only its own state and its local packet, as in the story CA. | |
| - for _ in range(len(rows)): | |
| - following = [None] * len(rows) | |
| - for position, cell in enumerate(rows): | |
| - message = carriers[position] | |
| - if message is None: | |
| - continue | |
| - if op in ('move', 'uncertain', 'exclude') and (cell.name == actor or cell.owner == actor): | |
| - if op == 'move': | |
| - self.locate(cell, {value}, {index} if cell.name == actor else cell.support | {index}) | |
| - elif op == 'uncertain': | |
| - self.locate(cell, {value, extra[0]}, {index}) | |
| + # Ownership edges define the local neighbourhood. No empty-slot | |
| + # N-by-N packet scan: only actor/owned-object cells receive the event. | |
| + if op in ('move', 'uncertain', 'exclude'): | |
| + for name in [actor, *sorted(self.carried[actor])]: | |
| + cell = self.cells[name]; self.transition_visits += 1 | |
| + if op == 'move': | |
| + self.locate(cell, {value}, {index} if name == actor else cell.support | {index}) | |
| + elif op == 'uncertain': | |
| + self.locate(cell, {value, extra[0]}, {index} if name == actor else cell.support | {index}) | |
| + else: | |
| + remaining = cell.locations - {value}; support = cell.support | {index} | |
| + if remaining: | |
| + self.locate(cell, remaining, support) | |
| else: | |
| - cell.locations = set() | |
| - cell.excluded = {value} | |
| - cell.support = {index} | |
| - elif op in ('take', 'drop','give') and cell.name == value: | |
| - if op == 'drop' and cell.owner not in (None, actor): | |
| - raise Unparsed('An actor cannot drop an object held by someone else') | |
| - cell.owner = extra[0] if op=='give' else actor if op == 'take' else None | |
| - self.locate(cell, message['locations'], message['support']) | |
| - if position + 1 < len(rows): | |
| - following[position + 1] = message | |
| - carriers = following | |
| + cell.locations = set(); cell.support = support | |
| + cell.excluded.add(value) | |
| + else: | |
| + cell = self.cells[value]; self.transition_visits += 1 | |
| + if cell.owner is not None: self.carried[cell.owner].discard(value) | |
| + cell.owner = extra[0] if op == 'give' else actor if op == 'take' else None | |
| + if cell.owner is not None: self.carried[cell.owner].add(value) | |
| + support = {index} | actor_cell.support | |
| + if op in ('drop', 'give'): support |= cell.support | |
| + self.locate(cell, set(actor_cell.locations), support) | |
| def query(self, context, question): | |
| self.cells = {} | |
| self.gifts = [] | |
| + self.carried = defaultdict(set) | |
| + self.transition_visits = 0 | |
| for index, statement in enumerate(sentences(context), 1): | |
| self.apply(self.event(statement), index) | |
| question = question.lower().strip(' ?.!.') | |
| --- a/cael_unified/self_knowledge.json | |
| +++ b/cael_unified/self_knowledge.json | |
| { | |
| - "version": "0.3.9.1", | |
| + "version": "0.4.0", | |
| "changes": [ | |
| - "Native readers now interpret complete supported arithmetic, rule, ordering, location, string and table tasks before unrelated chat fallbacks.", | |
| - "Supported interpretations compile to checked symbolic operations using the existing non-neural engines; weights and the resource bundle are unchanged.", | |
| - "Unsupported wording or constraints can still lead to clarification or abstention rather than a guessed answer.", | |
| - "Installed version, change-list and capability questions use local product metadata before public web lookup." | |
| + "This is an isolated unpublished 0.4.0 core-engine candidate, not the current Vynly worker.", | |
| + "Indexed rule-graph propagation, sparse validated world transitions and finite-domain ordering replace earlier core algorithms.", | |
| + "A new non-neural local transition learner infers bounded discrete updates from explicit examples.", | |
| + "Live references are traversed iteratively; retrieval honours requested neighbour counts and deterministic ties." | |
| ], | |
| "capabilities": [ | |
| - "Solve supported standalone arithmetic, rule chains, precedence, location updates, string operations and table reductions.", | |
| - "Keep supported context and explicit preferences from the messages in this chat.", | |
| - "Calculate exact arithmetic and recheck supported calculations.", | |
| - "Write and check bounded Python examples; unsupported code needs clarification.", | |
| - "Read public sources and show links when a supported answer can be extracted.", | |
| - "Explain Vynly features and help write image prompts." | |
| + "Execute supported exact arithmetic, rule, state, ordering and bounded Python tasks.", | |
| + "Infer finite local discrete transitions from explicit input/output examples, reporting empirical guesses and ambiguity.", | |
| + "Retain supported context from the supplied conversation while isolating independent requests." | |
| ], | |
| "limitations": [ | |
| - "Finite readers miss unsupported wording. In the fresh 900-question study, 123 questions received no scored answer.", | |
| - "I still miss some phrasings, option switches, dictionary follow-ups and requests for richer examples.", | |
| - "My Python interpreter and language readers cover finite patterns, not arbitrary programs or English.", | |
| - "A retrieved source is evidence, not proof that an answer is true.", | |
| - "Supported forgetting is not general transcript erasure." | |
| + "This candidate is not published, deployed or independently evaluated.", | |
| + "Finite readers still miss wording and complete context; no general chatbot or Qwen superiority is established.", | |
| + "An inferred local transition is a hypothesis, not proof of a unique general rule.", | |
| + "Inherited weights, protected solvers, image CA kernels and the 0.3.7 public resource bundle are unchanged." | |
| ], | |
| - "evidence": "On one fresh 900-question synthetic study, the evaluated native candidate scored 777/900 versus 414/900 for the unchanged 0.3.9 baseline. The release preserves those task modules and adds version metadata. Known task families, maintainer/AI-assisted authoring and fixed neural letter scoring limit the result. This does not establish general chat competence or a Qwen win; Qwen was not part of this study. The evaluated count fallback was inactive; a fresh installation activates it, so that primary score is configuration-specific.", | |
| - "sources": [ | |
| - { | |
| - "title": "CAEL 0.3.9.1 release notes", | |
| - "url": "https://github.com/Vovala14/CAEL/releases/tag/v0.3.9.1" | |
| - }, | |
| - { | |
| - "title": "CAEL 0.3.9.1 model card", | |
| - "url": "https://huggingface.co/vynly/CAEL-0.3.9.1" | |
| - }, | |
| - { | |
| - "title": "Fresh interpretation study and complete evidence", | |
| - "url": "https://huggingface.co/datasets/vynly/nnai-bench/tree/main/fresh-transfer/2026-11-07-native" | |
| - } | |
| + "evidence": "Authored core development and regressions are separate from external benchmark results. This source snapshot claims no CellARC score or broad 0.4 win. See local CORE_0_4.md for scope.", | |
| + "sources": [], | |
| + "local_document_provenance": [ | |
| + "CORE_0_4.md" | |
| ], | |
| - "local_document_provenance": [ | |
| - "README.md", | |
| - "RELEASE_0_3_9_1.md", | |
| - "benchmarks/native_interpretation_0391/REPORT.md" | |
| - ], | |
| - "scope": "Curated installed product facts; never user messages or retrieved instructions." | |
| + "scope": "Curated isolated-candidate product facts, not user or benchmark answer data." | |
| } | |