Coverage for install/scipp/core/variable.py: 65%

93 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-04-28 01:28 +0000

1# SPDX-License-Identifier: BSD-3-Clause 

2# Copyright (c) 2023 Scipp contributors (https://github.com/scipp) 

3# @author Matthew Andrew 

4# ruff: noqa: E501 

5 

6from __future__ import annotations 

7 

8import warnings 

9from contextlib import contextmanager 

10from typing import Any, Iterable, Optional, Sequence, TypeVar, Union 

11 

12import numpy as _np 

13from numpy.typing import ArrayLike 

14 

15from .._scipp import core as _cpp 

16from ..typing import DTypeLike 

17from ..units import default_unit 

18from ._sizes import _parse_dims_shape_sizes 

19from .cpp_classes import DType, Unit, Variable 

20 

21NumberOrVar = TypeVar('NumberOrVar', Union[int, float], Variable) 

22 

23 

24def scalar( 

25 value: Any, 

26 *, 

27 variance: Any = None, 

28 unit: Union[Unit, str, None] = default_unit, 

29 dtype: Optional[DTypeLike] = None, 

30) -> Variable: 

31 """Constructs a zero dimensional :class:`Variable` with a unit and optional 

32 variance. 

33 

34 Parameters 

35 ---------- 

36 value: 

37 Initial value. 

38 variance: 

39 Optional, initial variance. 

40 unit: 

41 Optional, unit. 

42 dtype: scipp.typing.DTypeLike 

43 Optional, type of underlying data. By default, 

44 the dtype is inferred from the `value` argument. 

45 Cannot be specified for value types of 

46 Dataset or DataArray. 

47 

48 Returns 

49 ------- 

50 : 

51 A scalar (zero-dimensional) Variable. 

52 

53 See Also 

54 -------- 

55 scipp.array, scipp.empty, scipp.index, scipp.ones, scipp.zeros 

56 

57 Examples 

58 -------- 

59 With deduced dtype and default unit: 

60 

61 >>> sc.scalar(3.14) 

62 <scipp.Variable> () float64 [dimensionless] 3.14 

63 

64 >>> sc.scalar('a string') 

65 <scipp.Variable> () string <no unit> "a string" 

66 

67 Or specifying a unit and dtype: 

68 

69 >>> sc.scalar(3.14, unit='m', dtype=int) 

70 <scipp.Variable> () int64 [m] 3 

71 

72 Calling ``scalar`` with a list (or similar array-like object) will store that 

73 object in a scalar variable and *not* create an array variable: 

74 

75 >>> sc.scalar([1, 2, 3]) 

76 <scipp.Variable> () PyObject <no unit> [1, 2, 3] 

77 """ 

78 return _cpp.Variable( 

79 dims=(), values=value, variances=variance, unit=unit, dtype=dtype 

80 ) 

81 

82 

83def index(value: Any, *, dtype: Optional[DTypeLike] = None) -> Variable: 

84 """Constructs a zero dimensional :class:`Variable` representing an index. 

85 

86 This is equivalent to calling :py:func:`scipp.scalar` with unit=None. 

87 

88 Parameters 

89 ---------- 

90 value: 

91 Initial value. 

92 dtype: scipp.typing.DTypeLike 

93 Optional, type of underlying data. By default, 

94 the dtype is inferred from the `value` argument. 

95 

96 Returns 

97 ------- 

98 : 

99 A scalar (zero-dimensional) variable without unit. 

100 

101 See Also 

102 -------- 

103 scipp.scalar, scipp.array 

104 

105 Examples 

106 -------- 

107 

108 >>> sc.index(123) 

109 <scipp.Variable> () int64 <no unit> 123 

110 """ 

111 return scalar(value=value, dtype=dtype, unit=None) 

112 

113 

114def zeros( 

115 *, 

116 dims: Optional[Sequence[str]] = None, 

117 shape: Optional[Sequence[int]] = None, 

118 sizes: Optional[dict[str, int]] = None, 

119 unit: Union[Unit, str, None] = default_unit, 

120 dtype: DTypeLike = DType.float64, 

121 with_variances: bool = False, 

122) -> Variable: 

123 """Constructs a :class:`Variable` with default initialized values with 

124 given dimension labels and shape. 

125 

126 The dims and shape can also be specified using a `sizes` dict. 

127 Optionally can add default initialized variances. 

128 Only keyword arguments accepted. 

129 

130 Parameters 

131 ---------- 

132 dims: 

133 Optional (if sizes is specified), dimension labels. 

134 shape: 

135 Optional (if sizes is specified), dimension sizes. 

136 sizes: 

137 Optional, dimension label to size map. 

138 unit: 

139 Unit of contents. 

140 dtype: scipp.typing.DTypeLike 

141 Type of underlying data. 

142 with_variances: 

143 If True includes variances initialized to 0. 

144 

145 Returns 

146 ------- 

147 : 

148 A variable filled with 0's. 

149 

150 See Also 

151 -------- 

152 scipp.array, scipp.empty, scipp.ones, scipp.scalar, scipp.zeros_like 

153 

154 Examples 

155 -------- 

156 

157 >>> sc.zeros(dims=['x'], shape=[4]) 

158 <scipp.Variable> (x: 4) float64 [dimensionless] [0, 0, 0, 0] 

159 

160 >>> sc.zeros(sizes={'x': 4}) 

161 <scipp.Variable> (x: 4) float64 [dimensionless] [0, 0, 0, 0] 

162 

163 >>> sc.zeros(sizes={'y': 3}, with_variances=True) 

164 <scipp.Variable> (y: 3) float64 [dimensionless] [0, 0, 0] [0, 0, 0] 

165 

166 >>> sc.zeros(sizes={'z': 3}, unit='kg', dtype=int) 

167 <scipp.Variable> (z: 3) int64 [kg] [0, 0, 0] 

168 """ 

169 

170 return _cpp.zeros( 

171 **_parse_dims_shape_sizes(dims, shape, sizes), 

172 unit=unit, 

173 dtype=dtype, 

174 with_variances=with_variances, 

175 ) 

176 

177 

178def ones( 

179 *, 

180 dims: Optional[Sequence[str]] = None, 

181 shape: Optional[Sequence[int]] = None, 

182 sizes: Optional[dict[str, int]] = None, 

183 unit: Union[Unit, str, None] = default_unit, 

184 dtype: DTypeLike = DType.float64, 

185 with_variances: bool = False, 

186) -> Variable: 

187 """Constructs a :class:`Variable` with values initialized to 1 with 

188 given dimension labels and shape. 

189 

190 The dims and shape can also be specified using a `sizes` dict. 

191 

192 Parameters 

193 ---------- 

194 dims: 

195 Optional (if sizes is specified), dimension labels. 

196 shape: 

197 Optional (if sizes is specified), dimension sizes. 

198 sizes: 

199 Optional, dimension label to size map. 

200 unit: 

201 Unit of contents. 

202 dtype: scipp.typing.DTypeLike 

203 Type of underlying data. 

204 with_variances: 

205 If True includes variances initialized to 1. 

206 

207 Returns 

208 ------- 

209 : 

210 A variable filled with 1's. 

211 

212 See Also 

213 -------- 

214 scipp.array, scipp.empty, scipp.ones_like, scipp.scalar, scipp.zeros 

215 

216 Examples 

217 -------- 

218 

219 >>> sc.ones(dims=['x'], shape=[4]) 

220 <scipp.Variable> (x: 4) float64 [dimensionless] [1, 1, 1, 1] 

221 

222 >>> sc.ones(sizes={'x': 4}) 

223 <scipp.Variable> (x: 4) float64 [dimensionless] [1, 1, 1, 1] 

224 

225 >>> sc.ones(sizes={'y': 3}, with_variances=True) 

226 <scipp.Variable> (y: 3) float64 [dimensionless] [1, 1, 1] [1, 1, 1] 

227 

228 >>> sc.ones(sizes={'z': 3}, unit='kg', dtype=int) 

229 <scipp.Variable> (z: 3) int64 [kg] [1, 1, 1] 

230 """ 

231 return _cpp.ones( 

232 **_parse_dims_shape_sizes(dims, shape, sizes), 

233 unit=unit, 

234 dtype=dtype, 

235 with_variances=with_variances, 

236 ) 

237 

238 

239def empty( 

240 *, 

241 dims: Optional[Sequence[str]] = None, 

242 shape: Optional[Sequence[int]] = None, 

243 sizes: Optional[dict[str, int]] = None, 

244 unit: Union[Unit, str, None] = default_unit, 

245 dtype: DTypeLike = DType.float64, 

246 with_variances: bool = False, 

247 aligned: bool = True, 

248) -> Variable: 

249 """Constructs a :class:`Variable` with uninitialized values with given 

250 dimension labels and shape. 

251 

252 The dims and shape can also be specified using a `sizes` dict. 

253 

254 Warning 

255 ------- 

256 'Uninitialized' means that values have undetermined values. 

257 Reading elements before writing to them is undefined behavior. 

258 Consider using :py:func:`scipp.zeros` unless you 

259 know what you are doing and require maximum performance. 

260 

261 Parameters 

262 ---------- 

263 dims: 

264 Optional (if sizes is specified), dimension labels. 

265 shape: 

266 Optional (if sizes is specified), dimension sizes. 

267 sizes: 

268 Optional, dimension label to size map. 

269 unit: 

270 Unit of contents. 

271 dtype: scipp.typing.DTypeLike 

272 Type of underlying data. 

273 with_variances: 

274 If True includes uninitialized variances. 

275 aligned: 

276 Initial value for the alignment flag. 

277 

278 Returns 

279 ------- 

280 : 

281 A variable with uninitialized elements. 

282 

283 See Also 

284 -------- 

285 scipp.array, scipp.empty_like, scipp.ones, scipp.scalar, scipp.zeros 

286 

287 Examples 

288 -------- 

289 

290 >>> var = sc.empty(dims=['x'], shape=[4]) 

291 >>> var[:] = sc.scalar(2.0) # initialize values before printing 

292 >>> var 

293 <scipp.Variable> (x: 4) float64 [dimensionless] [2, 2, 2, 2] 

294 """ 

295 return _cpp.empty( 

296 **_parse_dims_shape_sizes(dims, shape, sizes), 

297 unit=unit, 

298 dtype=dtype, 

299 with_variances=with_variances, 

300 aligned=aligned, 

301 ) 

302 

303 

304def full( 

305 *, 

306 value: Any, 

307 variance: Any = None, 

308 dims: Optional[Sequence[str]] = None, 

309 shape: Optional[Sequence[int]] = None, 

310 sizes: Optional[dict[str, int]] = None, 

311 unit: Union[Unit, str, None] = default_unit, 

312 dtype: Optional[DTypeLike] = None, 

313) -> Variable: 

314 """Constructs a :class:`Variable` with values initialized to the specified 

315 value with given dimension labels and shape. 

316 

317 The dims and shape can also be specified using a `sizes` dict. 

318 

319 Parameters 

320 ---------- 

321 value: 

322 The value to fill the Variable with. 

323 variance: 

324 The variance to fill the Variable with. 

325 dims: 

326 Optional (if sizes is specified), dimension labels. 

327 shape: 

328 Optional (if sizes is specified), dimension sizes. 

329 sizes: 

330 Optional, dimension label to size map. 

331 unit: 

332 Unit of contents. 

333 dtype: scipp.typing.DTypeLike 

334 Type of underlying data. 

335 

336 Returns 

337 ------- 

338 : 

339 A variable filled with given value. 

340 

341 See Also 

342 -------- 

343 scipp.empty, scipp.full_like, scipp.ones, scipp.zeros 

344 

345 Examples 

346 -------- 

347 

348 >>> sc.full(value=2, dims=['x'], shape=[2]) 

349 <scipp.Variable> (x: 2) int64 [dimensionless] [2, 2] 

350 

351 >>> sc.full(value=2, sizes={'x': 2}) 

352 <scipp.Variable> (x: 2) int64 [dimensionless] [2, 2] 

353 

354 >>> sc.full(value=5, sizes={'y': 3, 'x': 2}) 

355 <scipp.Variable> (y: 3, x: 2) int64 [dimensionless] [5, 5, ..., 5, 5] 

356 

357 >>> sc.full(value=2.0, variance=0.1, sizes={'x': 3}, unit='s') 

358 <scipp.Variable> (x: 3) float64 [s] [2, 2, 2] [0.1, 0.1, 0.1] 

359 """ 

360 return ( 

361 scalar(value=value, variance=variance, unit=unit, dtype=dtype) 

362 .broadcast(**_parse_dims_shape_sizes(dims, shape, sizes)) 

363 .copy() 

364 ) 

365 

366 

367def vector( 

368 value: Union[_np.ndarray, list], *, unit: Union[Unit, str, None] = default_unit 

369) -> Variable: 

370 """Constructs a zero dimensional :class:`Variable` holding a single length-3 

371 vector. 

372 

373 Parameters 

374 ---------- 

375 value: 

376 Initial value, a list or 1-D numpy array. 

377 unit: 

378 Unit of contents. 

379 

380 Returns 

381 ------- 

382 : 

383 A scalar (zero-dimensional) variable of a vector. 

384 

385 See Also 

386 -------- 

387 scipp.vectors: 

388 Construct an array of vectors. 

389 scipp.spatial.as_vectors: 

390 Construct vectors from Scipp Variables 

391 

392 Examples 

393 -------- 

394 

395 >>> sc.vector(value=[1, 2, 3]) 

396 <scipp.Variable> () vector3 [dimensionless] (1, 2, 3) 

397 

398 >>> sc.vector(value=[4, 5, 6], unit='m') 

399 <scipp.Variable> () vector3 [m] (4, 5, 6) 

400 """ 

401 return vectors(dims=(), values=value, unit=unit) 

402 

403 

404def vectors( 

405 *, 

406 dims: Sequence[str], 

407 values: Union[_np.ndarray, list], 

408 unit: Union[Unit, str, None] = default_unit, 

409) -> Variable: 

410 """Constructs a :class:`Variable` with given dimensions holding an array 

411 of length-3 vectors. 

412 

413 Parameters 

414 ---------- 

415 dims: 

416 Dimension labels. 

417 values: 

418 Initial values. 

419 unit: 

420 Unit of contents. 

421 

422 Returns 

423 ------- 

424 : 

425 A variable of vectors with given values. 

426 

427 See Also 

428 -------- 

429 scipp.vector: 

430 Construct a single vector. 

431 scipp.spatial.as_vectors: 

432 Construct vectors from Scipp Variables 

433 

434 Examples 

435 -------- 

436 

437 >>> sc.vectors(dims=['x'], values=[[1, 2, 3]]) 

438 <scipp.Variable> (x: 1) vector3 [dimensionless] [(1, 2, 3)] 

439 

440 >>> var = sc.vectors(dims=['x'], values=[[1, 2, 3], [4, 5, 6]]) 

441 >>> var 

442 <scipp.Variable> (x: 2) vector3 [dimensionless] [(1, 2, 3), (4, 5, 6)] 

443 >>> var.values 

444 array([[1., 2., 3.], 

445 [4., 5., 6.]]) 

446 

447 >>> sc.vectors(dims=['x'], values=[[1, 2, 3], [4, 5, 6]], unit='mm') 

448 <scipp.Variable> (x: 2) vector3 [mm] [(1, 2, 3), (4, 5, 6)] 

449 """ 

450 return _cpp.Variable(dims=dims, values=values, unit=unit, dtype=DType.vector3) 

451 

452 

453def array( 

454 *, 

455 dims: Iterable[str], 

456 values: ArrayLike, 

457 variances: Optional[ArrayLike] = None, 

458 unit: Union[Unit, str, None] = default_unit, 

459 dtype: Optional[DTypeLike] = None, 

460) -> Variable: 

461 """Constructs a :class:`Variable` with given dimensions, containing given 

462 values and optional variances. 

463 

464 Dimension and value shape must match. 

465 Only keyword arguments accepted. 

466 

467 Parameters 

468 ---------- 

469 dims: 

470 Dimension labels 

471 values: numpy.typing.ArrayLike 

472 Initial values. 

473 variances: numpy.typing.ArrayLike 

474 Initial variances, must be same shape and size as values. 

475 unit: 

476 Unit of contents. 

477 dtype: scipp.typing.DTypeLike 

478 Type of underlying data. By default, inferred from `values` argument. 

479 

480 Returns 

481 ------- 

482 : 

483 A variable with the given values. 

484 

485 See Also 

486 -------- 

487 scipp.empty, scipp.ones, scipp.scalar, scipp.zeros 

488 

489 Examples 

490 -------- 

491 

492 >>> sc.array(dims=['x'], values=[1, 2, 3]) 

493 <scipp.Variable> (x: 3) int64 [dimensionless] [1, 2, 3] 

494 

495 Multiple dimensions: 

496 

497 >>> sc.array(dims=['x', 'y'], values=[[1, 2, 3], [4, 5, 6]]) 

498 <scipp.Variable> (x: 2, y: 3) int64 [dimensionless] [1, 2, ..., 5, 6] 

499 

500 DType upcasting: 

501 

502 >>> sc.array(dims=['x'], values=[1, 2, 3.0]) 

503 <scipp.Variable> (x: 3) float64 [dimensionless] [1, 2, 3] 

504 

505 Manually specified dtype: 

506 

507 >>> sc.array(dims=['x'], values=[1, 2, 3], dtype=float) 

508 <scipp.Variable> (x: 3) float64 [dimensionless] [1, 2, 3] 

509 

510 Set unit: 

511 

512 >>> sc.array(dims=['x'], values=[1, 2, 3], unit='m') 

513 <scipp.Variable> (x: 3) int64 [m] [1, 2, 3] 

514 

515 Setting variances: 

516 

517 >>> sc.array(dims=['x'], values=[1.0, 2.0, 3.0], variances=[0.1, 0.2, 0.3]) 

518 <scipp.Variable> (x: 3) float64 [dimensionless] [1, 2, 3] [0.1, 0.2, 0.3] 

519 """ 

520 return _cpp.Variable( 

521 dims=dims, values=values, variances=variances, unit=unit, dtype=dtype 

522 ) 

523 

524 

525def _expect_no_variances(args): 

526 has_variances = [ 

527 key 

528 for key, val in args.items() 

529 if val is not None and val.variances is not None 

530 ] 

531 if has_variances: 

532 raise _cpp.VariancesError( 

533 'Arguments cannot have variances. ' f'Passed variances in {has_variances}' 

534 ) 

535 

536 

537# Assumes that all arguments are Variable or None. 

538def _ensure_same_unit(*, unit, args: dict): 

539 if unit == default_unit: 

540 units = {key: val.unit for key, val in args.items() if val is not None} 

541 if len(set(units.values())) != 1: 

542 raise _cpp.UnitError( 

543 f'All units of the following arguments must be equal: {units}. ' 

544 'You can specify a unit explicitly with the `unit` argument.' 

545 ) 

546 unit = next(iter(units.values())) 

547 return { 

548 key: _cpp.to_unit(val, unit, copy=False).value if val is not None else None 

549 for key, val in args.items() 

550 }, unit 

551 

552 

553# Process arguments of arange, linspace, etc and return them as plain numbers or None. 

554def _normalize_range_args(*, unit, **kwargs): 

555 is_var = { 

556 key: isinstance(val, _cpp.Variable) 

557 for key, val in kwargs.items() 

558 if val is not None 

559 } 

560 if any(is_var.values()): 

561 if not all(is_var.values()): 

562 arg_types = {key: type(val) for key, val in kwargs.items()} 

563 raise TypeError( 

564 'Either all of the following arguments or none have to ' 

565 f'be variables: {arg_types}' 

566 ) 

567 _expect_no_variances(kwargs) 

568 return _ensure_same_unit(unit=unit, args=kwargs) 

569 return kwargs, unit 

570 

571 

572def linspace( 

573 dim: str, 

574 start: NumberOrVar, 

575 stop: NumberOrVar, 

576 num: int, 

577 *, 

578 endpoint: bool = True, 

579 unit: Union[Unit, str, None] = default_unit, 

580 dtype: Optional[DTypeLike] = None, 

581) -> Variable: 

582 """Constructs a :class:`Variable` with `num` evenly spaced samples, 

583 calculated over the interval `[start, stop]`. 

584 

585 Parameters 

586 ---------- 

587 dim: 

588 Dimension label. 

589 start: 

590 The starting value of the sequence. 

591 stop: 

592 The end value of the sequence. 

593 num: 

594 Number of samples to generate. 

595 endpoint: 

596 If True, `step` is the last returned value. 

597 Otherwise, it is not included. 

598 unit: 

599 Unit of contents. 

600 dtype: scipp.typing.DTypeLike 

601 Type of underlying data. By default, inferred from `value` argument. 

602 

603 Returns 

604 ------- 

605 : 

606 A variable of evenly spaced values. 

607 

608 See Also 

609 -------- 

610 scipp.arange, scipp.geomspace, scipp.logspace 

611 

612 Examples 

613 -------- 

614 

615 >>> sc.linspace('x', 2.0, 4.0, num=4) 

616 <scipp.Variable> (x: 4) float64 [dimensionless] [2, 2.66667, 3.33333, 4] 

617 >>> sc.linspace('x', 2.0, 4.0, num=4, endpoint=False) 

618 <scipp.Variable> (x: 4) float64 [dimensionless] [2, 2.5, 3, 3.5] 

619 

620 >>> sc.linspace('x', 1.5, 3.0, num=4, unit='m') 

621 <scipp.Variable> (x: 4) float64 [m] [1.5, 2, 2.5, 3] 

622 """ 

623 range_args, unit = _normalize_range_args(unit=unit, start=start, stop=stop) 

624 return array( 

625 dims=[dim], 

626 values=_np.linspace(**range_args, num=num, endpoint=endpoint), 

627 unit=unit, 

628 dtype=dtype, 

629 ) 

630 

631 

632def geomspace( 

633 dim: str, 

634 start: NumberOrVar, 

635 stop: NumberOrVar, 

636 num: int, 

637 *, 

638 endpoint: bool = True, 

639 unit: Union[Unit, str, None] = default_unit, 

640 dtype: Optional[DTypeLike] = None, 

641) -> Variable: 

642 """Constructs a :class:`Variable` with values spaced evenly on a log scale 

643 (a geometric progression). 

644 

645 This is similar to :py:func:`scipp.logspace`, but with endpoints specified 

646 directly instead of as exponents. 

647 Each output sample is a constant multiple of the previous. 

648 

649 Parameters 

650 ---------- 

651 dim: 

652 Dimension label. 

653 start: 

654 The starting value of the sequence. 

655 stop: 

656 The end value of the sequence. 

657 num: 

658 Number of samples to generate. 

659 endpoint: 

660 If True, `step` is the last returned value. 

661 Otherwise, it is not included. 

662 unit: 

663 Unit of contents. 

664 dtype: scipp.typing.DTypeLike 

665 Type of underlying data. By default, inferred from `value` argument. 

666 

667 Returns 

668 ------- 

669 : 

670 A variable of evenly spaced values on a logscale. 

671 

672 See Also 

673 -------- 

674 scipp.arange, scipp.linspace, scipp.logspace 

675 

676 Examples 

677 -------- 

678 

679 >>> sc.geomspace('x', 1.0, 1000.0, num=4) 

680 <scipp.Variable> (x: 4) float64 [dimensionless] [1, 10, 100, 1000] 

681 >>> sc.geomspace('x', 1.0, 1000.0, num=4, endpoint=False) 

682 <scipp.Variable> (x: 4) float64 [dimensionless] [1, 5.62341, 31.6228, 177.828] 

683 

684 >>> sc.geomspace('x', 1.0, 100.0, num=3, unit='s') 

685 <scipp.Variable> (x: 3) float64 [s] [1, 10, 100] 

686 """ 

687 range_args, unit = _normalize_range_args(unit=unit, start=start, stop=stop) 

688 return array( 

689 dims=[dim], 

690 values=_np.geomspace(**range_args, num=num, endpoint=endpoint), 

691 unit=unit, 

692 dtype=dtype, 

693 ) 

694 

695 

696def logspace( 

697 dim: str, 

698 start: NumberOrVar, 

699 stop: NumberOrVar, 

700 num: int, 

701 *, 

702 endpoint: bool = True, 

703 base: float = 10.0, 

704 unit: Union[Unit, str, None] = default_unit, 

705 dtype: Optional[DTypeLike] = None, 

706) -> Variable: 

707 """Constructs a :class:`Variable` with values spaced evenly on a log scale. 

708 

709 This is similar to :py:func:`scipp.geomspace`, but with endpoints specified 

710 as exponents. 

711 

712 Parameters 

713 ---------- 

714 dim: 

715 Dimension label. 

716 start: 

717 The starting value of the sequence. 

718 stop: 

719 The end value of the sequence. 

720 num: 

721 Number of samples to generate. 

722 base: 

723 The base of the log space. 

724 endpoint: 

725 If True, `step` is the last returned value. 

726 Otherwise, it is not included. 

727 unit: 

728 Unit of contents. 

729 dtype: scipp.typing.DTypeLike 

730 Type of underlying data. By default, inferred from `value` argument. 

731 

732 Returns 

733 ------- 

734 : 

735 A variable of evenly spaced values on a logscale. 

736 

737 See Also 

738 -------- 

739 scipp.arange, scipp.geomspace, scipp.linspace 

740 

741 Examples 

742 -------- 

743 

744 >>> sc.logspace('x', 1, 4, num=4) 

745 <scipp.Variable> (x: 4) float64 [dimensionless] [10, 100, 1000, 10000] 

746 >>> sc.logspace('x', 1, 4, num=4, endpoint=False) 

747 <scipp.Variable> (x: 4) float64 [dimensionless] [10, 56.2341, 316.228, 1778.28] 

748 

749 Set a different base: 

750 

751 >>> sc.logspace('x', 1, 4, num=4, base=2) 

752 <scipp.Variable> (x: 4) float64 [dimensionless] [2, 4, 8, 16] 

753 

754 Set a unit: 

755 

756 >>> sc.logspace('x', 1, 3, num=3, unit='m') 

757 <scipp.Variable> (x: 3) float64 [m] [10, 100, 1000] 

758 """ 

759 # Passing unit='one' enforces that start and stop are dimensionless. 

760 range_args, _ = _normalize_range_args(unit='one', start=start, stop=stop) 

761 return array( 

762 dims=[dim], 

763 values=_np.logspace(**range_args, num=num, base=base, endpoint=endpoint), 

764 unit=unit, 

765 dtype=dtype, 

766 ) 

767 

768 

769def arange( 

770 dim: str, 

771 start: Union[NumberOrVar, _np.datetime64, str], 

772 stop: Optional[Union[NumberOrVar, _np.datetime64, str]] = None, 

773 step: Optional[NumberOrVar] = None, 

774 *, 

775 unit: Union[Unit, str, None] = default_unit, 

776 dtype: Optional[DTypeLike] = None, 

777) -> Variable: 

778 """Creates a :class:`Variable` with evenly spaced values within a given interval. 

779 

780 Values are generated within the half-open interval [start, stop). 

781 In other words, the interval including start but excluding stop. 

782 

783 ``start``, ``stop``, and ``step`` may be given as plain values or as 0-D variables. 

784 In the latter case this then implies the unit (the units of all arguments must be 

785 identical), but a different unit-scale can still be requested with the ``unit`` 

786 argument. 

787 

788 When all the types or dtypes of the input arguments are the same, the output will 

789 also have this dtype. This is different to :func:`numpy.arange` which will always 

790 produce 64-bit (int64 or float64) outputs. 

791 

792 Warning 

793 ------- 

794 The length of the output might not be numerically stable. 

795 See :func:`numpy.arange`. 

796 

797 Parameters 

798 ---------- 

799 dim: 

800 Dimension label. 

801 start: 

802 Optional, the starting value of the sequence. Default=0. 

803 stop: 

804 End of interval. The interval does not include this value, 

805 except in some (rare) cases where step is not an integer and 

806 floating-point round-off can come into play. 

807 step: 

808 Spacing between values. 

809 unit: 

810 Unit of contents. 

811 dtype: scipp.typing.DTypeLike 

812 Type of underlying data. By default, inferred from `value` argument. 

813 

814 Returns 

815 ------- 

816 : 

817 A variable of evenly spaced values. 

818 

819 See Also 

820 -------- 

821 scipp.geomspace, scipp.linspace, scipp.logspace 

822 

823 Examples 

824 -------- 

825 

826 >>> sc.arange('x', 4) 

827 <scipp.Variable> (x: 4) int64 [dimensionless] [0, 1, 2, 3] 

828 >>> sc.arange('x', 1, 5) 

829 <scipp.Variable> (x: 4) int64 [dimensionless] [1, 2, 3, 4] 

830 >>> sc.arange('x', 1, 5, 0.5) 

831 <scipp.Variable> (x: 8) float64 [dimensionless] [1, 1.5, ..., 4, 4.5] 

832 

833 >>> sc.arange('x', 1, 5, unit='m') 

834 <scipp.Variable> (x: 4) int64 [m] [1, 2, 3, 4] 

835 

836 Datetimes are also supported: 

837 

838 >>> sc.arange('t', '2000-01-01T01:00:00', '2000-01-01T01:01:30', 30 * sc.Unit('s'), dtype='datetime64') 

839 <scipp.Variable> (t: 3) datetime64 [s] [2000-01-01T01:00:00, 2000-01-01T01:00:30, 2000-01-01T01:01:00] 

840 

841 Note that in this case the datetime ``start`` and ``stop`` strings implicitly 

842 define the unit. The ``step`` must have the same unit. 

843 """ 

844 if dtype == 'datetime64' and isinstance(start, str): 

845 start = datetime(start) 

846 stop = stop if stop is None else datetime(stop) 

847 range_args, unit = _normalize_range_args( 

848 unit=unit, start=start, stop=stop, step=step 

849 ) 

850 args = [x for x in [start, stop, step] if x is not None] 

851 types = [ 

852 x.values.dtype if isinstance(x, _cpp.Variable) else _np.asarray(x).dtype 

853 for x in args 

854 ] 

855 if dtype is None: 

856 candidates = set(types) 

857 if len(candidates) == 1: 

858 dtype = next(iter(candidates)) 

859 if dtype is not None and dtype != str('datetime64'): 

860 numpy_dtype = str(dtype) if isinstance(dtype, DType) else dtype 

861 else: 

862 numpy_dtype = None 

863 return array( 

864 dims=[dim], 

865 values=_np.arange(**range_args, dtype=numpy_dtype), 

866 unit=unit, 

867 dtype=dtype, 

868 ) 

869 

870 

871@contextmanager 

872def _timezone_warning_as_error(): 

873 with warnings.catch_warnings(): 

874 warnings.filterwarnings( 

875 'error', category=DeprecationWarning, message='parsing timezone' 

876 ) 

877 try: 

878 yield 

879 except DeprecationWarning: 

880 raise ValueError( 

881 'Parsing timezone aware datetimes is not supported' 

882 ) from None 

883 

884 

885def datetime( 

886 value: Union[str, int, _np.datetime64], 

887 *, 

888 unit: Optional[Union[Unit, str, None]] = default_unit, 

889) -> Variable: 

890 """Constructs a zero dimensional :class:`Variable` with a dtype of datetime64. 

891 

892 Parameters 

893 ---------- 

894 value: 

895 

896 - `str`: Interpret the string according to the ISO 8601 date time format. 

897 - `int`: Number of time units (see argument ``unit``) 

898 since Scipp's epoch (see :py:func:`scipp.epoch`). 

899 - `np.datetime64`: Construct equivalent datetime of Scipp. 

900 

901 unit: Unit of the resulting datetime. 

902 Can be deduced if ``value`` is a str or np.datetime64 but 

903 is required if it is an int. 

904 

905 Returns 

906 ------- 

907 : 

908 A scalar variable containing a datetime. 

909 

910 See Also 

911 -------- 

912 scipp.datetimes: 

913 scipp.epoch: 

914 Details in: 

915 'Dates and Times' section in `Data Types <../../reference/dtype.rst>`_ 

916 

917 Examples 

918 -------- 

919 

920 >>> sc.datetime('2021-01-10T14:16:15') 

921 <scipp.Variable> () datetime64 [s] 2021-01-10T14:16:15 

922 >>> sc.datetime('2021-01-10T14:16:15', unit='ns') 

923 <scipp.Variable> () datetime64 [ns] 2021-01-10T14:16:15.000000000 

924 >>> sc.datetime(1610288175, unit='s') 

925 <scipp.Variable> () datetime64 [s] 2021-01-10T14:16:15 

926 

927 Get the current time: 

928 

929 >>> now = sc.datetime('now', unit='s') 

930 """ 

931 if isinstance(value, str): 

932 with _timezone_warning_as_error(): 

933 return scalar(_np.datetime64(value), unit=unit) 

934 return scalar(value, unit=unit, dtype=_cpp.DType.datetime64) 

935 

936 

937def datetimes( 

938 *, dims, values: ArrayLike, unit: Optional[Union[Unit, str, None]] = default_unit 

939) -> Variable: 

940 """Constructs an array :class:`Variable` with a dtype of datetime64. 

941 

942 Parameters 

943 ---------- 

944 dims: 

945 Dimension labels 

946 values: numpy.typing.ArrayLike 

947 Numpy array or something that can be converted to a 

948 Numpy array of datetimes. 

949 unit: 

950 Unit for the resulting Variable. 

951 Can be deduced if ``values`` contains strings or np.datetime64's. 

952 

953 Returns 

954 ------- 

955 : 

956 An array variable containing a datetime. 

957 

958 See Also 

959 -------- 

960 scipp.datetime: 

961 scipp.epoch: 

962 Details in: 

963 'Dates and Times' section in `Data Types <../../reference/dtype.rst>`_ 

964 

965 Examples 

966 -------- 

967 

968 >>> sc.datetimes(dims=['t'], values=['2021-01-10T01:23:45', 

969 ... '2021-01-11T01:23:45']) 

970 <scipp.Variable> (t: 2) datetime64 [s] [2021-01-10T01:23:45, 2021-01-11T01:23:45] 

971 >>> sc.datetimes(dims=['t'], values=['2021-01-10T01:23:45', 

972 ... '2021-01-11T01:23:45'], unit='h') 

973 <scipp.Variable> (t: 2) datetime64 [h] [2021-01-10T01:00:00, 2021-01-11T01:00:00] 

974 >>> sc.datetimes(dims=['t'], values=[0, 1610288175], unit='s') 

975 <scipp.Variable> (t: 2) datetime64 [s] [1970-01-01T00:00:00, 2021-01-10T14:16:15] 

976 """ 

977 np_unit_str = f'[{u}]' if (u := _cpp.to_numpy_time_string(unit)) else '' 

978 with _timezone_warning_as_error(): 

979 return array( 

980 dims=dims, values=_np.asarray(values, dtype=f'datetime64{np_unit_str}') 

981 ) 

982 

983 

984def epoch(*, unit: Union[Unit, str]) -> Variable: 

985 """Constructs a zero dimensional :class:`Variable` with a dtype of 

986 datetime64 that contains Scipp's epoch. 

987 

988 Currently, the epoch of datetimes in Scipp is the Unix epoch 1970-01-01T00:00:00. 

989 

990 Parameters 

991 ---------- 

992 unit: 

993 Unit of the resulting Variable. 

994 

995 Returns 

996 ------- 

997 : 

998 A scalar variable containing the datetime of the epoch. 

999 

1000 See Also 

1001 -------- 

1002 scipp.datetime: 

1003 scipp.datetimes: 

1004 Details in: 

1005 'Dates and Times' section in `Data Types <../../reference/dtype.rst>`_ 

1006 

1007 Examples 

1008 -------- 

1009 

1010 >>> sc.epoch(unit='s') 

1011 <scipp.Variable> () datetime64 [s] 1970-01-01T00:00:00 

1012 """ 

1013 return scalar(0, unit=unit, dtype=_cpp.DType.datetime64)