Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c03f90edd | |||
| 7c06f1a850 | |||
| 2c4e5918b9 | |||
| c2128879f1 |
+31
-9
@@ -2,7 +2,7 @@
|
||||
|
||||
julia_version = "1.12.6"
|
||||
manifest_format = "2.0"
|
||||
project_hash = "3e47cb4426f8b430734383590f86ec8b7094cfd2"
|
||||
project_hash = "eefceed38d1e3b7216d3f052806b5d83d4fd80b7"
|
||||
|
||||
[[deps.Accessors]]
|
||||
deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "MacroTools"]
|
||||
@@ -179,6 +179,11 @@ git-tree-sha1 = "7442a5dfe1ebb773c29cc2962a8980f47221d76c"
|
||||
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
|
||||
version = "0.9.5"
|
||||
|
||||
[[deps.EnumX]]
|
||||
git-tree-sha1 = "c49898e8438c828577f04b92fc9368c388ac783c"
|
||||
uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
|
||||
version = "1.0.7"
|
||||
|
||||
[[deps.FilePathsBase]]
|
||||
deps = ["Compat", "Dates"]
|
||||
git-tree-sha1 = "3bab2c5aa25e7840a4b065805c0cdfc01f3068d2"
|
||||
@@ -220,13 +225,24 @@ deps = ["Random"]
|
||||
uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820"
|
||||
version = "1.11.0"
|
||||
|
||||
[[deps.Gamma]]
|
||||
git-tree-sha1 = "86f86b6168a016ed88e4ae4e64577b98c3b59e8e"
|
||||
uuid = "a0844989-3bd2-4988-8bea-c9407ab0941b"
|
||||
version = "1.1.0"
|
||||
|
||||
[[deps.GeneralUtils]]
|
||||
deps = ["CSV", "DataFrames", "DataStructures", "Dates", "Distributions", "JSON", "NATS", "PrettyPrinting", "Random", "Revise", "SHA", "UUIDs"]
|
||||
git-tree-sha1 = "f1bad7621e6ac2d235adc8c593b0aff87bd1d93e"
|
||||
deps = ["CSV", "DataFrames", "DataStructures", "Dates", "Distributions", "HTTP", "JSON", "NATS", "PrettyPrinting", "Random", "Revise", "SHA", "UUIDs"]
|
||||
git-tree-sha1 = "7c0600c166a5deb2c607018a491c04eb25969c2e"
|
||||
repo-rev = "main"
|
||||
repo-url = "https://git.yiem.cc/ton/GeneralUtils"
|
||||
uuid = "c6c72f09-b708-4ac8-ac7c-2084d70108fe"
|
||||
version = "0.4.2"
|
||||
version = "0.4.9"
|
||||
|
||||
[[deps.HTTP]]
|
||||
deps = ["Base64", "CodecZlib", "Dates", "EnumX", "PrecompileTools", "Random", "Reseau", "SHA", "URIs", "UUIDs", "Zlib_jll"]
|
||||
git-tree-sha1 = "eda1d37cb55d90a17d0957c75841138c88b361a1"
|
||||
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
|
||||
version = "2.5.4"
|
||||
|
||||
[[deps.HashArrayMappedTries]]
|
||||
git-tree-sha1 = "2eaa69a7cab70a52b9687c8bf950a5a93ec895ae"
|
||||
@@ -234,10 +250,10 @@ uuid = "076d061b-32b6-4027-95e0-9a2c6f6d7e74"
|
||||
version = "0.2.0"
|
||||
|
||||
[[deps.HypergeometricFunctions]]
|
||||
deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"]
|
||||
git-tree-sha1 = "68c173f4f449de5b438ee67ed0c9c748dc31a2ec"
|
||||
deps = ["Gamma", "LinearAlgebra"]
|
||||
git-tree-sha1 = "18d7deab5fb0440dc6a7b6993c5c27b25420de10"
|
||||
uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a"
|
||||
version = "0.3.28"
|
||||
version = "0.3.29"
|
||||
|
||||
[[deps.InlineStrings]]
|
||||
git-tree-sha1 = "8f3d257792a522b4601c24a577954b0a8cd7334d"
|
||||
@@ -553,6 +569,12 @@ git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
|
||||
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
|
||||
version = "1.2.2"
|
||||
|
||||
[[deps.Reseau]]
|
||||
deps = ["NetworkOptions", "OpenSSL_jll", "PrecompileTools", "Random", "SHA"]
|
||||
git-tree-sha1 = "0eab6d95ed40c2ef3992255c1c71e4f9748932b5"
|
||||
uuid = "802f3686-a58f-41ce-bb0c-3c43c75bba36"
|
||||
version = "1.3.1"
|
||||
|
||||
[[deps.Revise]]
|
||||
deps = ["CRC32c", "CodeTracking", "FileWatching", "InteractiveUtils", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Preferences", "REPL", "UUIDs"]
|
||||
git-tree-sha1 = "27e3ee13fc8739a59b380d6163d6a82f52c03bd7"
|
||||
@@ -579,9 +601,9 @@ version = "0.5.1+0"
|
||||
|
||||
[[deps.Roots]]
|
||||
deps = ["Accessors", "CommonSolve", "Printf"]
|
||||
git-tree-sha1 = "91cfb1cb4f6e27557cc2df798a31eff6089a41eb"
|
||||
git-tree-sha1 = "ed45bcc7cf3c8887595b973f2b1efbe91dcc50ec"
|
||||
uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
|
||||
version = "3.0.0"
|
||||
version = "3.0.1"
|
||||
|
||||
[deps.Roots.extensions]
|
||||
RootsChainRulesCoreExt = "ChainRulesCore"
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
name = "LLMMCTS"
|
||||
uuid = "d76c5a4d-449e-4835-8cc4-dd86ec44f241"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
authors = ["narawat lamaiin <narawat@outlook.com>"]
|
||||
|
||||
[deps]
|
||||
@@ -9,6 +9,6 @@ JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
|
||||
PrettyPrinting = "54e16d92-306c-5ea0-a30b-337be88ac337"
|
||||
|
||||
[compat]
|
||||
GeneralUtils = "0.4.2"
|
||||
GeneralUtils = "0.4.0 - 0.9.0"
|
||||
JSON = "1.6.1"
|
||||
PrettyPrinting = "0.4.2"
|
||||
|
||||
+25
-25
@@ -74,7 +74,7 @@ function runMCTS(
|
||||
multithread=false,
|
||||
)::NamedTuple{(:root, :bestNextState, :bestTerminalState, :highValueStateList),
|
||||
Tuple{MCTSNode,T,T,Vector{Dict{String,Any}}}} where {T<:Any}
|
||||
println("--> LLMMCTS runMCTS 1")
|
||||
|
||||
# Initialize the MCTS tree with a root node representing the initial state
|
||||
# root.visits=0: no visits yet
|
||||
# root.statevalue=0: no simulation results yet
|
||||
@@ -91,38 +91,38 @@ function runMCTS(
|
||||
# Start from root and traverse down using UCT selection
|
||||
node = root
|
||||
node.visits += 1 # Count this iteration's visit to root
|
||||
println("--> LLMMCTS runMCTS 2")
|
||||
|
||||
# Phase 1: SELECTION - Traverse tree using UCT until reaching a leaf node
|
||||
# UCT balances exploration (new branches) vs exploitation (promising branches)
|
||||
while !isleaf(node)
|
||||
println("--> LLMMCTS runMCTS 3")
|
||||
|
||||
node = UCTselect(node, explorationweight)
|
||||
end
|
||||
println("--> LLMMCTS runMCTS 4")
|
||||
|
||||
# Phase 2: TERMINAL CHECK - If leaf is terminal, just backpropagate
|
||||
if node.isterminal
|
||||
println("--> LLMMCTS runMCTS 5")
|
||||
|
||||
# If this terminal state has high reward (>= 8), store it for later
|
||||
if node.state[:reward] >= 8
|
||||
println("--> LLMMCTS runMCTS 6")
|
||||
|
||||
put!(highValueState, deepcopy(node.state))
|
||||
end
|
||||
println("--> LLMMCTS runMCTS 7")
|
||||
|
||||
# Backpropagate the terminal node's own reward up to root
|
||||
# This updates all ancestors with this path's outcome
|
||||
backpropagate(node, node.reward)
|
||||
else
|
||||
println("--> LLMMCTS runMCTS 8")
|
||||
|
||||
# Phase 3: EXPANSION - Generate children for this non-terminal leaf
|
||||
# Horizontal sampling: create multiple child nodes via LLM transition
|
||||
_ = expand(node, transition, transitionargs;
|
||||
horizontalSample=horizontalSampleExpansionPhase,
|
||||
multithread=multithread)
|
||||
println("--> LLMMCTS runMCTS 9")
|
||||
|
||||
# Phase 4: SIMULATION + BACKPROPAGATION
|
||||
# For each newly expanded child, run simulation and update statistics
|
||||
if multithread
|
||||
println("--> LLMMCTS runMCTS 10")
|
||||
|
||||
# Parallel simulation: spawn threads for each child node
|
||||
@sync for (leafNodeKey, leafNode) in node.children
|
||||
@spawn simulateThenBackpropagate(leafNode, transition, transitionargs;
|
||||
@@ -134,10 +134,10 @@ function runMCTS(
|
||||
)
|
||||
end
|
||||
else
|
||||
println("--> LLMMCTS runMCTS 11")
|
||||
|
||||
# Sequential simulation: process each child one at a time
|
||||
for (leafNodeKey, leafNode) in node.children
|
||||
println("--> LLMMCTS runMCTS 11-1")
|
||||
|
||||
simulateThenBackpropagate(leafNode, transition, transitionargs;
|
||||
maxSimulationDepth=maxSimulationDepth,
|
||||
horizontalSampleSimulationPhase=horizontalSampleSimulationPhase,
|
||||
@@ -147,29 +147,29 @@ function runMCTS(
|
||||
end
|
||||
end
|
||||
end
|
||||
println("--> LLMMCTS runMCTS 12")
|
||||
|
||||
# Phase 5: EARLY STOP CHECK
|
||||
# Optional: stop search early if a condition is met
|
||||
if typeof(earlystop) <: Function && earlystop(node.state)
|
||||
println("--> LLMMCTS runMCTS 13")
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
println("--> LLMMCTS runMCTS 14")
|
||||
|
||||
# After all iterations, extract results from the search tree
|
||||
# Select best immediate next state (best child of root)
|
||||
bestNextState = selectBestNextNode(root)
|
||||
println("--> LLMMCTS runMCTS 15")
|
||||
|
||||
# Select best terminal state along the optimal trajectory
|
||||
bestTerminalState = selectBestTrajectoryNode(root)
|
||||
|
||||
# Collect all high-value states from the channel into a list
|
||||
highValueStateList = Vector{Dict{String, Any}}()
|
||||
while !isempty(highValueState)
|
||||
println("--> LLMMCTS runMCTS 16")
|
||||
|
||||
push!(highValueStateList, take!(highValueState))
|
||||
end
|
||||
println("--> LLMMCTS runMCTS 17")
|
||||
|
||||
# Return complete search results
|
||||
result = (
|
||||
root=root,
|
||||
@@ -218,7 +218,7 @@ function simulateThenBackpropagate(node::MCTSNode, transition::Function, transit
|
||||
saveSimulatedNode::Bool=false,
|
||||
multithread=false,
|
||||
highValueState=Union{Nothing,Any}=nothing)
|
||||
println("--> LLMMCTS simulateThenBackpropagate 1")
|
||||
|
||||
# Phase 1: RUN SIMULATION (rollout)
|
||||
# Perform a rollout from this node, accumulating rewards along the way
|
||||
simTrajectoryReward, terminalstate =
|
||||
@@ -226,30 +226,30 @@ function simulateThenBackpropagate(node::MCTSNode, transition::Function, transit
|
||||
maxSimulationDepth=maxSimulationDepth,
|
||||
horizontalSample=horizontalSampleSimulationPhase,
|
||||
multithread=multithread)
|
||||
println("--> LLMMCTS simulateThenBackpropagate 2")
|
||||
|
||||
# Phase 2: HIGH-VALUE STATE TRACKING
|
||||
# If we reached a terminal state with high reward (>= 8), store it
|
||||
# This allows users to access multiple good solutions, not just the best one
|
||||
if highValueState !== nothing &&
|
||||
terminalstate !== nothing &&
|
||||
terminalstate["reward"] >= 8
|
||||
println("--> LLMMCTS simulateThenBackpropagate 3")
|
||||
|
||||
put!(highValueState, deepcopy(terminalstate))
|
||||
end
|
||||
println("--> LLMMCTS simulateThenBackpropagate 4")
|
||||
|
||||
# Phase 3: BACKPROPAGATE
|
||||
# Update statistics (visits, statevalue) for all ancestors up to root
|
||||
# The simulation result is now incorporated into the tree
|
||||
backpropagate(node, simTrajectoryReward)
|
||||
println("--> LLMMCTS simulateThenBackpropagate 5")
|
||||
|
||||
# Phase 4: MEMORY MANAGEMENT
|
||||
# Clear children unless user wants to keep them for analysis
|
||||
# This frees memory for the next iteration while preserving tree structure
|
||||
if saveSimulatedNode == false
|
||||
println("--> LLMMCTS simulateThenBackpropagate 6")
|
||||
|
||||
node.children = Dict{String, MCTSNode}()
|
||||
end
|
||||
println("--> LLMMCTS simulateThenBackpropagate 7")
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
+19
-19
@@ -108,14 +108,14 @@ leaf node to the root, applying reward discounting for future rewards.
|
||||
"""
|
||||
function backpropagate(node::MCTSNode, simTrajectoryReward::T;
|
||||
discountRewardCoeff::AbstractFloat=0.9) where {T<:Number}
|
||||
println("--> LLMMCTS backpropagate 1")
|
||||
|
||||
# Propagate the simulation result back up the tree to update all ancestor nodes
|
||||
# Each node's statistics are updated with the cumulative reward from the simulation
|
||||
while !isroot(node)
|
||||
println("--> LLMMCTS backpropagate 2")
|
||||
|
||||
# Increment visit count - this simulation passed through this node
|
||||
node.visits += 1
|
||||
println("--> LLMMCTS backpropagate 3")
|
||||
|
||||
node.statevalue += ((node.statevalue * (node.visits-1)) + simTrajectoryReward) / node.visits # Update running average of state value
|
||||
|
||||
# Apply discount to future rewards - rewards further from the current state are worth less
|
||||
@@ -125,7 +125,7 @@ function backpropagate(node::MCTSNode, simTrajectoryReward::T;
|
||||
# Move up to parent node to continue propagation
|
||||
node = node.parent
|
||||
end
|
||||
println("--> LLMMCTS backpropagate 4")
|
||||
|
||||
end
|
||||
|
||||
""" Determine whether a node is a leaf node.
|
||||
@@ -227,15 +227,15 @@ function expand(node::MCTSNode,transition::Function, transitionargs::NamedTuple;
|
||||
# This is called "horizontal sampling" - we branch out horizontally in the tree
|
||||
# - multithread=true: spawn parallel threads for each expansion
|
||||
# - multithread=false: sequential expansion (default, simpler)
|
||||
println("--> LLMMCTS expand 1")
|
||||
|
||||
if multithread
|
||||
@sync for i in 1:horizontalSample
|
||||
@spawn _expand(node, transition, transitionargs)
|
||||
end
|
||||
else
|
||||
println("--> LLMMCTS expand 2")
|
||||
|
||||
for i in 1:horizontalSample
|
||||
println("--> LLMMCTS expand 3")
|
||||
|
||||
_expand(node, transition, transitionargs)
|
||||
end
|
||||
end
|
||||
@@ -258,17 +258,17 @@ Checks for semantically equivalent states (dejavu) to avoid duplicates.
|
||||
- `Nothing`
|
||||
"""
|
||||
function _expand(node::MCTSNode,transition::Function, transitionargs::NamedTuple)
|
||||
println("--> LLMMCTS _expand 1")
|
||||
|
||||
# Generate one child node from the parent using the transition function
|
||||
result = transition(node.state, transitionargs)
|
||||
newNodeKey::AbstractString = result[:newNodeKey]
|
||||
newstate::AbstractDict = result[:newstate]
|
||||
progressvalue::Integer = result[:progressvalue]
|
||||
println("--> LLMMCTS _expand 2")
|
||||
|
||||
# Dejavu detection: avoid adding duplicate states
|
||||
# If newNodeKey already exists, skip - this handles semantically equivalent states
|
||||
if newNodeKey ∉ keys(node.children)
|
||||
println("--> LLMMCTS _expand 3")
|
||||
|
||||
# Create new MCTS node with:
|
||||
# - visits=0: no simulations yet
|
||||
# - statevalue=0: will be updated after simulation
|
||||
@@ -276,9 +276,9 @@ function _expand(node::MCTSNode,transition::Function, transitionargs::NamedTuple
|
||||
# - reward: immediate environment feedback
|
||||
newNode = MCTSNode(newNodeKey, newstate, 0, progressvalue, 0, newstate["reward"],
|
||||
newstate["isterminal"], node, Dict{String, MCTSNode}(), Dict{String, Any}())
|
||||
println("--> LLMMCTS _expand 4")
|
||||
|
||||
node.children[newNodeKey] = newNode
|
||||
println("--> LLMMCTS _expand 5")
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -311,7 +311,7 @@ sampling child nodes at each level and accumulating rewards along the way.
|
||||
function simulate(node::MCTSNode, transition::Function, transitionargs::NamedTuple;
|
||||
maxSimulationDepth::Integer=3, horizontalSample::Integer=3, multithread=false
|
||||
)::NamedTuple{(:simTrajectoryReward, :terminalstate), Tuple{<:Number, Union{Dict{String, Any}, Nothing}}}
|
||||
println("--> LLMMCTS simulate 1")
|
||||
|
||||
# Perform a rollout simulation from the given node:
|
||||
# 1. Accumulate rewards along the trajectory
|
||||
# 2. Expand nodes horizontally at each level
|
||||
@@ -322,29 +322,29 @@ function simulate(node::MCTSNode, transition::Function, transitionargs::NamedTup
|
||||
terminalstate = nothing
|
||||
|
||||
for depth in 1:maxSimulationDepth
|
||||
println("--> LLMMCTS simulate 2")
|
||||
|
||||
# Accumulate the current node's reward to the trajectory total
|
||||
simTrajectoryReward += node.reward
|
||||
|
||||
# Check if we've reached a terminal state
|
||||
if node.isterminal
|
||||
println("--> LLMMCTS simulate 3")
|
||||
|
||||
terminalstate = node.state
|
||||
break
|
||||
else
|
||||
println("--> LLMMCTS simulate 4")
|
||||
|
||||
# Expand current node to generate children (horizontal sampling)
|
||||
_ = expand(node, transition, transitionargs;
|
||||
horizontalSample=horizontalSample,
|
||||
multithread=multithread)
|
||||
println("--> LLMMCTS simulate 5")
|
||||
|
||||
# Select best child to continue the rollout (vertical exploration)
|
||||
# Uses progressvalue + reward for fast selection during simulation
|
||||
node = selectChildNode(node)
|
||||
end
|
||||
println("--> LLMMCTS simulate 6")
|
||||
|
||||
end
|
||||
println("--> LLMMCTS simulate 7")
|
||||
|
||||
return (simTrajectoryReward=simTrajectoryReward,
|
||||
terminalstate=terminalstate)
|
||||
end
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# This file is machine-generated - editing it directly is not advised
|
||||
|
||||
julia_version = "1.11.4"
|
||||
manifest_format = "2.0"
|
||||
project_hash = "71d91126b5a1fb1020e1098d9d492de2a4438fd2"
|
||||
|
||||
[[deps.Base64]]
|
||||
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||
version = "1.11.0"
|
||||
|
||||
[[deps.InteractiveUtils]]
|
||||
deps = ["Markdown"]
|
||||
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
|
||||
version = "1.11.0"
|
||||
|
||||
[[deps.Logging]]
|
||||
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||
version = "1.11.0"
|
||||
|
||||
[[deps.Markdown]]
|
||||
deps = ["Base64"]
|
||||
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
|
||||
version = "1.11.0"
|
||||
|
||||
[[deps.Random]]
|
||||
deps = ["SHA"]
|
||||
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
version = "1.11.0"
|
||||
|
||||
[[deps.SHA]]
|
||||
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
|
||||
version = "0.7.0"
|
||||
|
||||
[[deps.Serialization]]
|
||||
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
|
||||
version = "1.11.0"
|
||||
|
||||
[[deps.Test]]
|
||||
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
|
||||
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||
version = "1.11.0"
|
||||
@@ -1,2 +0,0 @@
|
||||
[deps]
|
||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||
Reference in New Issue
Block a user