Skip to content

Commit 4e278e1

Browse files
authored
Fix bug in the order in contraction_cost, fix header bugs, and missing includes (#46)
This PR fixes a bug related to the order of arguments in the C++ `contraction_cost` function. This bug was subtle because it wasn't triggering any error and existing unit tests failed to catch it; indeed, for the implemented `SimpleCost`, the order of arguments in C++ does not actually matter. Specifically, `SimpleCost` only depends on the total number of indices, counted once. In principle, the total number of indices should be computed as `inds_in1 | inds_in2`. However, it was previously computed as `inds_in1 | inds_out`. Since `inds_out = (inds_in1 ^ inds_in2) | hyper_inds`, it is easy to show that `inds_in1 | inds_in2 == inds_in1 | inds_out` (or any other permutation). Other changes: - Rename `inds_A`, `inds_B`, and `inds_C` to `inds_out`, `inds_in1`, and `inds_in2` in both the C++ and Python cost models for better clarity and consistency. - Fix copy-paste errors in `isinf` and `signbit` pybind11 bindings for fixed_float, and correct the return type of `operator/=` to `FixedFloat &`. - Prevent potential division by zero and domain errors in the Metropolis-Hastings probability calculation (`mh.hpp`) and in the relative error validation blocks (`optimizer.hpp`). - Add missing `<stdexcept>`, `<sstream>`, and `<variant>` standard library includes across various headers (`assert.hpp`, `globals.hpp`, `node.hpp`, `tree.hpp`).
1 parent 56011bc commit 4e278e1

File tree

17 files changed

+132
-111
lines changed

17 files changed

+132
-111
lines changed

include/tnco/assert.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#pragma once
1616

1717
#include <sstream>
18+
#include <stdexcept>
1819

1920
namespace tnco {
2021

include/tnco/fixed_float.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ struct FixedFloat {
104104
return *this;
105105
}
106106

107-
auto operator/=(const FixedFloat &x) -> FixedFloat {
107+
auto operator/=(const FixedFloat &x) -> FixedFloat & {
108108
mpfr_div(_x, _x, x._x, rounding);
109109
return *this;
110110
}
@@ -575,9 +575,10 @@ void init(py::module &m, const std::string &name) {
575575
[](const self_type &self, const long double &other) -> auto {
576576
return self >= other;
577577
})
578-
.def("isinf", [](const self_type &self) -> auto { return isnan(self); })
578+
.def("isinf", [](const self_type &self) -> auto { return isinf(self); })
579579
.def("isnan", [](const self_type &self) -> auto { return isnan(self); })
580-
.def("signbit", [](const self_type &self) -> auto { return isnan(self); })
580+
.def("signbit",
581+
[](const self_type &self) -> auto { return signbit(self); })
581582
.def(py::pickle(
582583
[](const self_type &self) -> std::string { return dump(self); },
583584
[](const std::string &state) -> auto {

include/tnco/globals.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#pragma once
1616

1717
#include <random>
18+
#include <sstream>
1819

1920
#include "bitset.hpp"
2021
#include "ctree.hpp"

include/tnco/node.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <array>
2121
#include <cstdint>
22+
#include <variant>
2223

2324
namespace tnco::node {
2425

include/tnco/optimize/finite_width/cost_model/base.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ struct CostModel {
7373
return std::numeric_limits<width_type>::signaling_NaN();
7474
}
7575

76-
[[nodiscard]] virtual auto contraction_cost(const bitset_type &inds_A,
77-
const bitset_type &inds_B,
78-
const bitset_type &inds_C,
76+
[[nodiscard]] virtual auto contraction_cost(const bitset_type &inds_in1,
77+
const bitset_type &inds_in2,
78+
const bitset_type &inds_out,
7979
const dims_type &dims,
8080
const bitset_type &slices) const
8181
-> cost_type {
@@ -123,8 +123,8 @@ void init(py::module &m, const std::string &name) {
123123
.def_readonly("max_width", &self_type::max_width)
124124
.def("width", &self_type::width, "inds"_a, "dims"_a)
125125
.def("delta_width", &self_type::delta_width, "inds"_a, "dims"_a, "pos"_a)
126-
.def("contraction_cost", &self_type::contraction_cost, "inds_A"_a,
127-
"inds_B"_a, "inds_C"_a, "dims"_a, "slices"_a)
126+
.def("contraction_cost", &self_type::contraction_cost, "inds_in1"_a,
127+
"inds_in2"_a, "inds_out"_a, "dims"_a, "slices"_a)
128128
.def_property_readonly("width_type",
129129
[](const self_type &self) -> auto {
130130
return type_to_str<width_type>();

include/tnco/optimize/finite_width/cost_model/simple.hpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,23 +121,23 @@ struct CostModel : cost_model::base::CostModel<T...> {
121121
dims);
122122
}
123123

124-
[[nodiscard]] auto contraction_cost(const bitset_type &inds_A,
125-
const bitset_type &inds_B,
126-
const bitset_type &inds_C,
124+
[[nodiscard]] auto contraction_cost(const bitset_type &inds_in1,
125+
const bitset_type &inds_in2,
126+
const bitset_type &inds_out,
127127
const dims_type &dims,
128128
const bitset_type &slices) const
129129
#ifdef NDEBUG
130130
noexcept
131131
#endif
132132
-> cost_type override {
133-
ASSERT(std::size(inds_A) == std::size(inds_B) &&
134-
std::size(inds_A) == std::size(inds_C) &&
135-
std::size(inds_A) == std::size(slices),
133+
ASSERT(std::size(inds_in1) == std::size(inds_in2) &&
134+
std::size(inds_in1) == std::size(inds_out) &&
135+
std::size(inds_in1) == std::size(slices),
136136
"Indices and slices must have the same size.");
137-
ASSERT(inds_C.is_subset_of(inds_A | inds_B),
138-
"'inds_C' must be a subset of 'inds_A | inds_B'.");
137+
ASSERT(inds_out.is_subset_of(inds_in1 | inds_in2),
138+
"'inds_out' must be a subset of 'inds_in1 | inds_in2'.");
139139
return std::visit(
140-
[inds = inds_A | inds_B | slices](auto &&dims) -> auto {
140+
[inds = inds_in1 | inds_in2 | slices](auto &&dims) -> auto {
141141
return infinite_memory::cost_model::simple::get_cost<cost_type>(inds,
142142
dims);
143143
},

include/tnco/optimize/finite_width/cost_model/simple_sparse_inds.hpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,23 +133,23 @@ struct CostModel : simple::CostModel<T...> {
133133
dims);
134134
}
135135

136-
[[nodiscard]] auto contraction_cost(const bitset_type &inds_A,
137-
const bitset_type &inds_B,
138-
const bitset_type &inds_C,
136+
[[nodiscard]] auto contraction_cost(const bitset_type &inds_in1,
137+
const bitset_type &inds_in2,
138+
const bitset_type &inds_out,
139139
const dims_type &dims,
140140
const bitset_type &slices) const
141141
#ifdef NDEBUG
142142
noexcept
143143
#endif
144144
-> cost_type override {
145-
ASSERT(std::size(inds_A) == std::size(inds_B) &&
146-
std::size(inds_A) == std::size(inds_C) &&
147-
std::size(inds_A) == std::size(slices),
145+
ASSERT(std::size(inds_in1) == std::size(inds_in2) &&
146+
std::size(inds_in1) == std::size(inds_out) &&
147+
std::size(inds_in1) == std::size(slices),
148148
"Indices and slices must have the same size.");
149-
ASSERT(inds_C.is_subset_of(inds_A | inds_B),
150-
"'inds_C' must be a subset of 'inds_A | inds_B'.");
149+
ASSERT(inds_out.is_subset_of(inds_in1 | inds_in2),
150+
"'inds_out' must be a subset of 'inds_in1 | inds_in2'.");
151151
return std::visit(
152-
[inds = inds_A | inds_B | slices, &sparse_inds = this->sparse_inds,
152+
[inds = inds_in1 | inds_in2 | slices, &sparse_inds = this->sparse_inds,
153153
&n_projs = this->n_projs](auto &&dims) -> auto {
154154
return infinite_memory::cost_model::simple_sparse_inds::get_cost<
155155
cost_type>(inds, sparse_inds, n_projs, dims);

include/tnco/optimize/finite_width/greedy/optimizer.hpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,9 @@ struct Optimizer : optimize::optimizer::Optimizer {
188188
if (new_sliced_width_B <= p_cmodel->max_width) {
189189
// Get new costs for B and A
190190
const auto new_ccost_A =
191-
cmodel.contraction_cost(inds_A, new_inds_B, inds_E, dims, slices);
191+
cmodel.contraction_cost(new_inds_B, inds_E, inds_A, dims, slices);
192192
const auto new_ccost_B =
193-
cmodel.contraction_cost(new_inds_B, inds_D, inds_C, dims, slices);
193+
cmodel.contraction_cost(inds_D, inds_C, new_inds_B, dims, slices);
194194

195195
// Get the delta cost
196196
const auto delta_cost =
@@ -343,11 +343,16 @@ struct Optimizer : optimize::optimizer::Optimizer {
343343
}
344344

345345
// Check final cost
346-
if (const auto rel_error = abs((log(total_cost) - log(get_total_cost())) /
347-
log(get_total_cost()));
348-
rel_error > 1e-2) {
349-
throw std::logic_error("Total cost is not properly cached (rel. error: " +
350-
tnco::to_string(rel_error * 100) + "%)");
346+
if (const auto actual_total_cost = get_total_cost();
347+
actual_total_cost > 0) {
348+
if (const auto rel_error =
349+
abs((log(total_cost) - log(actual_total_cost)) /
350+
log(actual_total_cost));
351+
rel_error > 1e-2) {
352+
throw std::logic_error(
353+
"Total cost is not properly cached (rel. error: " +
354+
tnco::to_string(rel_error * 100) + "%)");
355+
}
351356
}
352357
#endif
353358

include/tnco/optimize/infinite_memory/cost_model/base.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ struct CostModel {
4545
auto operator=(CostModel &&) -> CostModel & = delete;
4646
virtual ~CostModel() = default;
4747

48-
[[nodiscard]] virtual auto contraction_cost(const bitset_type &inds_A,
49-
const bitset_type &inds_B,
50-
const bitset_type &inds_C,
48+
[[nodiscard]] virtual auto contraction_cost(const bitset_type &inds_in1,
49+
const bitset_type &inds_in2,
50+
const bitset_type &inds_out,
5151
const dims_type &dims) const
5252
-> cost_type {
5353
/*
@@ -87,8 +87,8 @@ void init(py::module &m, const std::string &name) {
8787
[](const self_type &self, const self_type &other) -> auto {
8888
return true;
8989
})
90-
.def("contraction_cost", &self_type::contraction_cost, "inds_A"_a,
91-
"inds_B"_a, "inds_C"_a, "dims"_a)
90+
.def("contraction_cost", &self_type::contraction_cost, "inds_in1"_a,
91+
"inds_in2"_a, "inds_out"_a, "dims"_a)
9292
.def_property_readonly("cost_type", [](const self_type &self) -> auto {
9393
return type_to_str<cost_type>();
9494
});

include/tnco/optimize/infinite_memory/cost_model/simple.hpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,21 @@ struct CostModel : cost_model::base::CostModel<T...> {
6262
using bitset_type = typename base_type::bitset_type;
6363
using dims_type = typename base_type::dims_type;
6464

65-
[[nodiscard]] auto contraction_cost(const bitset_type &inds_A,
66-
const bitset_type &inds_B,
67-
const bitset_type &inds_C,
65+
[[nodiscard]] auto contraction_cost(const bitset_type &inds_in1,
66+
const bitset_type &inds_in2,
67+
const bitset_type &inds_out,
6868
const dims_type &dims) const
6969
#ifdef NDEBUG
7070
noexcept
7171
#endif
7272
-> cost_type override {
73-
ASSERT(std::size(inds_A) == std::size(inds_B) &&
74-
std::size(inds_A) == std::size(inds_C),
73+
ASSERT(std::size(inds_in1) == std::size(inds_in2) &&
74+
std::size(inds_in1) == std::size(inds_out),
7575
"Indices must have the same size.");
76-
ASSERT(inds_C.is_subset_of(inds_A | inds_B),
77-
"'inds_C' must be a subset of 'inds_A | inds_B'.");
76+
ASSERT(inds_out.is_subset_of(inds_in1 | inds_in2),
77+
"'inds_out' must be a subset of 'inds_in1 | inds_in2'.");
7878
return std::visit(
79-
[inds = inds_A | inds_B](auto &&dims) -> auto {
79+
[inds = inds_in1 | inds_in2](auto &&dims) -> auto {
8080
return get_cost<cost_type>(inds, dims);
8181
},
8282
dims);

0 commit comments

Comments
 (0)