Skip to content

Commit 9542de2

Browse files
committed
Add O(1) shifting case to 'bdd_replace'
1 parent f0038a3 commit 9542de2

File tree

6 files changed

+3082
-1141
lines changed

6 files changed

+3082
-1141
lines changed

src/adiar/bdd/relprod.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ namespace adiar
307307
throw invalid_argument("Non-monotonic variable replacement not (yet) supported.");
308308

309309
case replace_type::Monotone:
310+
case replace_type::Shift:
310311
case replace_type::Identity:
311312
#ifdef ADIAR_STATS
312313
internal::stats_replace.monotonic_reduces += 1u;

src/adiar/internal/algorithms/replace.h

+43-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <adiar/internal/algorithms/reduce.h>
1212
#include <adiar/internal/assert.h>
13+
#include <adiar/internal/dd_func.h>
1314
#include <adiar/internal/io/levelized_file_stream.h>
1415
#include <adiar/internal/io/node_file.h>
1516
#include <adiar/internal/io/node_stream.h>
@@ -60,19 +61,24 @@ namespace adiar::internal
6061
replace_type
6162
__replace__infer_type(LevelInfoStream& ls, const ReplaceFunction& m)
6263
{
63-
using label_type = typename Policy::label_type;
64-
using result_type = typename ReplaceFunction::result_type;
64+
using label_type = typename Policy::label_type;
65+
using signed_label_type = typename Policy::signed_label_type;
66+
using result_type = typename ReplaceFunction::result_type;
6567

6668
constexpr bool is_total_map = is_same<result_type, label_type>;
6769
constexpr bool is_partial_map = is_same<result_type, optional<label_type>>;
6870

6971
static_assert(is_total_map || is_partial_map);
7072

7173
bool identity = true;
74+
bool shift = true;
7275
bool monotone = true;
7376

7477
label_type prev_before = Policy::max_label + 1;
7578
label_type prev_after = Policy::max_label + 1;
79+
80+
signed_label_type prev_diff = 0;
81+
7682
while (ls.can_pull()) {
7783
const label_type next_before = ls.pull().level();
7884
const result_type next_after_opt = m(next_before);
@@ -89,6 +95,14 @@ namespace adiar::internal
8995
next_after = next_after_opt;
9096
}
9197

98+
if (shift) {
99+
const signed_label_type next_diff =
100+
static_cast<signed_label_type>(next_before) - static_cast<signed_label_type>(next_after);
101+
102+
shift &= Policy::max_label < prev_before || prev_diff == next_diff;
103+
prev_diff = next_diff;
104+
}
105+
92106
identity &= next_before == next_after;
93107
monotone &= Policy::max_label < prev_before || prev_after < next_after;
94108

@@ -97,13 +111,32 @@ namespace adiar::internal
97111
}
98112

99113
if (!monotone) { return replace_type::Non_Monotone; }
100-
if (!identity) { return replace_type::Monotone; }
114+
if (!shift) { return replace_type::Monotone; }
115+
if (!identity) { return replace_type::Shift; }
101116
return replace_type::Identity;
102117
}
103118

104119
//////////////////////////////////////////////////////////////////////////////////////////////////
105120
// Algorithms
106121

122+
//////////////////////////////////////////////////////////////////////////////////////////////////
123+
/// \brief Replace the level in constant time
124+
///
125+
/// \remark This requires that the mapping, `m`, is *monotonic*.
126+
//////////////////////////////////////////////////////////////////////////////////////////////////
127+
template <typename Policy>
128+
inline typename Policy::dd_type
129+
__replace__shift_return(const typename Policy::dd_type& dd, const replace_func<Policy>& m)
130+
{
131+
adiar_assert(!dd->is_terminal());
132+
133+
const typename Policy::signed_label_type topvar = dd_topvar(dd);
134+
const typename Policy::signed_label_type shifted_topvar = m(topvar);
135+
136+
return typename Policy::dd_type(
137+
dd.file_ptr(), dd.is_negated(), dd.shift() + (shifted_topvar - topvar));
138+
}
139+
107140
//////////////////////////////////////////////////////////////////////////////////////////////////
108141
/// \brief Replace the level of all nodes in a single linear scan.
109142
///
@@ -211,6 +244,12 @@ namespace adiar::internal
211244
#endif
212245
return __replace__monotonic_scan<Policy>(dd, m);
213246

247+
case replace_type::Shift:
248+
#ifdef ADIAR_STATS
249+
stats_replace.shift_returns += 1u;
250+
#endif
251+
return __replace__shift_return<Policy>(dd, m);
252+
214253
case replace_type::Identity:
215254
#ifdef ADIAR_STATS
216255
stats_replace.identity_returns += 1u;
@@ -263,6 +302,7 @@ namespace adiar::internal
263302
throw invalid_argument("Non-monotonic variable replacement not (yet) supported.");
264303

265304
case replace_type::Monotone:
305+
case replace_type::Shift:
266306
#ifdef ADIAR_STATS
267307
stats_replace.monotonic_reduces += 1u;
268308
#endif

src/adiar/statistics.h

+6
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,12 @@ namespace adiar
546546
////////////////////////////////////////////////////////////////////////////////////////////
547547
uintwide identity_reduces = 0;
548548

549+
////////////////////////////////////////////////////////////////////////////////////////////
550+
/// \brief Number of runs using 0 I/Os due to the variables are replaced according to a
551+
/// simple linear shift.
552+
////////////////////////////////////////////////////////////////////////////////////////////
553+
uintwide shift_returns = 0;
554+
549555
////////////////////////////////////////////////////////////////////////////////////////////
550556
/// \brief Number of runs using a 2N/B linear copy-paste.
551557
////////////////////////////////////////////////////////////////////////////////////////////

src/adiar/types.h

+4-5
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,11 @@ namespace adiar
3636
/** For any `x < y` then the mapped values preserve that order, i.e. `m(x) < m(y)`. */
3737
Monotone = 2,
3838

39-
/* TODO (constant time variable replacement): `m(x)` is of the form `ax + b` for a positive
40-
fraction `a` and integer `b`. */
41-
// Affine = 1,
39+
/* TODO (faster version of 'Monotone'): `m(x) = ax + b` for some integers `a` and `b`. */
40+
// Affine = 1,
4241

43-
/* TODO (faster version of 'Affine'): `m(x)` is of the form `x + b` for an integer `b`. */
44-
// Shift = 0,
42+
/** (faster version of 'Monotone'): `m(x) = x + b` for an integer `b`. */
43+
Shift = 0,
4544

4645
/** Nothing needs to be done, as `m(x) = x`. */
4746
Identity = -1

0 commit comments

Comments
 (0)