Rafs-an09002 commited on
Commit
26adafb
·
verified ·
1 Parent(s): 0994cfc

Create engine/evaluate.py

Browse files
Files changed (1) hide show
  1. engine/evaluate.py +125 -0
engine/evaluate.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Nexus-Nano Evaluator
3
+ Ultra-lightweight 2.8M parameter CNN
4
+
5
+ Research: MobileNet architecture principles for efficiency
6
+ """
7
+
8
+ import onnxruntime as ort
9
+ import numpy as np
10
+ import chess
11
+ import logging
12
+ from pathlib import Path
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class NexusNanoEvaluator:
18
+ """
19
+ Lightweight evaluator for Nexus-Nano
20
+ Optimized for speed over accuracy
21
+ """
22
+
23
+ PIECE_VALUES = {
24
+ chess.PAWN: 100,
25
+ chess.KNIGHT: 320,
26
+ chess.BISHOP: 330,
27
+ chess.ROOK: 500,
28
+ chess.QUEEN: 900,
29
+ chess.KING: 0
30
+ }
31
+
32
+ def __init__(self, model_path: str, num_threads: int = 1):
33
+ """Initialize with single-threaded ONNX session for speed"""
34
+
35
+ self.model_path = Path(model_path)
36
+ if not self.model_path.exists():
37
+ raise FileNotFoundError(f"Model not found: {model_path}")
38
+
39
+ # ONNX session (single-threaded for lowest latency)
40
+ sess_options = ort.SessionOptions()
41
+ sess_options.intra_op_num_threads = num_threads
42
+ sess_options.inter_op_num_threads = num_threads
43
+ sess_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL
44
+ sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
45
+
46
+ logger.info(f"Loading Nexus-Nano model...")
47
+ self.session = ort.InferenceSession(
48
+ str(self.model_path),
49
+ sess_options=sess_options,
50
+ providers=['CPUExecutionProvider']
51
+ )
52
+
53
+ self.input_name = self.session.get_inputs()[0].name
54
+ self.output_name = self.session.get_outputs()[0].name
55
+
56
+ logger.info(f"✅ Model loaded: {self.input_name} -> {self.output_name}")
57
+
58
+ def fen_to_tensor(self, board: chess.Board) -> np.ndarray:
59
+ """
60
+ Fast 12-channel tensor conversion
61
+ Optimized for minimal overhead
62
+ """
63
+ tensor = np.zeros((1, 12, 8, 8), dtype=np.float32)
64
+
65
+ # Piece to channel mapping
66
+ piece_channels = {
67
+ chess.PAWN: 0, chess.KNIGHT: 1, chess.BISHOP: 2,
68
+ chess.ROOK: 3, chess.QUEEN: 4, chess.KING: 5
69
+ }
70
+
71
+ # Fast piece placement
72
+ for square, piece in board.piece_map().items():
73
+ rank, file = divmod(square, 8)
74
+ channel = piece_channels[piece.piece_type]
75
+ if piece.color == chess.BLACK:
76
+ channel += 6
77
+ tensor[0, channel, rank, file] = 1.0
78
+
79
+ return tensor
80
+
81
+ def evaluate_neural(self, board: chess.Board) -> float:
82
+ """
83
+ Fast neural evaluation
84
+ Single forward pass, minimal post-processing
85
+ """
86
+ input_tensor = self.fen_to_tensor(board)
87
+ outputs = self.session.run([self.output_name], {self.input_name: input_tensor})
88
+
89
+ # Raw value (tanh output)
90
+ raw_value = float(outputs[0][0][0])
91
+
92
+ # Scale to centipawns
93
+ return raw_value * 300.0 # Slightly lower scale for faster games
94
+
95
+ def evaluate_material(self, board: chess.Board) -> int:
96
+ """Fast material count"""
97
+ material = 0
98
+
99
+ for piece_type, value in self.PIECE_VALUES.items():
100
+ if piece_type == chess.KING:
101
+ continue
102
+ white = len(board.pieces(piece_type, chess.WHITE))
103
+ black = len(board.pieces(piece_type, chess.BLACK))
104
+ material += (white - black) * value
105
+
106
+ return material
107
+
108
+ def evaluate_hybrid(self, board: chess.Board) -> float:
109
+ """
110
+ Fast hybrid: 85% neural + 15% material
111
+ Higher material weight for stability in fast games
112
+ """
113
+ neural = self.evaluate_neural(board)
114
+ material = self.evaluate_material(board)
115
+
116
+ hybrid = 0.85 * neural + 0.15 * material
117
+
118
+ if board.turn == chess.BLACK:
119
+ hybrid = -hybrid
120
+
121
+ return hybrid
122
+
123
+ def get_model_size_mb(self) -> float:
124
+ """Get model size"""
125
+ return self.model_path.stat().st_size / (1024 * 1024)