Skip to content

Commit 5b088f2

Browse files
vikriaspefk
andauthored
feat core: update duplicate_gates_cleaner (#7)
Co-authored-by: spefk <f.kurmazov.b@gmail.com>
1 parent a820f68 commit 5b088f2

23 files changed

Lines changed: 332 additions & 279 deletions

app/simplifier.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ std::ifstream openFileStream(std::string const& file_path, csat::Logger& logger)
3939
/**
4040
* Helper to run specific simplification strategies on the circuit in the provided basis.
4141
*/
42-
std::tuple<std::unique_ptr<csat::DAG>, std::unique_ptr<csat::utils::GateEncoder<std::string> > > applySimplification(
42+
std::tuple<std::unique_ptr<csat::DAG>, std::unique_ptr<csat::utils::GateEncoder> > applySimplification(
4343
std::string const& basis,
4444
std::unique_ptr<csat::DAG>& csat_instance,
45-
csat::utils::GateEncoder<std::string>& encoder)
45+
csat::utils::GateEncoder& encoder)
4646
{
4747
if (basis == AIG_BASIS)
4848
{
@@ -81,7 +81,7 @@ std::tuple<std::unique_ptr<csat::DAG>, std::unique_ptr<csat::utils::GateEncoder<
8181
void writeResult(
8282
argparse::ArgumentParser const& program,
8383
csat::DAG const& simplified_circuit,
84-
csat::utils::GateEncoder<std::string> const& encoder,
84+
csat::utils::GateEncoder const& encoder,
8585
std::string const& file_path)
8686
{
8787
if (auto output_dir = program.present("-o"))

src/parser/ibench_parser.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ class IBenchParser : public ICircuitParser
4343
}
4444

4545
/* Encoder of inputs and gates. */
46-
csat::utils::GateEncoder<std::string> encoder;
46+
csat::utils::GateEncoder encoder;
4747

4848
/**
4949
* @return Encoder, built according to parser info.
5050
*/
5151
[[nodiscard]]
52-
csat::utils::GateEncoder<std::string> const& getEncoder() const
52+
csat::utils::GateEncoder const& getEncoder() const
5353
{
5454
return encoder;
5555
}

src/simplification/composition.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct Composition : public ITransformer<CircuitT>
3939
*/
4040
CircuitAndEncoder<CircuitT, std::string> transform(
4141
std::unique_ptr<CircuitT> circuit,
42-
std::unique_ptr<GateEncoder<std::string>> encoder)
42+
std::unique_ptr<GateEncoder> encoder)
4343
{
4444
auto _transformer = TransformerT();
4545
auto [_circuit, _encoder] = _transformer.transform(std::move(circuit), std::move(encoder));
@@ -61,7 +61,7 @@ struct Composition<CircuitT, TransformerT> : public ITransformer<CircuitT>
6161
public:
6262
CircuitAndEncoder<CircuitT, std::string> transform(
6363
std::unique_ptr<CircuitT> circuit,
64-
std::unique_ptr<GateEncoder<std::string>> encoder)
64+
std::unique_ptr<GateEncoder> encoder)
6565
{
6666
auto _transformer = TransformerT();
6767
return _transformer.transform(std::move(circuit), std::move(encoder));

src/simplification/constant_gate_reducer.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class ConstantGateReducer_ : public ITransformer<CircuitT>
5353
*/
5454
CircuitAndEncoder<CircuitT, std::string> transform(
5555
std::unique_ptr<CircuitT> circuit,
56-
std::unique_ptr<GateEncoder<std::string>> encoder)
56+
std::unique_ptr<GateEncoder> encoder)
5757
{
5858
logger.debug("START ConstantGateReducer");
5959

@@ -232,7 +232,7 @@ class ConstantGateReducer_ : public ITransformer<CircuitT>
232232

233233
return {
234234
std::make_unique<CircuitT>(std::move(gate_info), std::move(new_output_gates)),
235-
std::make_unique<GateEncoder<std::string>>(*encoder)};
235+
std::make_unique<GateEncoder>(*encoder)};
236236
};
237237

238238
private:
@@ -265,7 +265,7 @@ class ConstantGateReducer_ : public ITransformer<CircuitT>
265265
*/
266266
void createMiniCircuit_(
267267
GateInfoContainer& gate_info,
268-
GateEncoder<std::string>& encoder,
268+
GateEncoder& encoder,
269269
GateIdContainer& new_output_gates,
270270
std::string const& new_gate_name_prefix,
271271
GateId& circuit_size,
Lines changed: 117 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
#pragma once
22

3-
#include <algorithm>
4-
#include <map>
53
#include <memory>
6-
#include <string>
4+
#include <ranges>
5+
#include <sstream>
76
#include <type_traits>
7+
#include <unordered_map>
8+
#include <vector>
89

910
#include "src/algo.hpp"
10-
#include "src/common/csat_types.hpp"
1111
#include "src/simplification/transformer_base.hpp"
12-
#include "src/structures/circuit/gate_info.hpp"
13-
#include "src/structures/circuit/icircuit.hpp"
1412
#include "src/utility/converters.hpp"
15-
#include "src/utility/encoder.hpp"
16-
#include "src/utility/logger.hpp"
1713

1814
namespace csat::simplification
1915
{
@@ -23,114 +19,186 @@ namespace csat::simplification
2319
* Duplicates are gates with the same operands and operator
2420
*
2521
* Note that this algorithm requires RedundantGatesCleaner to be applied right before.
22+
* Note that this algorithm will not reduce duplicate operands of gate, but will account
23+
* them "as one operand" during duplicates search. To reduce such gates one can use
24+
* separate `DuplicateOperandsCleaner` strategy.
2625
*
2726
* @tparam CircuitT
2827
*/
29-
template<class CircuitT, typename = std::enable_if_t<std::is_base_of_v<ICircuit, CircuitT>>>
28+
template<class CircuitT, typename = std::enable_if_t<std::is_base_of_v<ICircuit, CircuitT> > >
3029
class DuplicateGatesCleaner_ : public ITransformer<CircuitT>
3130
{
3231
csat::Logger logger{"DuplicateGatesCleaner"};
3332

3433
public:
3534
CircuitAndEncoder<CircuitT, std::string> transform(
3635
std::unique_ptr<CircuitT> circuit,
37-
std::unique_ptr<GateEncoder<std::string>> encoder)
36+
std::unique_ptr<GateEncoder> encoder)
3837
{
3938
logger.debug("=========================================================================================");
4039
logger.debug("START DuplicateGatesCleaner");
4140

42-
logger.debug("Top sort");
41+
GateEncoder new_encoder{};
42+
43+
logger.debug("Performing top sort");
44+
// Topsort, from inputs to outputs.
4345
csat::GateIdContainer gateSorting(algo::TopSortAlgorithm<algo::DFSTopSort>::sorting(*circuit));
4446
std::reverse(gateSorting.begin(), gateSorting.end());
4547

4648
logger.debug("Building mask to delete gates and filling map -- old_to_new_gateId");
4749
// 0 -- if gate is a duplicate, 1 -- otherwise
48-
BoolVector safe_mask(circuit->getNumberOfGates(), 1);
49-
// maps encoded (`operator_operand1_operand2...`) gate to new gate id
50-
GateEncoder<std::string> auxiliary_names_encoder{};
51-
// surjection of old gate ids to new gate ids
52-
std::map<GateId, GateId> old_to_new_gateId{};
53-
// bijection of old gate ids to new gate ids
54-
GateEncoder<GateId> new_encoder{};
55-
std::string encoded_name;
56-
50+
BoolVector safe_mask(circuit->getNumberOfGates(), true);
51+
// `auxiliary_encoder` helps to deduplicate gates by mapping same auxiliary name to one index.
52+
GateEncoder auxiliary_encoder{};
53+
// maps original gate ID to auxiliary ID, gotten from `auxiliary_encoder`.
54+
std::unordered_map<GateId, GateId> gate_id_to_auxiliary_id{};
55+
56+
logger.debug("Building mask to delete gates and filling map -- gate_id_to_auxiliary_id");
57+
std::string auxiliary_name;
5758
for (GateId gateId : gateSorting)
5859
{
59-
encoded_name = get_gate_auxiliary_name_(
60-
gateId, circuit->getGateType(gateId), circuit->getGateOperands(gateId), old_to_new_gateId);
61-
logger.debug("Gate number ", gateId, ". Its encoded_name is ", encoded_name);
60+
logger.debug("Processing gate ", gateId);
61+
auxiliary_name.clear();
62+
auxiliary_name = formatGateAuxiliaryName_(
63+
gateId, circuit->getGateType(gateId), circuit->getGateOperands(gateId), gate_id_to_auxiliary_id);
64+
logger.debug("Auxiliary name for gate ", gateId, " is ", auxiliary_name);
6265

63-
if (auxiliary_names_encoder.keyExists(encoded_name))
66+
if (auxiliary_encoder.keyExists(auxiliary_name))
6467
{
6568
logger.debug("Gate number ", gateId, " is a Duplicate and will be removed.");
66-
safe_mask.at(gateId) = 0;
69+
safe_mask.at(gateId) = false;
6770
}
6871
else
6972
{
70-
new_encoder.encodeGate(gateId);
73+
logger.debug(
74+
"Gate number ", gateId, " is either unique, or first of found duplicated, and will be saved.");
75+
new_encoder.encodeGate(encoder->decodeGate(gateId));
7176
}
7277

73-
old_to_new_gateId[gateId] = auxiliary_names_encoder.encodeGate(encoded_name);
78+
gate_id_to_auxiliary_id[gateId] = auxiliary_encoder.encodeGate(auxiliary_name);
7479
}
7580

7681
logger.debug("Building new circuit");
77-
GateInfoContainer gate_info(auxiliary_names_encoder.size());
82+
GateInfoContainer gate_info(auxiliary_encoder.size());
7883
for (GateId gateId = 0; gateId < circuit->getNumberOfGates(); ++gateId)
7984
{
80-
if (safe_mask.at(gateId) != 0)
85+
if (safe_mask.at(gateId))
8186
{
8287
logger.debug(
8388
"New Gate ",
84-
old_to_new_gateId.at(gateId),
89+
gate_id_to_auxiliary_id.at(gateId),
8590
"; Type: ",
8691
utils::gateTypeToString(circuit->getGateType(gateId)),
87-
"; Operands: ");
92+
"; Operands: ",
93+
formatOperandsString_(circuit->getGateOperands(gateId), gate_id_to_auxiliary_id).str());
8894

8995
GateIdContainer masked_operands_{};
9096
for (GateId operand : circuit->getGateOperands(gateId))
9197
{
92-
masked_operands_.push_back(old_to_new_gateId.at(operand));
93-
logger.debug(old_to_new_gateId.at(operand));
98+
masked_operands_.push_back(gate_id_to_auxiliary_id.at(operand));
9499
}
95-
gate_info.at(old_to_new_gateId.at(gateId)) = {circuit->getGateType(gateId), masked_operands_};
100+
gate_info.at(gate_id_to_auxiliary_id.at(gateId)) = {circuit->getGateType(gateId), masked_operands_};
96101
}
97102
}
98103

99104
GateIdContainer new_output_gates{};
100105
new_output_gates.reserve(circuit->getOutputGates().size());
101106
for (GateId output_gate : circuit->getOutputGates())
102107
{
103-
new_output_gates.push_back(old_to_new_gateId.at(output_gate));
108+
new_output_gates.push_back(gate_id_to_auxiliary_id.at(output_gate));
104109
}
105110

106111
logger.debug("END DuplicateGatesCleaner");
107112
logger.debug("=========================================================================================");
108-
return {
109-
std::make_unique<CircuitT>(gate_info, new_output_gates), utils::mergeGateEncoders(*encoder, new_encoder)};
113+
return {std::make_unique<CircuitT>(gate_info, new_output_gates), std::make_unique<GateEncoder>(new_encoder)};
110114
};
111115

112116
private:
113-
std::string get_gate_auxiliary_name_(
114-
GateId idx,
115-
GateType type,
117+
/**
118+
* Formats new auxiliary name for the gate based on its attributes (type, operands).
119+
* Such name may be used to determine literal duplicates in the circuit.
120+
*
121+
* @param gateId -- original ID of gate.
122+
* @param gateType -- type of gate.
123+
* @param operands -- gate's operand IDs.
124+
* @param encoder -- mapping of circuit gates' original IDs to new names. Must already contain
125+
* value for each operand of provided gate. It is needed to detect dependant duplicates
126+
* in only one circuit iteration.
127+
* @return auxiliary name of gate.
128+
*/
129+
std::string formatGateAuxiliaryName_(
130+
GateId gateId,
131+
GateType gateType,
116132
GateIdContainer const& operands,
117-
std::map<GateId, GateId> const& encoder)
133+
std::unordered_map<GateId, GateId> const& deduplicator)
118134
{
119-
std::string encoded_name;
120-
encoded_name = std::to_string(static_cast<int>(type));
135+
std::stringstream auxiliary_name;
136+
auxiliary_name << std::to_string(static_cast<uint8_t>(gateType));
137+
138+
// All Input gates are unique, but we can't differentiate them
139+
// by their operands (since there are none), so we simply add
140+
// their current (unique) index to auxiliary name and return.
141+
if (gateType == GateType::INPUT)
142+
{
143+
auxiliary_name << '_' + std::to_string(gateId);
144+
return auxiliary_name.str();
145+
}
121146

122-
if (type == GateType::INPUT)
147+
// Preparing set of operands, by accounting already found duplicates.
148+
GateIdContainer prepared_operands{};
149+
prepared_operands.reserve(operands.size());
150+
std::transform(
151+
operands.begin(),
152+
operands.end(),
153+
std::back_inserter(prepared_operands),
154+
[&deduplicator](GateId operand) { return deduplicator.at(operand); });
155+
156+
// For gates defined by symmetrical function operands are sorted during
157+
// circuit construction (see GateInfo), but then some of their operands
158+
// may be decided to be duplicates, so we resort them so such gates with
159+
// same set of operands will have same auxiliary name and will be detected
160+
// as duplicates.
161+
if (utils::symmetricOperatorQ(gateType))
123162
{
124-
encoded_name += '_' + std::to_string(idx);
163+
std::sort(prepared_operands.begin(), prepared_operands.end());
164+
// Since several operands of same gate may be decided to be duplicates
165+
// we can have here something like `AND(X, X, Y)`, which is equivalent
166+
// to the `AND(X, Y)`, hence to allow such duplicated to be found we
167+
// also clean such duplicates.
168+
if (utils::reducibleMultipleOperandsQ(gateType))
169+
{
170+
// Note: `unique` requires container to be sorted.
171+
auto last = std::unique(prepared_operands.begin(), prepared_operands.end());
172+
prepared_operands.erase(last, prepared_operands.end());
173+
}
125174
}
126175

127-
for (GateId const operand : operands)
176+
// Adds names of gate's operands to its auxiliary name.
177+
for (GateId operand : prepared_operands)
128178
{
129-
encoded_name += '_' + std::to_string(encoder.at(operand));
179+
auxiliary_name << '_' + std::to_string(operand);
130180
}
131181

132-
return encoded_name;
182+
return auxiliary_name.str();
133183
};
184+
185+
std::stringstream formatOperandsString_(
186+
GateIdContainer const& operands,
187+
std::unordered_map<GateId, GateId> const& encoder)
188+
{
189+
std::stringstream ss;
190+
if (operands.empty())
191+
{
192+
return ss;
193+
}
194+
195+
ss << encoder.at(operands[0]);
196+
for (auto ptr = operands.begin() + 1; ptr != operands.end(); ++ptr)
197+
{
198+
ss << ',' << encoder.at(*ptr);
199+
}
200+
return ss;
201+
}
134202
};
135203

136-
} // namespace csat::simplification
204+
} // namespace csat::simplification

src/simplification/duplicate_operands_cleaner.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class DuplicateOperandsCleaner_ : public ITransformer<CircuitT>
5656
*/
5757
CircuitAndEncoder<CircuitT, std::string> transform(
5858
std::unique_ptr<CircuitT> circuit,
59-
std::unique_ptr<GateEncoder<std::string>> encoder)
59+
std::unique_ptr<GateEncoder> encoder)
6060
{
6161
logger.debug("START DuplicateOperandsCleaner");
6262

@@ -265,7 +265,7 @@ class DuplicateOperandsCleaner_ : public ITransformer<CircuitT>
265265

266266
return {
267267
std::make_unique<CircuitT>(std::move(gate_info), std::move(new_output_gates)),
268-
std::make_unique<GateEncoder<std::string>>(*encoder)};
268+
std::make_unique<GateEncoder>(*encoder)};
269269
};
270270

271271
private:

src/simplification/nest.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ struct Nest : public ITransformer<CircuitT>
4646
*/
4747
CircuitAndEncoder<CircuitT, std::string> transform(
4848
std::unique_ptr<CircuitT> circuit,
49-
std::unique_ptr<GateEncoder<std::string>> encoder)
49+
std::unique_ptr<GateEncoder> encoder)
5050
{
51-
std::unique_ptr<CircuitT> circuit_ = std::move(circuit);
52-
std::unique_ptr<GateEncoder<std::string>> encoder_ = std::move(encoder);
51+
std::unique_ptr<CircuitT> circuit_ = std::move(circuit);
52+
std::unique_ptr<GateEncoder> encoder_ = std::move(encoder);
5353
for (std::size_t it = 0; it < n; ++it)
5454
{
5555
auto comp = Composition<CircuitT, OtherTransformersT...>();

src/simplification/reduce_not_composition.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class ReduceNotComposition_ : public ITransformer<CircuitT>
3939
*/
4040
CircuitAndEncoder<CircuitT, std::string> transform(
4141
std::unique_ptr<CircuitT> circuit,
42-
std::unique_ptr<GateEncoder<std::string>> encoder)
42+
std::unique_ptr<GateEncoder> encoder)
4343
{
4444
logger.debug("=========================================================================================");
4545
logger.debug("START ReduceNotComposition");
@@ -72,8 +72,7 @@ class ReduceNotComposition_ : public ITransformer<CircuitT>
7272
logger.debug("=========================================================================================");
7373

7474
return {
75-
std::make_unique<CircuitT>(gate_info, circuit->getOutputGates()),
76-
std::make_unique<GateEncoder<std::string>>(*encoder)};
75+
std::make_unique<CircuitT>(gate_info, circuit->getOutputGates()), std::make_unique<GateEncoder>(*encoder)};
7776
};
7877

7978
private:

0 commit comments

Comments
 (0)