@@ -1267,5 +1267,116 @@ TEST(AttentionTest, TestAttention4DWithPastAndPresentQkMatmulBias4DMaskCausal) {
12671267 );
12681268}
12691269
1270+ // Test for attention when past_key and past_value are nullptr.
1271+ // This test verifies the updated logic handles the case correctly when no past state is provided.
1272+ TEST(AttentionTest, AttentionNoPastKeyValue) {
1273+ int batch_size = 1; // Q.shape[0]
1274+ int q_num_heads = 2; // Q.shape[1]
1275+ int q_sequence_length = 2; // Q.shape[2]
1276+ int head_size = 3; // Q.shape[3]
1277+ int kv_sequence_length = 2; // K.shape[2] and V.shape[2]
1278+ int kv_num_heads = 2; // K.shape[1] and V.shape[1]
1279+ int v_head_size = 3; // V.shape[3]
1280+ int past_sequence_length = 0; // No past state
1281+
1282+ std::vector<float> q = {
1283+ 0.548814f, 0.715189f, 0.602763f,
1284+ 0.423655f, 0.645894f, 0.437587f,
1285+ 0.963663f, 0.383442f, 0.791725f,
1286+ 0.568045f, 0.925597f, 0.071036f};
1287+
1288+ std::vector<float> k = {
1289+ 0.186193f, 0.944372f, 0.739551f,
1290+ 0.227415f, 0.254356f, 0.058029f,
1291+ 0.311796f, 0.696343f, 0.377752f,
1292+ 0.024679f, 0.067250f, 0.679393f};
1293+
1294+ std::vector<float> v = {
1295+ 0.070870f, 0.292794f, 0.152355f,
1296+ 0.131289f, 0.604118f, 0.382808f,
1297+ 0.967795f, 0.546885f, 0.274824f,
1298+ 0.896761f, 0.406733f, 0.552078f};
1299+
1300+ ASSERT_EQ(q.size(), batch_size * q_num_heads * q_sequence_length * head_size);
1301+ ASSERT_EQ(k.size(), batch_size * kv_num_heads * kv_sequence_length * head_size);
1302+ ASSERT_EQ(v.size(), batch_size * kv_num_heads * kv_sequence_length * v_head_size);
1303+
1304+ std::vector<float> expected_y = {
1305+ 0.477184f, 0.407899f, 0.207834f,
1306+ 0.521223f, 0.503569f, 0.469034f,
1307+ 0.485670f, 0.410303f, 0.208993f,
1308+ 0.487087f, 0.512371f, 0.461486f};
1309+
1310+ // Test with no past_key or past_value (empty vectors)
1311+ // The empty vectors will cause AddOptionalInputEdge to be called, resulting in nullptr tensors
1312+ RunTest3D(batch_size, q_num_heads, q_sequence_length, head_size, kv_sequence_length, kv_num_heads, v_head_size, past_sequence_length,
1313+ q, k, v, std::vector<float>(), std::initializer_list<bool>(), std::vector<float>(), std::vector<float>(),
1314+ -1, -1, std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN(), -1, TensorType::kFloat,
1315+ expected_y, std::vector<float>(), std::vector<float>(), std::vector<float>(),
1316+ false, true, true // disable_cpu, disable_cuda, disable_dml
1317+ );
1318+ }
1319+
1320+ // Test for attention with present_key/present_value outputs but no past state
1321+ // This ensures the ConcatStateChunk logic works correctly when past_key/past_value are nullptr
1322+ TEST(AttentionTest, AttentionNoPastWithPresentOutput) {
1323+ int batch_size = 1; // Q.shape[0]
1324+ int q_num_heads = 2; // Q.shape[1]
1325+ int q_sequence_length = 2; // Q.shape[2]
1326+ int head_size = 2; // Q.shape[3]
1327+ int kv_sequence_length = 2; // K.shape[2] and V.shape[2]
1328+ int kv_num_heads = 2; // K.shape[1] and V.shape[1]
1329+ int v_head_size = 2; // V.shape[3]
1330+ int past_sequence_length = 0; // No past state
1331+
1332+ std::vector<float> q = {
1333+ 0.5f, 0.8f,
1334+ 0.3f, 0.9f,
1335+ 0.7f, 0.4f,
1336+ 0.6f, 0.2f};
1337+
1338+ std::vector<float> k = {
1339+ 0.1f, 0.7f,
1340+ 0.4f, 0.6f,
1341+ 0.8f, 0.3f,
1342+ 0.2f, 0.9f};
1343+
1344+ std::vector<float> v = {
1345+ 1.0f, 2.0f,
1346+ 3.0f, 4.0f,
1347+ 0.5f, 1.5f,
1348+ 2.5f, 3.5f};
1349+
1350+ // The output key/value has 4d shapes. So the data is transposed
1351+ std::vector<float> expected_present_key = {
1352+ 0.1f, 0.7f,
1353+ 0.8f, 0.3f,
1354+ 0.4f, 0.6f,
1355+ 0.2f, 0.9f};
1356+
1357+ std::vector<float> expected_present_value = {
1358+ 1.0f, 2.0f,
1359+ 0.5f, 1.5f,
1360+ 3.0f, 4.0f,
1361+ 2.5f, 3.5f};
1362+
1363+ ASSERT_EQ(q.size(), batch_size * q_num_heads * q_sequence_length * head_size);
1364+ ASSERT_EQ(k.size(), batch_size * kv_num_heads * kv_sequence_length * head_size);
1365+ ASSERT_EQ(v.size(), batch_size * kv_num_heads * kv_sequence_length * v_head_size);
1366+
1367+ std::vector<float> expected_y = {
1368+ 0.747348f, 1.747348f,
1369+ 2.731472f, 3.731472f,
1370+ 0.720963f, 1.720963f,
1371+ 2.755302f, 3.755302f};
1372+
1373+ RunTest3D(batch_size, q_num_heads, q_sequence_length, head_size, kv_sequence_length, kv_num_heads, v_head_size, past_sequence_length,
1374+ q, k, v, std::vector<float>(), std::initializer_list<bool>(), std::vector<float>(), std::vector<float>(),
1375+ -1, -1, std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN(), -1, TensorType::kFloat,
1376+ expected_y, expected_present_key, expected_present_value, std::vector<float>(),
1377+ false, true, true // disable_cpu, disable_cuda, disable_dml
1378+ );
1379+ }
1380+
12701381} // namespace test
12711382} // namespace onnxruntime
0 commit comments