invrs-opt 0.3.2__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
invrs_opt/__init__.py CHANGED
@@ -3,7 +3,7 @@
3
3
  Copyright (c) 2023 The INVRS-IO authors.
4
4
  """
5
5
 
6
- __version__ = "v0.3.2"
6
+ __version__ = "v0.5.0"
7
7
  __author__ = "Martin F. Schubert <mfschubert@gmail.com>"
8
8
 
9
9
  from invrs_opt.lbfgsb.lbfgsb import density_lbfgsb as density_lbfgsb
File without changes
@@ -0,0 +1,152 @@
1
+ """Defines basic client optimizers for use with an optimization service.
2
+
3
+ Copyright (c) 2023 The INVRS-IO authors.
4
+ """
5
+
6
+ import json
7
+ import requests
8
+ import time
9
+ from typing import Any, Dict, Optional
10
+
11
+ from totypes import json_utils
12
+
13
+ from invrs_opt import base
14
+ from invrs_opt.experimental import labels
15
+
16
+
17
+ PyTree = Any
18
+ StateToken = str
19
+
20
+ SESSION = None
21
+ SERVER_ADDRESS = None
22
+
23
+
24
+ def login(server_address: str) -> None:
25
+ """Set the global server address and create a requests session."""
26
+ global SESSION
27
+ global SERVER_ADDRESS
28
+ SESSION = requests.Session()
29
+ SERVER_ADDRESS = server_address
30
+
31
+
32
+ def optimizer_client(
33
+ algorithm: str,
34
+ hparams: Dict[str, Any],
35
+ server_address: Optional[str],
36
+ session: Optional[requests.Session],
37
+ ) -> base.Optimizer:
38
+ """Generic optimizer class."""
39
+
40
+ if server_address is None:
41
+ if SERVER_ADDRESS is None:
42
+ raise ValueError(
43
+ "Argument `server_address` and the global `SERVER_ADDRESS` cannot "
44
+ "both be `None`. Use the `login` method to set the global, or "
45
+ "explicitly provide a value."
46
+ )
47
+ if session is None:
48
+ if SESSION is None:
49
+ raise ValueError(
50
+ "Argument `session` and the global `SESSION` cannot "
51
+ "both be `None`. Use the `login` method to set the global, or "
52
+ "explicitly provide a value."
53
+ )
54
+ session = SESSION
55
+
56
+ opt_config = {
57
+ labels.ALGORITHM: algorithm,
58
+ labels.HPARAMS: hparams,
59
+ }
60
+
61
+ def init_fn(params: PyTree) -> StateToken:
62
+ """Handles 'init' requests."""
63
+ serialized_data = json_utils.json_from_pytree(
64
+ dict(opt_config=opt_config, data={"params": params})
65
+ )
66
+ post_response = session.post(
67
+ f"{SERVER_ADDRESS}/{labels.ROUTE_INIT}/", data=serialized_data
68
+ )
69
+
70
+ if not post_response.status_code == 200:
71
+ raise requests.RequestException(post_response.text)
72
+ response = json.loads(post_response.text)
73
+ new_state_token: str = response[labels.STATE_TOKEN]
74
+ return new_state_token
75
+
76
+ def update_fn(
77
+ *,
78
+ grad: PyTree,
79
+ value: float,
80
+ params: PyTree,
81
+ state: StateToken,
82
+ ) -> StateToken:
83
+ """Handles 'update' requests."""
84
+ state_token = state
85
+ del state
86
+ serialized_data = json_utils.json_from_pytree(
87
+ {
88
+ labels.OPT_CONFIG: opt_config,
89
+ labels.DATA: {
90
+ labels.PARAMS: params,
91
+ labels.VALUE: value,
92
+ labels.GRAD: grad,
93
+ labels.STATE_TOKEN: state_token,
94
+ },
95
+ }
96
+ )
97
+ post_response = session.post(
98
+ f"{SERVER_ADDRESS}/{labels.ROUTE_UPDATE}/{state_token}/",
99
+ data=serialized_data,
100
+ )
101
+
102
+ if not post_response.status_code == 200:
103
+ raise requests.RequestException(post_response.text)
104
+ response = json.loads(post_response.text)
105
+ new_state_token: str = response[labels.STATE_TOKEN]
106
+ return new_state_token
107
+
108
+ def params_fn(
109
+ state: StateToken,
110
+ timeout: float = 60.0,
111
+ poll_interval: float = 0.1,
112
+ ) -> PyTree:
113
+ """Handles 'params' requests."""
114
+ state_token = state
115
+ del state
116
+ assert timeout >= poll_interval
117
+ start_time = time.time()
118
+ while time.time() < start_time + timeout:
119
+ get_response = session.get(
120
+ f"{SERVER_ADDRESS}/{labels.ROUTE_PARAMS}/{state_token}"
121
+ )
122
+ if get_response.status_code == 200:
123
+ break
124
+ elif get_response.status_code == 404 and get_response.text.endswith(
125
+ labels.MESSAGE_STATE_NOT_READY.format(state_token)
126
+ ):
127
+ time.sleep(poll_interval)
128
+ else:
129
+ raise requests.RequestException(get_response.text)
130
+
131
+ if not get_response.status_code == 200:
132
+ raise requests.Timeout("Timed out while waiting for params.")
133
+ response = json.loads(get_response.text)
134
+ return json_utils.pytree_from_json(response[labels.PARAMS])
135
+
136
+ return base.Optimizer(init=init_fn, update=update_fn, params=params_fn)
137
+
138
+
139
+ # -----------------------------------------------------------------------------
140
+ # Specific optimizers implemented here.
141
+ # -----------------------------------------------------------------------------
142
+
143
+
144
+ def lbfgsb(maxcor: int = 20, line_search_max_steps: int = 100) -> base.Optimizer:
145
+ """Optimizer implementing the L-BFGS-B scheme."""
146
+ hparams = {
147
+ "maxcor": maxcor,
148
+ "line_search_max_steps": line_search_max_steps,
149
+ }
150
+ return optimizer_client(
151
+ algorithm="lbfgsb", hparams=hparams, server_address=None, session=None
152
+ )
@@ -0,0 +1,23 @@
1
+ """Defines labels and messages used in the context of an optimization service.
2
+
3
+ Copyright (c) 2023 The INVRS-IO authors.
4
+ """
5
+
6
+ VALUE = "value"
7
+ GRAD = "grad"
8
+ PARAMS = "params"
9
+ STATE_TOKEN = "state_token"
10
+ MESSAGE = "message"
11
+
12
+ OPT_CONFIG = "opt_config"
13
+ ALGORITHM = "algorithm"
14
+ HPARAMS = "hparams"
15
+ DATA = "data"
16
+
17
+ ROUTE_INIT = "init"
18
+ ROUTE_UPDATE = "update"
19
+ ROUTE_PARAMS = "params"
20
+
21
+ MESSAGE_STATE_NOT_KNOWN = "State token {} was not recognized."
22
+ MESSAGE_STATE_NOT_READY = "State for token {} is not ready."
23
+ MESSAGE_STATE_NOT_VALID = "State for token {} is not valid."
@@ -29,16 +29,16 @@ LbfgsbState = Tuple[PyTree, PyTree, JaxLbfgsbDict]
29
29
  # Task message prefixes for the underlying L-BFGS-B implementation.
30
30
  TASK_START = b"START"
31
31
  TASK_FG = b"FG"
32
+ TASK_CONVERGED = b"CONVERGENCE"
32
33
 
33
- # Parameters which configure the state update step.
34
34
  UPDATE_IPRINT = -1
35
- UPDATE_PGTOL = 0.0
36
- UPDATE_FACTR = 0.0
37
35
 
38
36
  # Maximum value for the `maxcor` parameter in the L-BFGS-B scheme.
39
37
  MAXCOR_MAX_VALUE = 100
40
38
  MAXCOR_DEFAULT = 20
41
39
  LINE_SEARCH_MAX_STEPS_DEFAULT = 100
40
+ FTOL_DEFAULT = 0.0
41
+ GTOL_DEFAULT = 0.0
42
42
 
43
43
  # Maps bound scenarios to integers.
44
44
  BOUNDS_MAP: Dict[Tuple[bool, bool], int] = {
@@ -54,6 +54,8 @@ FORTRAN_INT = scipy_lbfgsb.types.intvar.dtype
54
54
  def lbfgsb(
55
55
  maxcor: int = MAXCOR_DEFAULT,
56
56
  line_search_max_steps: int = LINE_SEARCH_MAX_STEPS_DEFAULT,
57
+ ftol: float = FTOL_DEFAULT,
58
+ gtol: float = GTOL_DEFAULT,
57
59
  ) -> base.Optimizer:
58
60
  """Return an optimizer implementing the standard L-BFGS-B algorithm.
59
61
 
@@ -85,10 +87,17 @@ def lbfgsb(
85
87
  While the algorithm can work with pytrees of jax arrays, numpy arrays can
86
88
  also be used. Thus, e.g. the optimizer can directly be used with autograd.
87
89
 
90
+ When the optimization has converged (according to `ftol` or `gtol` criteria), the
91
+ optimizer simply returns the parameters which obtained the converged result. The
92
+ convergence can be queried by `is_converged(state)`.
93
+
88
94
  Args:
89
95
  maxcor: The maximum number of variable metric corrections used to define
90
96
  the limited memory matrix, in the L-BFGS-B scheme.
91
97
  line_search_max_steps: The maximum number of steps in the line search.
98
+ ftol: Tolerance for stopping criteria based on function values. See scipy
99
+ documentation for details.
100
+ gtol: Tolerance for stopping criteria based on gradient.
92
101
 
93
102
  Returns:
94
103
  The `base.Optimizer`.
@@ -96,6 +105,8 @@ def lbfgsb(
96
105
  return transformed_lbfgsb(
97
106
  maxcor=maxcor,
98
107
  line_search_max_steps=line_search_max_steps,
108
+ ftol=ftol,
109
+ gtol=gtol,
99
110
  transform_fn=lambda x: x,
100
111
  initialize_latent_fn=lambda x: x,
101
112
  )
@@ -105,6 +116,8 @@ def density_lbfgsb(
105
116
  beta: float,
106
117
  maxcor: int = MAXCOR_DEFAULT,
107
118
  line_search_max_steps: int = LINE_SEARCH_MAX_STEPS_DEFAULT,
119
+ ftol: float = FTOL_DEFAULT,
120
+ gtol: float = GTOL_DEFAULT,
108
121
  ) -> base.Optimizer:
109
122
  """Return an L-BFGS-B optimizer with additional transforms for density arrays.
110
123
 
@@ -117,11 +130,18 @@ def density_lbfgsb(
117
130
  and spacing parameters of the `DensityArray2D`. Where the bounds differ, the
118
131
  density is scaled before the transform is applied, and then unscaled afterwards.
119
132
 
133
+ When the optimization has converged (according to `ftol` or `gtol` criteria), the
134
+ optimizer simply returns the parameters which obtained the converged result. The
135
+ convergence can be queried by `is_converged(state)`.
136
+
120
137
  Args:
121
138
  beta: Determines the steepness of the thresholding.
122
139
  maxcor: The maximum number of variable metric corrections used to define
123
140
  the limited memory matrix, in the L-BFGS-B scheme.
124
141
  line_search_max_steps: The maximum number of steps in the line search.
142
+ ftol: Tolerance for stopping criteria based on function values. See scipy
143
+ documentation for details.
144
+ gtol: Tolerance for stopping criteria based on gradient.
125
145
 
126
146
  Returns:
127
147
  The `base.Optimizer`.
@@ -164,6 +184,8 @@ def density_lbfgsb(
164
184
  return transformed_lbfgsb(
165
185
  maxcor=maxcor,
166
186
  line_search_max_steps=line_search_max_steps,
187
+ ftol=ftol,
188
+ gtol=gtol,
167
189
  transform_fn=transform_fn,
168
190
  initialize_latent_fn=initialize_latent_fn,
169
191
  )
@@ -172,6 +194,8 @@ def density_lbfgsb(
172
194
  def transformed_lbfgsb(
173
195
  maxcor: int,
174
196
  line_search_max_steps: int,
197
+ ftol: float,
198
+ gtol: float,
175
199
  transform_fn: Callable[[PyTree], PyTree],
176
200
  initialize_latent_fn: Callable[[PyTree], PyTree],
177
201
  ) -> base.Optimizer:
@@ -182,10 +206,17 @@ def transformed_lbfgsb(
182
206
  `transform_fn`. In the simple case where this is just `lambda x: x` (i.e.
183
207
  the identity), this is equivalent to the standard L-BFGS-B algorithm.
184
208
 
209
+ When the optimization has converged (according to `ftol` or `gtol` criteria), the
210
+ optimizer simply returns the parameters which obtained the converged result. The
211
+ convergence can be queried by `is_converged(state)`.
212
+
185
213
  Args:
186
214
  maxcor: The maximum number of variable metric corrections used to define
187
215
  the limited memory matrix, in the L-BFGS-B scheme.
188
216
  line_search_max_steps: The maximum number of steps in the line search.
217
+ ftol: Tolerance for stopping criteria based on function values. See scipy
218
+ documentation for details.
219
+ gtol: Tolerance for stopping criteria based on gradient.
189
220
  transform_fn: Function which transforms the internal latent parameters to
190
221
  the parameters returned by the optimizer.
191
222
  initialize_latent_fn: Function which computes the initial latent parameters
@@ -218,6 +249,8 @@ def transformed_lbfgsb(
218
249
  upper_bound=_bound_for_params(upper_bound, params),
219
250
  maxcor=maxcor,
220
251
  line_search_max_steps=line_search_max_steps,
252
+ ftol=ftol,
253
+ gtol=gtol,
221
254
  )
222
255
  latent_params = _to_pytree(scipy_lbfgsb_state.x, params)
223
256
  return latent_params, scipy_lbfgsb_state.to_jax()
@@ -261,10 +294,12 @@ def transformed_lbfgsb(
261
294
  flat_latent_params = jnp.asarray(scipy_lbfgsb_state.x)
262
295
  return flat_latent_params, scipy_lbfgsb_state.to_jax()
263
296
 
264
- params, latent_params, jax_lbfgsb_state = state
297
+ _, latent_params, jax_lbfgsb_state = state
265
298
  _, vjp_fn = jax.vjp(transform_fn, latent_params)
266
299
  (latent_grad,) = vjp_fn(grad)
267
- flat_latent_grad, unflatten_fn = flatten_util.ravel_pytree(latent_grad)
300
+ flat_latent_grad, unflatten_fn = flatten_util.ravel_pytree(
301
+ latent_grad
302
+ ) # type: ignore[no-untyped-call]
268
303
 
269
304
  (
270
305
  flat_latent_params,
@@ -286,6 +321,11 @@ def transformed_lbfgsb(
286
321
  )
287
322
 
288
323
 
324
+ def is_converged(state: LbfgsbState) -> jnp.ndarray:
325
+ """Returns `True` if the optimization has converged."""
326
+ return state[2]["converged"]
327
+
328
+
289
329
  # ------------------------------------------------------------------------------
290
330
  # Helper functions.
291
331
  # ------------------------------------------------------------------------------
@@ -390,8 +430,11 @@ def _example_state(params: PyTree, maxcor: int) -> PyTree:
390
430
  float_params = tree_util.tree_map(lambda x: jnp.asarray(x, dtype=float), params)
391
431
  example_jax_lbfgsb_state = dict(
392
432
  x=jnp.zeros(n, dtype=float),
433
+ converged=jnp.asarray(False),
393
434
  _maxcor=jnp.zeros((), dtype=int),
394
435
  _line_search_max_steps=jnp.zeros((), dtype=int),
436
+ _ftol=jnp.zeros((), dtype=float),
437
+ _gtol=jnp.zeros((), dtype=float),
395
438
  _wa=jnp.ones(_wa_size(n=n, maxcor=maxcor), dtype=float),
396
439
  _iwa=jnp.ones(n * 3, dtype=jnp.int32), # Fortran int
397
440
  _task=jnp.zeros(59, dtype=int),
@@ -441,10 +484,13 @@ class ScipyLbfgsbState:
441
484
  """
442
485
 
443
486
  x: NDArray
487
+ converged: NDArray
444
488
  # Private attributes correspond to internal variables in the `scipy.optimize.
445
489
  # lbfgsb._minimize_lbfgsb` function.
446
490
  _maxcor: int
447
491
  _line_search_max_steps: int
492
+ _ftol: NDArray
493
+ _gtol: NDArray
448
494
  _wa: NDArray
449
495
  _iwa: NDArray
450
496
  _task: NDArray
@@ -474,8 +520,11 @@ class ScipyLbfgsbState:
474
520
  """Generates a dictionary of jax arrays defining the state."""
475
521
  return dict(
476
522
  x=jnp.asarray(self.x),
523
+ converged=jnp.asarray(self.converged),
477
524
  _maxcor=jnp.asarray(self._maxcor),
478
525
  _line_search_max_steps=jnp.asarray(self._line_search_max_steps),
526
+ _ftol=jnp.asarray(self._ftol),
527
+ _gtol=jnp.asarray(self._gtol),
479
528
  _wa=jnp.asarray(self._wa),
480
529
  _iwa=jnp.asarray(self._iwa),
481
530
  _task=_array_from_s60_str(self._task),
@@ -494,8 +543,11 @@ class ScipyLbfgsbState:
494
543
  state_dict = copy.deepcopy(state_dict)
495
544
  return ScipyLbfgsbState(
496
545
  x=onp.asarray(state_dict["x"], dtype=onp.float64),
546
+ converged=onp.asarray(state_dict["converged"], dtype=bool),
497
547
  _maxcor=int(state_dict["_maxcor"]),
498
548
  _line_search_max_steps=int(state_dict["_line_search_max_steps"]),
549
+ _ftol=onp.asarray(state_dict["_ftol"], dtype=onp.float64),
550
+ _gtol=onp.asarray(state_dict["_gtol"], dtype=onp.float64),
499
551
  _wa=onp.asarray(state_dict["_wa"], onp.float64),
500
552
  _iwa=onp.asarray(state_dict["_iwa"], dtype=FORTRAN_INT),
501
553
  _task=_s60_str_from_array(state_dict["_task"]),
@@ -516,6 +568,8 @@ class ScipyLbfgsbState:
516
568
  upper_bound: ElementwiseBound,
517
569
  maxcor: int,
518
570
  line_search_max_steps: int,
571
+ ftol: float,
572
+ gtol: float,
519
573
  ) -> "ScipyLbfgsbState":
520
574
  """Initializes the `ScipyLbfgsbState` for `x0`.
521
575
 
@@ -526,6 +580,9 @@ class ScipyLbfgsbState:
526
580
  maxcor: The maximum number of variable metric corrections used to define
527
581
  the limited memory matrix, in the L-BFGS-B scheme.
528
582
  line_search_max_steps: The maximum number of steps in the line search.
583
+ ftol: Tolerance for stopping criteria based on function values. See scipy
584
+ documentation for details.
585
+ gtol: Tolerance for stopping criteria based on gradient.
529
586
 
530
587
  Returns:
531
588
  The `ScipyLbfgsbState`.
@@ -556,8 +613,11 @@ class ScipyLbfgsbState:
556
613
  wa_size = _wa_size(n=n, maxcor=maxcor)
557
614
  state = ScipyLbfgsbState(
558
615
  x=onp.array(x0, onp.float64),
616
+ converged=onp.asarray(False),
559
617
  _maxcor=maxcor,
560
618
  _line_search_max_steps=line_search_max_steps,
619
+ _ftol=onp.asarray(ftol, onp.float64),
620
+ _gtol=onp.asarray(gtol, onp.float64),
561
621
  _wa=onp.zeros(wa_size, onp.float64),
562
622
  _iwa=onp.zeros(3 * n, FORTRAN_INT),
563
623
  _task=task,
@@ -580,12 +640,14 @@ class ScipyLbfgsbState:
580
640
  grad: NDArray,
581
641
  value: NDArray,
582
642
  ) -> None:
583
- """Performs an in-place update of the `ScipyLbfgsbState`.
643
+ """Performs an in-place update of the `ScipyLbfgsbState` if not converged.
584
644
 
585
645
  Args:
586
646
  grad: The function gradient for the current `x`.
587
647
  value: The scalar function value for the current `x`.
588
648
  """
649
+ if self.converged:
650
+ return
589
651
  if grad.shape != self.x.shape:
590
652
  raise ValueError(
591
653
  f"`grad` must have the same shape as attribute `x`, but got shapes "
@@ -606,8 +668,8 @@ class ScipyLbfgsbState:
606
668
  nbd=self._bound_type,
607
669
  f=value,
608
670
  g=grad,
609
- factr=UPDATE_FACTR,
610
- pgtol=UPDATE_PGTOL,
671
+ factr=self._ftol / onp.finfo(float).eps,
672
+ pgtol=self._gtol,
611
673
  wa=self._wa,
612
674
  iwa=self._iwa,
613
675
  task=self._task,
@@ -619,6 +681,8 @@ class ScipyLbfgsbState:
619
681
  maxls=self._line_search_max_steps,
620
682
  )
621
683
  task_str = self._task.tobytes()
684
+ if task_str.startswith(TASK_CONVERGED):
685
+ self.converged = onp.asarray(True)
622
686
  if task_str.startswith(TASK_FG):
623
687
  break
624
688
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: invrs_opt
3
- Version: 0.3.2
3
+ Version: 0.5.0
4
4
  Summary: Algorithms for inverse design
5
5
  Author-email: "Martin F. Schubert" <mfschubert@gmail.com>
6
6
  Maintainer-email: "Martin F. Schubert" <mfschubert@gmail.com>
@@ -33,8 +33,10 @@ License-File: LICENSE
33
33
  Requires-Dist: jax
34
34
  Requires-Dist: jaxlib
35
35
  Requires-Dist: numpy
36
+ Requires-Dist: requests
36
37
  Requires-Dist: scipy
37
38
  Requires-Dist: totypes
39
+ Requires-Dist: types-requests
38
40
  Provides-Extra: dev
39
41
  Requires-Dist: bump-my-version ; extra == 'dev'
40
42
  Requires-Dist: darglint ; extra == 'dev'
@@ -47,7 +49,7 @@ Requires-Dist: pytest-cov ; extra == 'tests'
47
49
  Requires-Dist: pytest-subtests ; extra == 'tests'
48
50
 
49
51
  # invrs-opt - Optimization algorithms for inverse design
50
- `v0.3.2`
52
+ `v0.5.0`
51
53
 
52
54
  ## Overview
53
55
 
@@ -0,0 +1,14 @@
1
+ invrs_opt/__init__.py,sha256=SK2nHKhwT60br9D6W8gfJ5Kj11BR3fIdGAEVAJBgH1I,309
2
+ invrs_opt/base.py,sha256=dSX9QkMPzI8ROxy2cFNmMwk_89eQbk0rw94CzvLPQoY,907
3
+ invrs_opt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ invrs_opt/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ invrs_opt/experimental/client.py,sha256=td5o_YqqbcSypDrWCVrHGSoF8UxEdOLtKU0z9Dth9lA,4842
6
+ invrs_opt/experimental/labels.py,sha256=dQDAMPyFMV6mXnMy295z8Ap205DRdVzysXny_Be8FmY,562
7
+ invrs_opt/lbfgsb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ invrs_opt/lbfgsb/lbfgsb.py,sha256=59OpEYmGH3ofDv6m8KQQob5mQJei2vTcm1COh7SAbgA,27896
9
+ invrs_opt/lbfgsb/transform.py,sha256=a_Saj9Wq4lvqCJBrg5L2Z9DZ2NVs1xqrHLqha90a9Ws,5971
10
+ invrs_opt-0.5.0.dist-info/LICENSE,sha256=ty6jHPvpyjHy6dbhnu6aDSY05bbl2jQTjnq9u1sBCfM,1078
11
+ invrs_opt-0.5.0.dist-info/METADATA,sha256=kV8T1uAmMnr0IDaNjq1SK8btKa_XlyHjapKOfti_MbY,3326
12
+ invrs_opt-0.5.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
13
+ invrs_opt-0.5.0.dist-info/top_level.txt,sha256=hOziS2uJ_NgwaW9yhtOfeuYnm1X7vobPBcp_6eVWKfM,10
14
+ invrs_opt-0.5.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,11 +0,0 @@
1
- invrs_opt/__init__.py,sha256=JG9h93Vq0AA2jX6zcqwxjA-LXOws8CJQeEbY9VQvQWQ,309
2
- invrs_opt/base.py,sha256=dSX9QkMPzI8ROxy2cFNmMwk_89eQbk0rw94CzvLPQoY,907
3
- invrs_opt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- invrs_opt/lbfgsb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- invrs_opt/lbfgsb/lbfgsb.py,sha256=2X0GVQwMCj2beYHLiAdwoAnBIdJLPThH0Jjduz3GzHA,25080
6
- invrs_opt/lbfgsb/transform.py,sha256=a_Saj9Wq4lvqCJBrg5L2Z9DZ2NVs1xqrHLqha90a9Ws,5971
7
- invrs_opt-0.3.2.dist-info/LICENSE,sha256=ty6jHPvpyjHy6dbhnu6aDSY05bbl2jQTjnq9u1sBCfM,1078
8
- invrs_opt-0.3.2.dist-info/METADATA,sha256=pwrRENMlb_s1bNAeROw-cJ-nIVuqR9815r-jPNM3cEg,3272
9
- invrs_opt-0.3.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
10
- invrs_opt-0.3.2.dist-info/top_level.txt,sha256=hOziS2uJ_NgwaW9yhtOfeuYnm1X7vobPBcp_6eVWKfM,10
11
- invrs_opt-0.3.2.dist-info/RECORD,,