Metadata-Version: 2.4
Name: proact-py
Version: 0.2.1
Summary: Active logic meets Prolog — three-valued reasoning engine
License-Expression: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"

# Proact

```
pip install proact-py
```

Prolog like language for decision making and parallel reasoning under uncertainty. Proact adds a continuation state to Prolog's truth values, signalling uncertainty.

Every goal returns a status (3 state value). Conjunction is a sequence. Disjunction is a selector. Backtracking only triggers on failure — `cont` and `done` cut the search. Between ticks, queries returning `cont` are automatically re-evaluated while the database persists, giving you a stateless tick loop for free.

Applications:

- **Reasoning under uncertainty.** The three values map onto epistemic states: *verified*, *undetermined*, *refuted*. A conjunction of checks naturally implements iterative deepening — work focuses on the earliest unresolved premise. Unary operators give you cognitive control: `demote` means "don't commit yet" (done→cont); `promote` means "don't give up yet" (fail→cont). This is relevant to validation layers for LLM reasoning, where forcing every check into true/false is lossy compression of the actual epistemic state.

- **Behavior modeling.** Reactive agents, planners, robot controllers — anything where logic meets time. Prolog gives you unification, backtracking, and pattern matching. Active logic adds temporal flow. You get behavior trees with the expressiveness of logic programming, without the trees.

## Python integration

```python
from proact import Proact

p = Proact()
p.add("mortal(X) :- human(X). human(socrates).")
print(p.query("mortal(socrates)"))  # Result(status='done')
```

## Using Proact in terminal

```
./proact                          # interactive mode
./proact file.pa                  # load file, then interactive mode
./proact file.pa -e "goal."      # evaluate goal and exit
./proact --max-ticks 100 -e "g." # limit ticks; run until resolved or budget exhausted
```

## Tutorials

- [Reasoning under uncertainty](tutorial/reasoning.md) — from predicate logic to three-valued epistemics
- [Behavior modeling](tutorial/behavior.md) — behavior trees via logic programming

## The Transform

### Conjunction (`,`) — Active logic AND / Sequence

```
         fail  cont  done
  fail   fail  fail  fail
  cont   cont  cont  cont
  done   fail  cont  done
```

If the left goal is `cont`, the whole conjunction is `cont` — don't evaluate the right side yet.

### Disjunction (`;`) — Active logic OR / Selector

```
         fail  cont  done
  fail   fail  cont  done
  cont   cont  cont  cont
  done   done  done  done
```

If the left goal is `done` or `cont`, don't try alternatives.

### Clause resolution

Multiple clauses for the same predicate provide choice points. Backtracking occurs **only on fail** — if a clause body returns `cont` or `done`, stop searching.

### Tick loop

Queries returning `cont` are automatically re-evaluated on the next tick with a fresh environment. The database (asserted facts) persists between ticks.

## Built-ins

| Predicate | Status | Description |
|---|---|---|
| `done` / `true` | done | Always succeeds |
| `fail` / `false` | fail | Always fails |
| `cont` | cont | Always continues |
| `inv(G)` / `\+ G` | swap done/fail, keep cont | Inverter |
| `condone(G)` | fail→done | Forgive failure |
| `promote(G)` | fail→cont, cont→done | Optimistic |
| `demote(G)` | done→cont, cont→fail | Pessimistic |
| `par_any(A,B)` | max(A,B) | Lenient parallel |
| `par_all(A,B)` | min(A,B) | Strict parallel |
| `tick(N)` | done | Unify N with current tick |

Plus standard Prolog: `write/1`, `writeln/1`, `nl`, `assert/1`, `retract/1`, `is/2`, `=/2`, `\=/2`, `</2`, `>/2`, `>=/2`, `=</2`, `call/1`, `consult/1`.

## Examples

### Wait N ticks, then succeed

```prolog
wait(N) :- tick(T), T >= N.
wait(N) :- tick(T), T < N, cont.
```

```
?- wait(3), writeln(hello).
[tick 0] cont
[tick 1] cont
[tick 2] cont
hello
done.
```

### Counter (stateful, using assert/retract)

```prolog
:- assert(count(0)).

count_to(Target) :- count(N), N >= Target.
count_to(Target) :-
    count(N), N < Target,
    retract(count(N)), N1 is N + 1, assert(count(N1)),
    cont.
```

### Reactive behavior (condition-gated sequence)

```prolog
:- assert(agent_pos(0)).

move_to(X) :- agent_pos(P), P =:= X.
move_to(X) :- agent_pos(P), P < X,
    retract(agent_pos(P)), P1 is P + 1, assert(agent_pos(P1)), cont.

patrol :-
    agent_pos(P), P < 3, move_to(3), cont.
patrol :-
    agent_pos(P), P >= 3, P < 5, move_to(5), cont.
patrol :-
    agent_pos(P), P >= 5.
```

### Reasoning under uncertainty

```prolog
% reason.pa — three-valued validation for multi-step arguments

:- assert(known(premise_a)).
:- assert(investigation(premise_b, 2)).
:- assert(investigation(premise_c, 1)).

check(P) :- known(P).
check(P) :- known_false(P), fail.
check(P) :-
    investigation(P, N), N > 0,
    retract(investigation(P, N)), N1 is N - 1,
    (N1 > 0 -> assert(investigation(P, N1)) ; assert(known(P))),
    cont.

argument :- check(premise_a), check(premise_b), check(premise_c).
```

The conjunction *is* the search strategy — it focuses work on the earliest unresolved premise and doesn't waste computation on steps whose preconditions haven't been met:

```
[tick 0] a=done, b=cont          → cont  (c never reached)
[tick 1] a=done, b=done, c=cont  → cont  (work shifts to c)
[tick 2] a=done, b=done, c=done  → done
```

The epistemic operators give cognitive control over the validation:

```prolog
cautious(X)       :- demote(check(X)).    % "verified but needs review"  (done→cont)
keep_exploring(X) :- promote(check(X)).   % "refuted but try harder"     (fail→cont)
viable_theory     :- par_any(hyp_a, hyp_b). % first hypothesis verified wins
```

## Relation to active-logic

Proact implements the core active logic operators:

| C# (activelogic-cs) | Proact | BT equivalent |
|---|---|---|
| `status && status` | `Goal, Goal` | Sequence |
| `status \|\| status` | `Goal ; Goal` | Selector |
| `+` (lenient) | `par_any(A, B)` | Parallel (any) |
| `*` (strict) | `par_all(A, B)` | Parallel (all) |
| `!status` | `inv(G)` | Inverter |
| `~status` | `condone(G)` | Condone |
| `+status` (unary) | `promote(G)` | Promoter |
| `-status` (unary) | `demote(G)` | Demoter |
| `cont` | `cont` | Running |
