Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 108 additions & 33 deletions dwave/optimization/include/dwave-optimization/nodes/numbers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,34 @@ namespace dwave::optimization {
/// A contiguous block of numbers.
class NumberNode : public ArrayOutputMixin<ArrayNode>, public DecisionNode {
public:
/// Allowable axis-wise bound operators.
enum BoundAxisOperator { Equal, LessEqual, GreaterEqual };

/// Struct for stateless axis-wise bound information. Given an `axis`, define
/// constraints on the sum of the values in each slice along `axis`.
/// Constraints can be defined for ALL slices along `axis` or PER slice along
/// `axis`. Allowable operators are defined by `BoundAxisOperator`.
struct BoundAxisInfo {
/// To reduce the # of `IntegerNode` and `BinaryNode` constructors, we
/// allow only one constructor.
BoundAxisInfo(ssize_t axis, std::vector<BoundAxisOperator> axis_operators,
std::vector<double> axis_bounds);
/// The bound axis
const ssize_t axis;
/// Operator for ALL axis slices (vector has length one) or operator*s* PER
/// slice (length of vector is equal to the number of slices).
const std::vector<BoundAxisOperator> operators;
/// Bound for ALL axis slices (vector has length one) or bound*s* PER slice
/// (length of vector is equal to the number of slices).
const std::vector<double> bounds;

/// Obtain the bound associated with a given slice along `axis`.
double get_bound(const ssize_t slice) const;

/// Obtain the operator associated with a given slice along `axis`.
BoundAxisOperator get_operator(const ssize_t slice) const;
};

NumberNode() = delete;

// Overloads needed by the Array ABC **************************************
Expand Down Expand Up @@ -68,6 +96,12 @@ class NumberNode : public ArrayOutputMixin<ArrayNode>, public DecisionNode {
// Initialize the state of the node randomly
template <std::uniform_random_bit_generator Generator>
void initialize_state(State& state, Generator& rng) const {
// Currently, we do not support random node Initialization with
// axis wise bounds.
if (bound_axes_info_.size() > 0) {
throw std::invalid_argument("Cannot randomly initialize_state with bound axes");
}

std::vector<double> values;
const ssize_t size = this->size();
values.reserve(size);
Expand Down Expand Up @@ -106,21 +140,38 @@ class NumberNode : public ArrayOutputMixin<ArrayNode>, public DecisionNode {
// in a given index.
void clip_and_set_value(State& state, ssize_t index, double value) const;

/// Return vector of axis-wise bounds.
const std::vector<BoundAxisInfo>& axis_wise_bounds() const;

/// Return vector containing the bound axis sums in a given state.
const std::vector<std::vector<double>>& bound_axis_sums(State& state) const;

protected:
explicit NumberNode(std::span<const ssize_t> shape, std::vector<double> lower_bound,
std::vector<double> upper_bound);
std::vector<double> upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);

// Return truth statement: 'value is valid in a given index'.
/// Return truth statement: 'value is valid in a given index'.
virtual bool is_valid(ssize_t index, double value) const = 0;

// Default value in a given index.
/// Default value in a given index.
virtual double default_value(ssize_t index) const = 0;

/// Update the running bound axis sums where the value stored at `index` is
/// changed by `value_change` in a given state.
void update_bound_axis_slice_sums(State& state, const ssize_t index,
const double value_change) const;

/// Statelss global minimum and maximum of the values stored in NumberNode.
double min_;
double max_;

/// Stateless index-wise upper and lower bounds.
std::vector<double> lower_bounds_;
std::vector<double> upper_bounds_;

/// Stateless information on each bound axis.
const std::vector<BoundAxisInfo> bound_axes_info_;
};

/// A contiguous block of integer numbers.
Expand All @@ -134,33 +185,45 @@ class IntegerNode : public NumberNode {
// Default to a single scalar integer with default bounds
IntegerNode() : IntegerNode({}) {}

// Create an integer array with the user-defined bounds.
// Defaulting to the specified default bounds.
// Create an integer array with the user-defined index- and axis-wise bounds.
// Index-wise bounds default to the specified default bounds.
IntegerNode(std::span<const ssize_t> shape,
std::optional<std::vector<double>> lower_bound = std::nullopt,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
IntegerNode(std::initializer_list<ssize_t> shape,
std::optional<std::vector<double>> lower_bound = std::nullopt,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
IntegerNode(ssize_t size, std::optional<std::vector<double>> lower_bound = std::nullopt,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);

IntegerNode(std::span<const ssize_t> shape, double lower_bound,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
IntegerNode(std::initializer_list<ssize_t> shape, double lower_bound,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
IntegerNode(ssize_t size, double lower_bound,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);

IntegerNode(std::span<const ssize_t> shape, std::optional<std::vector<double>> lower_bound,
double upper_bound);
double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
IntegerNode(std::initializer_list<ssize_t> shape,
std::optional<std::vector<double>> lower_bound, double upper_bound);
IntegerNode(ssize_t size, std::optional<std::vector<double>> lower_bound, double upper_bound);

IntegerNode(std::span<const ssize_t> shape, double lower_bound, double upper_bound);
IntegerNode(std::initializer_list<ssize_t> shape, double lower_bound, double upper_bound);
IntegerNode(ssize_t size, double lower_bound, double upper_bound);
std::optional<std::vector<double>> lower_bound, double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
IntegerNode(ssize_t size, std::optional<std::vector<double>> lower_bound, double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);

IntegerNode(std::span<const ssize_t> shape, double lower_bound, double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
IntegerNode(std::initializer_list<ssize_t> shape, double lower_bound, double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
IntegerNode(ssize_t size, double lower_bound, double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);

// Overloads needed by the Node ABC ***************************************

Expand Down Expand Up @@ -190,33 +253,45 @@ class BinaryNode : public IntegerNode {
/// A binary scalar variable with lower_bound = 0.0 and upper_bound = 1.0
BinaryNode() : BinaryNode({}) {}

// Create a binary array with the user-defined bounds.
// Defaulting to lower_bound = 0.0 and upper_bound = 1.0
// Create a binary array with the user-defined index- and axis-wise bounds.
// Index-wise bounds default to lower_bound = 0.0 and upper_bound = 1.0.
BinaryNode(std::span<const ssize_t> shape,
std::optional<std::vector<double>> lower_bound = std::nullopt,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
BinaryNode(std::initializer_list<ssize_t> shape,
std::optional<std::vector<double>> lower_bound = std::nullopt,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
BinaryNode(ssize_t size, std::optional<std::vector<double>> lower_bound = std::nullopt,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);

BinaryNode(std::span<const ssize_t> shape, double lower_bound,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
BinaryNode(std::initializer_list<ssize_t> shape, double lower_bound,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
BinaryNode(ssize_t size, double lower_bound,
std::optional<std::vector<double>> upper_bound = std::nullopt);
std::optional<std::vector<double>> upper_bound = std::nullopt,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);

BinaryNode(std::span<const ssize_t> shape, std::optional<std::vector<double>> lower_bound,
double upper_bound);
double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
BinaryNode(std::initializer_list<ssize_t> shape, std::optional<std::vector<double>> lower_bound,
double upper_bound);
BinaryNode(ssize_t size, std::optional<std::vector<double>> lower_bound, double upper_bound);

BinaryNode(std::span<const ssize_t> shape, double lower_bound, double upper_bound);
BinaryNode(std::initializer_list<ssize_t> shape, double lower_bound, double upper_bound);
BinaryNode(ssize_t size, double lower_bound, double upper_bound);
double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
BinaryNode(ssize_t size, std::optional<std::vector<double>> lower_bound, double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);

BinaryNode(std::span<const ssize_t> shape, double lower_bound, double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
BinaryNode(std::initializer_list<ssize_t> shape, double lower_bound, double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);
BinaryNode(ssize_t size, double lower_bound, double upper_bound,
std::optional<std::vector<BoundAxisInfo>> bound_axes = std::nullopt);

// Flip the value (0 -> 1 or 1 -> 0) at index i in the given state.
void flip(State& state, ssize_t i) const;
Expand Down
Loading