slice.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "slice.hpp"
27 #include "casadi_misc.hpp"
28 #include "serializing_stream.hpp"
29 
30 namespace casadi {
31 
32  Slice::Slice() : start(0), stop(std::numeric_limits<casadi_int>::max()), step(1) {
33  }
34 
35  Slice::Slice(casadi_int i, bool ind1) : start(i-ind1), stop(i-ind1+1), step(1) {
36  casadi_assert(!(ind1 && i<=0),
37  "Matlab is 1-based, but requested index " +
38  str(i) + ". Note that negative slices are"
39  " disabled in the Matlab interface. "
40  "Possibly you may want to use 'end'.");
41  if (i==-1) stop = std::numeric_limits<casadi_int>::max();
42  }
43 
44  Slice::Slice(casadi_int start, casadi_int stop, casadi_int step) :
45  start(start), stop(stop), step(step) { }
46 
47  Slice::Slice(int start, int stop, int step) : start(start), stop(stop), step(step) {
48  }
49  Slice::Slice(int start, casadi_int stop, int step) : start(start), stop(stop), step(step) {
50  }
51  Slice::Slice(casadi_int start, int stop, int step) : start(start), stop(stop), step(step) {
52  }
53 
54  Slice Slice::operator-(casadi_int i) const {
55  return Slice(start==std::numeric_limits<casadi_int>::min() ? start : start-i,
56  stop==std::numeric_limits<casadi_int>::max() ? stop : stop-i,
57  step);
58  }
59 
60  Slice Slice::operator*(casadi_int i) const {
61  return Slice(start==std::numeric_limits<casadi_int>::min() ? start : start*i,
62  stop==std::numeric_limits<casadi_int>::max() ? stop : stop*i,
63  step*i);
64  }
65 
66  Slice Slice::apply(casadi_int len, bool ind1) const {
67  casadi_int start = this->start;
68  if (start==std::numeric_limits<casadi_int>::min()) {
69  start = (step < 0) ? len - 1 : 0;
70  } else if (start<0) {
71  start+=len;
72  }
73  casadi_int stop = this->stop;
74  if (stop==std::numeric_limits<casadi_int>::max()) {
75  stop = (step < 0) ? -1 : len;
76  } else if (stop<0) {
77  stop+=len;
78  }
79 
80  casadi_assert(stop<=len,
81  "Slice (start=" + str(start) + ", stop=" + str(stop) + ", step=" + str(step)
82  + ") out of bounds with supplied length of " + str(len));
83  casadi_assert(start>=0,
84  "Slice (start=" + str(start) + ", stop=" + str(stop) + ", step=" + str(step)
85  + ") out of bounds with start<0.");
86 
87  return Slice(start+ind1, stop+ind1, step);
88  }
89 
90  std::vector<casadi_int> Slice::all() const {
91  casadi_assert(start!=std::numeric_limits<casadi_int>::min(), "Need a length");
92  casadi_assert(stop!=std::numeric_limits<casadi_int>::max(), "Need a length");
93 
94  if ((stop>=start && step<0) ||
95  (stop<=start && step>0)) return std::vector<casadi_int>();
96 
97  return range(start, stop, step);
98  }
99 
100  std::vector<casadi_int> Slice::all(casadi_int len, bool ind1) const {
101  return apply(len, ind1).all();
102  }
103 
104  size_t Slice::size() const {
105  casadi_assert(start!=std::numeric_limits<casadi_int>::min() &&
106  stop!=std::numeric_limits<casadi_int>::max(),
107  "Cannot determine numel of slice.");
108  return all(std::numeric_limits<casadi_int>::max()).size();
109  }
110 
111  bool Slice::is_empty() const {
112  return size()==0;
113  }
114 
115  void Slice::disp(std::ostream& stream, bool more) const {
116  bool from_beginning = start == 0;
117  bool till_end = stop == std::numeric_limits<casadi_int>::max();
118  bool skip_none = step==1;
119  if (stop==start+1) {
120  stream << start;
121  } else {
122  if (!from_beginning) stream << start;
123  stream << ":";
124  if (!till_end) stream << stop;
125  if (!skip_none) stream << ":" << step;
126  }
127  }
128 
129  std::vector<casadi_int> Slice::all(const Slice& outer, casadi_int len) const {
130  std::vector<casadi_int> ret;
131  for (casadi_int i=outer.start; i!=outer.stop; i+=outer.step) {
132  for (casadi_int j=i+start; j!=i+stop; j+=step) {
133  ret.push_back(j);
134  }
135  }
136  return ret;
137  }
138 
139  bool Slice::is_scalar(casadi_int len) const {
140  casadi_int start = std::min(this->start, len);
141  casadi_int stop = std::min(this->stop, len);
142  casadi_int nret = (stop-start)/step + ((stop-start)%step!=0);
143  return nret==1;
144  }
145 
146  casadi_int Slice::scalar(casadi_int len) const {
147  casadi_assert_dev(is_scalar(len));
148  casadi_assert(start >= -len && start < len, "Slice::getScalar: out of bounds");
149  return start >= 0 ? start : start+len;
150  }
151 
152  Slice CASADI_EXPORT to_slice(const std::vector<casadi_int>& v, bool ind1) {
153  Slice r;
154  casadi_assert(is_slice(v, ind1), "Cannot be represented as a Slice");
155  if (v.empty()) {
156  r.start=r.stop=0;
157  r.step = 1;
158  } else if (v.size()==1) {
159  r.start = v.front()-ind1;
160  r.stop = r.start + 1;
161  r.step = 1;
162  } else {
163  r.start = v[0]-ind1;
164  r.step = v[1]-v[0];
165  r.stop = r.start + r.step*v.size();
166  }
167  return r;
168  }
169 
170  bool CASADI_EXPORT is_slice(const std::vector<casadi_int>& v, bool ind1) {
171  // Always false if negative numbers or non-increasing
172  casadi_int last_v = -1;
173  for (casadi_int i=0; i<v.size(); ++i) {
174  casadi_assert(!(ind1 && v[i]<=0),
175  "Matlab is 1-based, but requested index " + str(v[i]) + ". "
176  "Note that negative slices are disabled in the Matlab interface. "
177  "Possibly you may want to use 'end'.");
178  if (v[i]-ind1<=last_v) return false;
179  last_v = v[i]-ind1;
180  }
181 
182  // Always true if less than 2 elements
183  if (v.size()<2) return true;
184 
185  // If two elements, true if they are different
186  if (v.size()==2) return v[0]!=v[1];
187 
188  // We can now get the beginning, end and step
189  casadi_int start = v[0]-ind1;
190  casadi_int step = v[1]-v[0];
191  //casadi_int stop = start + step*v.size();
192 
193  // Consistency check
194  for (casadi_int i=2; i<v.size(); ++i) {
195  if (v[i]-ind1!=start+i*step) return false;
196  }
197 
198  // True if reached this point
199  return true;
200  }
201 
202  bool CASADI_EXPORT is_slice2(const std::vector<casadi_int>& v) {
203  // Always true if 1D slice
204  if (is_slice(v)) return true;
205 
206  // Always false if negative numbers or non-increasing
207  casadi_int last_v = -1;
208  for (casadi_int i=0; i<v.size(); ++i) {
209  if (v[i]<=last_v) return false;
210  last_v = v[i];
211  }
212 
213  // Get the slices
214  casadi_int start_outer = 0;
215  casadi_int step_outer = -1;
216  casadi_int start_inner = v.front();
217  casadi_int step_inner = v[1]-v[0];
218  casadi_int stop_inner = -1;
219  for (casadi_int i=2; i<v.size(); ++i) {
220  casadi_int predicted_v = start_inner+i*step_inner;
221  if (v[i]!=predicted_v) {
222  stop_inner = predicted_v;
223  step_outer = v[i] - start_inner;
224  break;
225  }
226  }
227  casadi_assert_dev(stop_inner>=0);
228 
229  // Get the end of the outer slice
230  // Calculate based on actual number of groups in the data
231  casadi_int inner_size = (stop_inner - start_inner) / step_inner;
232  casadi_int num_groups = v.size() / inner_size;
233  casadi_int stop_outer = start_outer + num_groups * step_outer;
234 
235  // Check consistency
236  std::vector<casadi_int>::const_iterator it=v.begin();
237  for (casadi_int i=start_outer; i!=stop_outer; i+=step_outer) {
238  for (casadi_int j=i+start_inner; j!=i+stop_inner; j+=step_inner) {
239  // False if we've reached the end
240  if (it==v.end()) return false;
241 
242  // Check if value matches
243  if (*it++ != j) return false;
244  }
245  }
246 
247  // False if there are still elements not accounted for
248  if (it!=v.end()) return false; // NOLINT
249 
250  // True if reached this point
251  return true;
252  }
253 
254  std::pair<Slice, Slice> CASADI_EXPORT to_slice2(const std::vector<casadi_int>& v) {
255  casadi_assert(is_slice2(v), "Cannot be represented as a nested Slice");
256  Slice inner, outer;
257 
258  // If simple slice
259  if (is_slice(v)) {
260  inner = to_slice(v);
261  outer.start = 0;
262  outer.step = outer.stop = inner.stop;
263  return std::make_pair(inner, outer);
264  }
265 
266  // Get the slices
267  outer.start = 0;
268  outer.step = -1;
269  inner.start = v.front();
270  inner.step = v[1]-v[0];
271  inner.stop = -1;
272  for (casadi_int i=2; i<v.size(); ++i) {
273  casadi_int predicted_v = inner.start+i*inner.step;
274  if (v[i]!=predicted_v) {
275  inner.stop = predicted_v;
276  outer.step = v[i] - inner.start;
277  break;
278  }
279  }
280 
281  // Get the end of the outer slice
282  // Calculate based on actual number of groups in the data
283  casadi_int inner_size = (inner.stop - inner.start) / inner.step;
284  casadi_int num_groups = v.size() / inner_size;
285  outer.stop = outer.start + num_groups * outer.step;
286  return std::make_pair(inner, outer);
287  }
288 
289 
291  s.pack("Slice::start", start);
292  s.pack("Slice::stop", stop);
293  s.pack("Slice::step", step);
294  }
295 
297  casadi_int start, stop, step;
298  s.unpack("Slice::start", start);
299  s.unpack("Slice::stop", stop);
300  s.unpack("Slice::step", step);
301  return Slice(start, stop, step);
302  }
303 
304 } // namespace casadi
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
Helper class for Serialization.
void pack(const Sparsity &e)
Serializes an object to the output stream.
Class representing a Slice.
Definition: slice.hpp:48
void serialize(SerializingStream &s) const
Serialize an object.
Definition: slice.cpp:290
Slice operator-(casadi_int i) const
Substract.
Definition: slice.cpp:54
size_t size() const
Get number of elements.
Definition: slice.cpp:104
casadi_int scalar(casadi_int len) const
Get scalar (if is_scalar)
Definition: slice.cpp:146
bool is_empty() const
Check if slice is empty.
Definition: slice.cpp:111
casadi_int step
Definition: slice.hpp:54
Slice apply(casadi_int len, bool ind1=false) const
Apply concrete length.
Definition: slice.cpp:66
casadi_int stop
stop value: use std::numeric_limits<casadi_int>::max() to indicate unboundedness
Definition: slice.hpp:53
Slice()
Default constructor - all elements.
Definition: slice.cpp:32
void disp(std::ostream &stream, bool more=false) const
Print a description of the object.
Definition: slice.cpp:115
static Slice deserialize(DeserializingStream &s)
Deserialize without type information.
Definition: slice.cpp:296
Slice operator*(casadi_int i) const
Definition: slice.cpp:60
std::vector< casadi_int > all() const
Get a vector of indices.
Definition: slice.cpp:90
bool is_scalar(casadi_int len) const
Is the slice a scalar.
Definition: slice.cpp:139
casadi_int start
start value: negative values will get added to length
Definition: slice.hpp:51
The casadi namespace.
Definition: archiver.cpp:28
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
bool CASADI_EXPORT is_slice(const IM &x, bool ind1=false)
Is the IM a Slice.
bool CASADI_EXPORT is_slice2(const std::vector< casadi_int > &v)
Check if an index vector can be represented more efficiently as two nested slices.
Definition: slice.cpp:202
std::string str(const T &v)
String representation, any type.
std::pair< Slice, Slice > CASADI_EXPORT to_slice2(const std::vector< casadi_int > &v)
Construct nested slices from an index vector (requires is_slice2(v) to be true)
Definition: slice.cpp:254
Slice CASADI_EXPORT to_slice(const IM &x, bool ind1=false)
Convert IM to Slice.
Definition: sx_elem.cpp:508