1 #ifndef PACHI_UCT_TREE_H
2 #define PACHI_UCT_TREE_H
4 /* Management of UCT trees. See diagram below for the node structure.
6 * Two allocation methods are supported for the tree nodes:
8 * - calloc/free: each node is allocated with one calloc.
9 * After a move, all nodes except the subtree rooted at
10 * the played move are freed one by one with free().
11 * Since this can be very slow (seen 9s and loss on time because
12 * of this) the nodes are freed in a background thread.
13 * We still reserve enough memory for the next move in case
14 * the background thread doesn't free nodes fast enough.
16 * - fast_alloc: a large buffer is allocated once, and each
17 * node allocation takes some of this buffer. After a move
18 * is played, no memory if freed if the buffer still has
19 * enough free space. Otherwise the subtree rooted at the
20 * played move is copied to a temporary buffer, pruning it
21 * if necessary to fit in this small buffer. We copy by
22 * preference nodes with largest number of playouts.
23 * Then the temporary buffer is copied back to the original
24 * buffer, which has now plenty of space.
25 * Once the fast_alloc mode is proven reliable, the
26 * calloc/free method will be removed. */
42 * +------+ v- sibling +------+
43 * | node | ------------ | node |
46 * +------+ +------+ +------+ +------+
47 * | node | - | node | | node | - | node |
48 * +------+ +------+ +------+ +------+
51 /* TODO: Performance would benefit from a reorganization:
52 * (i) Allocate all children of a node within a single block.
53 * (ii) Keep all u stats together, and all amaf stats together.
54 * Currently, rave_update is top source of cache misses, and
55 * there is large memory overhead for having all nodes separate. */
59 struct tree_node
*parent
, *sibling
, *children
;
61 /*** From here on, struct is saved/loaded from opening tbook */
64 struct move_stats prior
;
65 /* XXX: Should be way for policies to add their own stats */
66 struct move_stats amaf
;
67 /* Stats before starting playout; used for distributed engine. */
69 /* Criticality information; information about final board owner
70 * of the tree coordinate corresponding to the node */
71 struct move_stats winner_owner
; // owner == winner
72 struct move_stats black_owner
; // owner == black
74 /* coord is usually coord_t, but this is very space-sensitive. */
75 #define node_coord(n) ((int) (n)->coord)
78 unsigned short depth
; // just for statistics
80 /* Number of parallel descents going through this node at the moment.
81 * Used for virtual loss computation. */
84 /* Common Fate Graph distance from parent, but at most TREE_NODE_D_MAX+1 */
85 #define TREE_NODE_D_MAX 3
88 #define TREE_HINT_INVALID 1 // don't go to this node, invalid move
91 /* In case multiple threads walk the tree, is_expanded is set
92 * atomically. Only the first thread setting it expands the node.
93 * The node goes through 3 states:
94 * 1) children == null, is_expanded == false: leaf node
95 * 2) children == null, is_expanded == true: one thread currently expanding
96 * 2) children != null, is_expanded == true: fully expanded node */
104 struct tree_node
*root
;
105 struct board_symmetry root_symmetry
;
106 enum stone root_color
;
108 /* Whether to use any extra komi during score counting. This is
109 * tree-specific variable since this can arbitrarily change between
112 /* A single-move-valid flag that marks a tree that is potentially
113 * badly skewed and should be used with care. Currently, we never
114 * resign on untrustworthy_tree and do not reuse the tree on next
116 bool untrustworthy_tree
;
117 /* The value of applied extra komi. For DYNKOMI_LINEAR, this value
118 * is only informative, the actual value is computed per simulation
119 * based on leaf node depth. */
120 floating_t extra_komi
;
121 /* Score in simulations, averaged over all branches, in the last
123 struct move_stats avg_score
;
125 /* We merge local (non-tenuki) sequences for both colors, occuring
126 * anywhere in the tree; nodes are created on-demand, special 'pass'
127 * nodes represent tenuki. Only u move_stats are used, prior and amaf
128 * is ignored. Values in root node are ignored. */
129 /* The value corresponds to black-to-play as usual; i.e. if white
130 * succeeds in its replies, the values will be low. */
131 struct tree_node
*ltree_black
;
132 /* ltree_white has white-first sequences as children. */
133 struct tree_node
*ltree_white
;
134 /* Aging factor; 2 means halve all playout values after each turn.
135 * 1 means don't age at all. */
136 floating_t ltree_aging
;
138 /* Hash table used when working as slave for the distributed engine.
139 * Maps coordinate path to tree node. */
140 struct tree_hash
*htable
;
145 volatile unsigned long nodes_size
; // byte size of all allocated nodes
146 unsigned long max_tree_size
; // maximum byte size for entire tree, > 0 only for fast_alloc
147 unsigned long max_pruned_size
;
148 unsigned long pruning_threshold
;
149 void *nodes
; // nodes buffer, only for fast_alloc
152 /* Warning: all functions below except tree_expand_node & tree_leaf_node are THREAD-UNSAFE! */
153 struct tree
*tree_init(struct board
*board
, enum stone color
, unsigned long max_tree_size
,
154 unsigned long max_pruned_size
, unsigned long pruning_threshold
, floating_t ltree_aging
, int hbits
);
155 void tree_done(struct tree
*tree
);
156 void tree_dump(struct tree
*tree
, double thres
);
157 void tree_save(struct tree
*tree
, struct board
*b
, int thres
);
158 void tree_load(struct tree
*tree
, struct board
*b
);
160 struct tree_node
*tree_get_node(struct tree
*tree
, struct tree_node
*node
, coord_t c
, bool create
);
161 struct tree_node
*tree_garbage_collect(struct tree
*tree
, struct tree_node
*node
);
162 void tree_promote_node(struct tree
*tree
, struct tree_node
**node
);
163 bool tree_promote_at(struct tree
*tree
, struct board
*b
, coord_t c
);
165 void tree_expand_node(struct tree
*tree
, struct tree_node
*node
, struct board
*b
, enum stone color
, struct uct
*u
, int parity
);
166 struct tree_node
*tree_lnode_for_node(struct tree
*tree
, struct tree_node
*ni
, struct tree_node
*lni
, int tenuki_d
);
168 static bool tree_leaf_node(struct tree_node
*node
);
170 #define tree_node_parity(tree, node) \
171 ((((node)->depth ^ (tree)->root->depth) & 1) ? -1 : 1)
173 /* Get black parity from parity within the tree. */
174 #define tree_parity(tree, parity) \
175 (tree->root_color == S_WHITE ? (parity) : -1 * (parity))
177 /* Get a 0..1 value to maximize; @parity is parity within the tree. */
178 #define tree_node_get_value(tree, parity, value) \
179 (tree_parity(tree, parity) > 0 ? value : 1 - value)
182 tree_leaf_node(struct tree_node
*node
)
184 return !(node
->children
);
187 static inline floating_t
188 tree_node_criticality(const struct tree
*t
, const struct tree_node
*node
)
190 /* cov(player_gets, player_wins) =
191 * [The argument: If 'gets' and 'wins' is uncorrelated, b_gets * b_wins
192 * is valid way to obtain winner_gets. The more correlated it is, the
193 * more distorted the result.]
194 * = winner_gets - (b_gets * b_wins + w_gets * w_wins)
195 * = winner_gets - (b_gets * b_wins + (1 - b_gets) * (1 - b_wins))
196 * = winner_gets - (b_gets * b_wins + 1 - b_gets - b_wins + b_gets * b_wins)
197 * = winner_gets - (2 * b_gets * b_wins - b_gets - b_wins + 1) */
198 return node
->winner_owner
.value
199 - (2 * node
->black_owner
.value
* node
->u
.value
200 - node
->black_owner
.value
- node
->u
.value
+ 1);