@mark1russell7/packing 0.2.6 → 0.2.7

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.
@@ -0,0 +1,62 @@
1
+ // The strategy `portfolio` of roadmap item k20, part 2 (`docs/PACKER.md`, "The per-cell portfolio"). It is a composite
2
+ // strategy: it packs an input with the members of the set of its cell, and it keeps the best output. Nothing default uses it.
3
+
4
+ import { maxrects, push, rows, shelf, skyline } from "./adapters.ts";
5
+ import type { PackInput, PackOutput } from "./contract.ts";
6
+ import { portfolioDefaultMembers, portfolioMembers } from "./portfolio-cell.ts";
7
+ import { PORTFOLIO_SETS } from "./portfolio-sets.ts";
8
+ import { packPortfolio } from "./portfolio.ts";
9
+ import type { PackContext, Strategy, Supports } from "./strategy.ts";
10
+
11
+ /** The options of the strategy `portfolio`. Without options, the output is a function of the input only. */
12
+ export interface PortfolioStrategyOptions {
13
+ /** The size of the set of the cell: 2 or 3. With `k`, each of the `k` members runs. Without `k`, the strategy uses the set of 3. */
14
+ k?: 2 | 3;
15
+ /** The largest count of the members that run. Without `k` and `maxMembers`, the count is `defaultMembers` of the cell. */
16
+ maxMembers?: number;
17
+ /** The opt-in time limit of `portfolio` of the contract, in milliseconds. It has no default. A cut member makes the result not deterministic. */
18
+ budgetMs?: number;
19
+ }
20
+
21
+ /** The single strategies that the members of the table use */
22
+ const SINGLES: readonly Strategy[] = [rows, skyline, shelf, push, maxrects] as Strategy[];
23
+
24
+ /** This function gives the union of the supports of the strategies of the members of the table. */
25
+ function unionSupports(): Supports {
26
+ const used = new Set(Object.values(PORTFOLIO_SETS.entries).map((e) => e.strategy));
27
+ const out: Record<string, string[]> = {};
28
+ for (const s of SINGLES.filter((x) => used.has(x.id))) {
29
+ for (const [dim, values] of Object.entries(s.supports) as [string, string[]][]) {
30
+ const list = (out[dim] ??= []);
31
+ for (const v of values) if (!list.includes(v)) list.push(v);
32
+ }
33
+ }
34
+ return out as unknown as Supports;
35
+ }
36
+
37
+ /**
38
+ * The strategy `portfolio`: `packPortfolio` with the function form `(input) => portfolioMembers(input, k)`. The seed of the context
39
+ * goes to each member. Its supports are the union of the supports of the members.
40
+ *
41
+ * - Without `k` and `maxMembers`, the members are the set of 3 of the cell, and `maxMembers` is `defaultMembers` of the cell.
42
+ * The default output thus does not depend on the clock.
43
+ * - A member that does not support the cell of an input throws, and it does not win. When each member throws, the strategy throws the first error.
44
+ * - The option `budgetMs` goes to `portfolio` of the contract. The first member always runs.
45
+ * - The strategy throws a `RangeError` for a `k` other than 2 or 3.
46
+ */
47
+ export const portfolioStrategy: Strategy<PortfolioStrategyOptions> = {
48
+ id: "portfolio",
49
+ family: "portfolio",
50
+ catalog: [],
51
+ supports: unionSupports(),
52
+ defaults: {},
53
+ deterministic: true,
54
+ pack(input: PackInput, options: PortfolioStrategyOptions, context: PackContext): PackOutput {
55
+ const { k, maxMembers, budgetMs } = options;
56
+ if (k !== undefined && k !== 2 && k !== 3) throw new RangeError(`The option k of the strategy portfolio is 2 or 3, not ${String(k)}.`);
57
+ const size = k ?? 3;
58
+ const count = maxMembers ?? (k === undefined ? portfolioDefaultMembers(input) : undefined);
59
+ const limits = { ...(count === undefined ? {} : { maxMembers: count }), ...(budgetMs === undefined ? {} : { budgetMs }) };
60
+ return packPortfolio((x: PackInput) => portfolioMembers(x, size), { id: k === undefined ? "portfolio" : `portfolio-k${k}`, seed: context.seed, ...limits }).run(input);
61
+ },
62
+ };
package/src/portfolio.ts CHANGED
@@ -57,18 +57,25 @@ export function hardBreaks(input: PackInput, output: PackOutput): number {
57
57
  * Its `pick(input)` packs the input with each member and gives the output of the winner, its id and the trace.
58
58
  *
59
59
  * - A member is a strategy id, or a `PortfolioEntry` with options. The members run in the given order.
60
+ * - `members` is a list, or a function of the input that gives the list. The strategy `portfolio` uses the function form.
60
61
  * - The key is the hard breaks, then `pack.overflow.excessArea`, then `pack.fill`. `PORTFOLIO_LEVELS` gives the specs.
61
62
  * - The hash of the key is `outputHash`. `selectBest` of the contract picks the winner.
62
63
  * - The output of the winner gets the trace stage `portfolio` with `move.pick` of `pickMoves`.
63
64
  * - The function throws a `RangeError` for an unknown strategy, an empty list and two members with the same id.
65
+ * With the function form, `pick` throws this error for the list of an input.
64
66
  */
65
- export function packPortfolio(members: readonly (string | PortfolioEntry)[], options: PackPortfolioOptions = {}): Portfolio<PackInput, PackOutput> {
67
+ export function packPortfolio(
68
+ members: readonly (string | PortfolioEntry)[] | ((input: PackInput) => readonly (string | PortfolioEntry)[]),
69
+ options: PackPortfolioOptions = {},
70
+ ): Portfolio<PackInput, PackOutput> {
66
71
  const seed = options.seed ?? 0;
67
- const list: Member<PackInput, PackOutput>[] = members.map((m) => {
68
- const e = typeof m === "string" ? { id: m, strategy: m } : m;
69
- strategy(e.strategy);
70
- return { id: e.id, run: (input: PackInput): PackOutput => packWith(e.strategy, input, e.options, seed) };
71
- });
72
+ const toMembers = (given: readonly (string | PortfolioEntry)[]): Member<PackInput, PackOutput>[] =>
73
+ given.map((m) => {
74
+ const e = typeof m === "string" ? { id: m, strategy: m } : m;
75
+ strategy(e.strategy);
76
+ return { id: e.id, run: (input: PackInput): PackOutput => packWith(e.strategy, input, e.options, seed) };
77
+ });
78
+ const list = typeof members === "function" ? (input: PackInput): Member<PackInput, PackOutput>[] => toMembers(members(input)) : toMembers(members);
72
79
  // Each level reads the measures of one output, so the measures of an output are computed one time
73
80
  const cache = new WeakMap<PackOutput, LayoutMeasures>();
74
81
  const measured = (input: PackInput, output: PackOutput): LayoutMeasures => {
package/src/registry.ts CHANGED
@@ -1,13 +1,20 @@
1
1
  import { maxrects, push, rows, shelf, skyline } from "./adapters.ts";
2
2
  import type { PackInput, PackOutput } from "./contract.ts";
3
+ import { portfolioStrategy } from "./portfolio-strategy.ts";
3
4
  import { createContext, type Strategy } from "./strategy.ts";
4
5
 
5
- /** Each strategy of the package. The benchmark starts each one on each input of the corpus. */
6
+ /** Each single strategy of the package. The benchmark starts each one on each input of the corpus. */
6
7
  export const strategies: readonly Strategy[] = Object.freeze([rows, skyline, shelf, push, maxrects] as Strategy[]);
7
8
 
8
- /** This function gives the strategy with an id. It throws a `RangeError` for an id that the registry does not have. */
9
+ /**
10
+ * The composite strategies of the registry: each one packs an input with other strategies. They are not in `strategies`.
11
+ * Since 0.2.7, the list has `portfolio` of roadmap item k20.
12
+ */
13
+ export const composites: readonly Strategy[] = Object.freeze([portfolioStrategy] as Strategy[]);
14
+
15
+ /** This function gives the strategy with an id, also a composite strategy. It throws a `RangeError` for an id that the registry does not have. */
9
16
  export function strategy(id: string): Strategy {
10
- const found = strategies.find((s) => s.id === id);
17
+ const found = strategies.find((s) => s.id === id) ?? composites.find((s) => s.id === id);
11
18
  if (found === undefined) throw new RangeError(`The registry has no strategy "${id}".`);
12
19
  return found;
13
20
  }