Skip to content

Commit 1ded1f2

Browse files
committed
sat: Add symmetry_util_test.cc
1 parent 2e6b0be commit 1ded1f2

File tree

5 files changed

+232
-67
lines changed

5 files changed

+232
-67
lines changed

cmake/cpp.cmake

+72-66
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,78 @@ if(MSVC)
141141
)
142142
endif()
143143

144+
################
145+
## C++ Test ##
146+
################
147+
# ortools_cxx_test()
148+
# CMake function to generate and build C++ test.
149+
# Parameters:
150+
# FILE_NAME: the C++ filename
151+
# COMPONENT_NAME: name of the ortools/ subdir where the test is located
152+
# note: automatically determined if located in ortools/<component>/
153+
# e.g.:
154+
# ortools_cxx_test(
155+
# FILE_NAME
156+
# ${PROJECT_SOURCE_DIR}/ortools/foo/foo_test.cc
157+
# COMPONENT_NAME
158+
# foo
159+
# DEPS
160+
# GTest::gmock
161+
# GTest::gtest_main
162+
# )
163+
function(ortools_cxx_test)
164+
set(options "")
165+
set(oneValueArgs "FILE_NAME;COMPONENT_NAME")
166+
set(multiValueArgs "DEPS")
167+
cmake_parse_arguments(TEST
168+
"${options}"
169+
"${oneValueArgs}"
170+
"${multiValueArgs}"
171+
${ARGN}
172+
)
173+
if(NOT TEST_FILE_NAME)
174+
message(FATAL_ERROR "no FILE_NAME provided")
175+
endif()
176+
get_filename_component(TEST_NAME ${TEST_FILE_NAME} NAME_WE)
177+
178+
message(STATUS "Configuring test ${TEST_FILE_NAME} ...")
179+
180+
if(NOT TEST_COMPONENT_NAME)
181+
# test is located in ortools/<component_name>/
182+
get_filename_component(COMPONENT_DIR ${TEST_FILE_NAME} DIRECTORY)
183+
get_filename_component(COMPONENT_NAME ${COMPONENT_DIR} NAME)
184+
else()
185+
set(COMPONENT_NAME ${TEST_COMPONENT_NAME})
186+
endif()
187+
188+
add_executable(${TEST_NAME} ${TEST_FILE_NAME})
189+
target_include_directories(${TEST_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
190+
target_compile_features(${TEST_NAME} PRIVATE cxx_std_17)
191+
target_link_libraries(${TEST_NAME} PRIVATE
192+
${PROJECT_NAMESPACE}::ortools
193+
${TEST_DEPS}
194+
)
195+
196+
include(GNUInstallDirs)
197+
if(APPLE)
198+
set_target_properties(${TEST_NAME} PROPERTIES INSTALL_RPATH
199+
"@loader_path/../${CMAKE_INSTALL_LIBDIR};@loader_path")
200+
elseif(UNIX)
201+
cmake_path(RELATIVE_PATH CMAKE_INSTALL_FULL_LIBDIR
202+
BASE_DIRECTORY ${CMAKE_INSTALL_FULL_BINDIR}
203+
OUTPUT_VARIABLE libdir_relative_path)
204+
set_target_properties(${TEST_NAME} PROPERTIES
205+
INSTALL_RPATH "$ORIGIN/${libdir_relative_path}")
206+
endif()
207+
208+
if(BUILD_TESTING)
209+
add_test(
210+
NAME cxx_${COMPONENT_NAME}_${TEST_NAME}
211+
COMMAND ${TEST_NAME})
212+
endif()
213+
message(STATUS "Configuring test ${TEST_FILE_NAME} ...DONE")
214+
endfunction()
215+
144216
##################
145217
## PROTO FILE ##
146218
##################
@@ -556,72 +628,6 @@ install(DIRECTORY ortools/routing/docs/
556628
PATTERN "*.md")
557629
endif()
558630

559-
################
560-
## C++ Test ##
561-
################
562-
# add_cxx_test()
563-
# CMake function to generate and build C++ test.
564-
# Parameters:
565-
# FILE_NAME: the C++ filename
566-
# COMPONENT_NAME: name of the ortools/ subdir where the test is located
567-
# note: automatically determined if located in ortools/<component>/
568-
# e.g.:
569-
# add_cxx_test(
570-
# FILE_NAME
571-
# ${PROJECT_SOURCE_DIR}/ortools/foo/foo_test.cc
572-
# COMPONENT_NAME
573-
# foo
574-
# )
575-
function(add_cxx_test)
576-
set(options "")
577-
set(oneValueArgs FILE_NAME COMPONENT_NAME)
578-
set(multiValueArgs "")
579-
cmake_parse_arguments(TEST
580-
"${options}"
581-
"${oneValueArgs}"
582-
"${multiValueArgs}"
583-
${ARGN}
584-
)
585-
if(NOT TEST_FILE_NAME)
586-
message(FATAL_ERROR "no FILE_NAME provided")
587-
endif()
588-
get_filename_component(TEST_NAME ${TEST_FILE_NAME} NAME_WE)
589-
590-
message(STATUS "Configuring test ${TEST_FILE_NAME} ...")
591-
592-
if(NOT TEST_COMPONENT_NAME)
593-
# test is located in ortools/<component_name>/
594-
get_filename_component(COMPONENT_DIR ${TEST_FILE_NAME} DIRECTORY)
595-
get_filename_component(COMPONENT_NAME ${COMPONENT_DIR} NAME)
596-
else()
597-
set(COMPONENT_NAME ${TEST_COMPONENT_NAME})
598-
endif()
599-
600-
add_executable(${TEST_NAME} ${TEST_FILE_NAME})
601-
target_include_directories(${TEST_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
602-
target_compile_features(${TEST_NAME} PRIVATE cxx_std_17)
603-
target_link_libraries(${TEST_NAME} PRIVATE ${PROJECT_NAMESPACE}::ortools)
604-
605-
include(GNUInstallDirs)
606-
if(APPLE)
607-
set_target_properties(${TEST_NAME} PROPERTIES INSTALL_RPATH
608-
"@loader_path/../${CMAKE_INSTALL_LIBDIR};@loader_path")
609-
elseif(UNIX)
610-
cmake_path(RELATIVE_PATH CMAKE_INSTALL_FULL_LIBDIR
611-
BASE_DIRECTORY ${CMAKE_INSTALL_FULL_BINDIR}
612-
OUTPUT_VARIABLE libdir_relative_path)
613-
set_target_properties(${TEST_NAME} PROPERTIES
614-
INSTALL_RPATH "$ORIGIN/${libdir_relative_path}")
615-
endif()
616-
617-
if(BUILD_TESTING)
618-
add_test(
619-
NAME cxx_${COMPONENT_NAME}_${TEST_NAME}
620-
COMMAND ${TEST_NAME})
621-
endif()
622-
message(STATUS "Configuring test ${TEST_FILE_NAME} ...DONE")
623-
endfunction()
624-
625631
##################
626632
## C++ Sample ##
627633
##################

examples/tests/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ endif()
1818
if(BUILD_CXX_EXAMPLES)
1919
file(GLOB CXX_SRCS "*.cc")
2020
foreach(FILE_NAME IN LISTS CXX_SRCS)
21-
add_cxx_test(FILE_NAME ${FILE_NAME})
21+
ortools_cxx_test(FILE_NAME ${FILE_NAME})
2222
endforeach()
2323
endif()
2424

ortools/sat/BUILD.bazel

+11
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,17 @@ cc_library(
10351035
],
10361036
)
10371037

1038+
cc_test(
1039+
name = "symmetry_util_test",
1040+
size = "small",
1041+
srcs = ["symmetry_util_test.cc"],
1042+
deps = [
1043+
":symmetry_util",
1044+
"//ortools/algorithms:sparse_permutation",
1045+
"//ortools/base:gmock_main",
1046+
],
1047+
)
1048+
10381049
cc_library(
10391050
name = "var_domination",
10401051
srcs = ["var_domination.cc"],

ortools/sat/CMakeLists.txt

+14
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# limitations under the License.
1313

1414
file(GLOB _SRCS "*.h" "*.cc")
15+
list(FILTER _SRCS EXCLUDE REGEX ".*/.*_test.cc")
1516
list(REMOVE_ITEM _SRCS
1617
${CMAKE_CURRENT_SOURCE_DIR}/opb_reader.h
1718
${CMAKE_CURRENT_SOURCE_DIR}/sat_cnf_reader.h
@@ -39,6 +40,19 @@ target_link_libraries(${NAME} PRIVATE
3940
${PROJECT_NAMESPACE}::ortools_proto)
4041
#add_library(${PROJECT_NAMESPACE}::sat ALIAS ${NAME})
4142

43+
if(BUILD_TESTING)
44+
file(GLOB _TEST_SRCS "*_test.cc")
45+
foreach(FILE_NAME IN LISTS _TEST_SRCS)
46+
ortools_cxx_test(
47+
FILE_NAME
48+
${FILE_NAME}
49+
DEPS
50+
GTest::gmock
51+
GTest::gtest_main
52+
)
53+
endforeach()
54+
endif()
55+
4256
# Sat Runner
4357
add_executable(sat_runner)
4458
target_sources(sat_runner PRIVATE "sat_runner.cc")

ortools/sat/symmetry_util_test.cc

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright 2010-2024 Google LLC
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
#include "ortools/sat/symmetry_util.h"
15+
16+
#include <memory>
17+
#include <vector>
18+
19+
#include "gtest/gtest.h"
20+
#include "ortools/algorithms/sparse_permutation.h"
21+
#include "ortools/base/gmock.h"
22+
23+
namespace operations_research {
24+
namespace sat {
25+
namespace {
26+
27+
using ::testing::ElementsAre;
28+
29+
TEST(GetOrbitsTest, BasicExample) {
30+
const int n = 10;
31+
std::vector<std::unique_ptr<SparsePermutation>> generators;
32+
generators.push_back(std::make_unique<SparsePermutation>(n));
33+
generators[0]->AddToCurrentCycle(0);
34+
generators[0]->AddToCurrentCycle(1);
35+
generators[0]->AddToCurrentCycle(2);
36+
generators[0]->CloseCurrentCycle();
37+
generators[0]->AddToCurrentCycle(7);
38+
generators[0]->AddToCurrentCycle(8);
39+
generators[0]->CloseCurrentCycle();
40+
41+
generators.push_back(std::make_unique<SparsePermutation>(n));
42+
generators[1]->AddToCurrentCycle(3);
43+
generators[1]->AddToCurrentCycle(2);
44+
generators[1]->AddToCurrentCycle(7);
45+
generators[1]->CloseCurrentCycle();
46+
const std::vector<int> orbits = GetOrbits(n, generators);
47+
for (const int i : std::vector<int>{0, 1, 2, 3, 7, 8}) {
48+
EXPECT_EQ(orbits[i], 0);
49+
}
50+
for (const int i : std::vector<int>{4, 5, 6, 9}) {
51+
EXPECT_EQ(orbits[i], -1);
52+
}
53+
}
54+
55+
// Recover for generators (in a particular form)
56+
// [0, 1, 2]
57+
// [4, 5, 3]
58+
// [8, 7, 6]
59+
TEST(BasicOrbitopeExtractionTest, BasicExample) {
60+
const int n = 10;
61+
std::vector<std::unique_ptr<SparsePermutation>> generators;
62+
63+
generators.push_back(std::make_unique<SparsePermutation>(n));
64+
generators[0]->AddToCurrentCycle(0);
65+
generators[0]->AddToCurrentCycle(1);
66+
generators[0]->CloseCurrentCycle();
67+
generators[0]->AddToCurrentCycle(4);
68+
generators[0]->AddToCurrentCycle(5);
69+
generators[0]->CloseCurrentCycle();
70+
generators[0]->AddToCurrentCycle(8);
71+
generators[0]->AddToCurrentCycle(7);
72+
generators[0]->CloseCurrentCycle();
73+
74+
generators.push_back(std::make_unique<SparsePermutation>(n));
75+
generators[1]->AddToCurrentCycle(2);
76+
generators[1]->AddToCurrentCycle(1);
77+
generators[1]->CloseCurrentCycle();
78+
generators[1]->AddToCurrentCycle(5);
79+
generators[1]->AddToCurrentCycle(3);
80+
generators[1]->CloseCurrentCycle();
81+
generators[1]->AddToCurrentCycle(6);
82+
generators[1]->AddToCurrentCycle(7);
83+
generators[1]->CloseCurrentCycle();
84+
85+
const std::vector<std::vector<int>> orbitope =
86+
BasicOrbitopeExtraction(generators);
87+
ASSERT_EQ(orbitope.size(), 3);
88+
EXPECT_THAT(orbitope[0], ElementsAre(0, 1, 2));
89+
EXPECT_THAT(orbitope[1], ElementsAre(4, 5, 3));
90+
EXPECT_THAT(orbitope[2], ElementsAre(8, 7, 6));
91+
}
92+
93+
// This one is trickier and is not an orbitope because 8 appear twice. So it
94+
// would be incorrect to "grow" the first two columns with the 3rd one.
95+
// [0, 1, 2]
96+
// [4, 5, 8]
97+
// [8, 7, 9]
98+
TEST(BasicOrbitopeExtractionTest, NotAnOrbitopeBecauseOfDuplicates) {
99+
const int n = 10;
100+
std::vector<std::unique_ptr<SparsePermutation>> generators;
101+
102+
generators.push_back(std::make_unique<SparsePermutation>(n));
103+
generators[0]->AddToCurrentCycle(0);
104+
generators[0]->AddToCurrentCycle(1);
105+
generators[0]->CloseCurrentCycle();
106+
generators[0]->AddToCurrentCycle(4);
107+
generators[0]->AddToCurrentCycle(5);
108+
generators[0]->CloseCurrentCycle();
109+
generators[0]->AddToCurrentCycle(8);
110+
generators[0]->AddToCurrentCycle(7);
111+
generators[0]->CloseCurrentCycle();
112+
113+
generators.push_back(std::make_unique<SparsePermutation>(n));
114+
generators[1]->AddToCurrentCycle(1);
115+
generators[1]->AddToCurrentCycle(2);
116+
generators[1]->CloseCurrentCycle();
117+
generators[1]->AddToCurrentCycle(5);
118+
generators[1]->AddToCurrentCycle(8);
119+
generators[1]->CloseCurrentCycle();
120+
generators[1]->AddToCurrentCycle(6);
121+
generators[1]->AddToCurrentCycle(9);
122+
generators[1]->CloseCurrentCycle();
123+
124+
const std::vector<std::vector<int>> orbitope =
125+
BasicOrbitopeExtraction(generators);
126+
ASSERT_EQ(orbitope.size(), 3);
127+
EXPECT_THAT(orbitope[0], ElementsAre(0, 1));
128+
EXPECT_THAT(orbitope[1], ElementsAre(4, 5));
129+
EXPECT_THAT(orbitope[2], ElementsAre(8, 7));
130+
}
131+
132+
} // namespace
133+
} // namespace sat
134+
} // namespace operations_research

0 commit comments

Comments
 (0)