
Security News
CISA’s 2025 SBOM Guidance Adds Hashes, Licenses, Tool Metadata, and Context
CISA’s 2025 draft SBOM guidance adds new fields like hashes, licenses, and tool metadata to make software inventories more actionable.
Ruby implementation of flexible and human-friendly operations on Cartesian products
✅ Named dimensions with arbitrary keys
✅ Enumerate over Cartesian space with a single block argument
✅ Actions on Cartesian are decoupled from dimensionality: s.cartesian { |v| do_something(v) }
✅ Conditions for Cartesian space: s.cond(:set) { |v| v.dim1 > v.dim2 } }
✅ Calculation over named dimensions: s.cartesian { |v| puts "#{v.dim1} and #{v.dim2}" }
✅ Functions on Cartesian space: s.func(:add, :my_sum) { |v| v.dim1 + v.dim2 }
✅ Lazy and eager evaluation
✅ Progress bars for large Cartesian spaces
✅ Export of Cartesian space to Markdown or CSV
✅ Import of Cartesian space from JSON or YAML
✅ Export of Cartesian space to Markdown or CSV
✅ Structured and colorized terminal output
FlexCartesian
is especially useful in the following scenarios.
Systematically evaluate an application or algorithm across all combinations of parameters:
threads
, batch_size
, backend
, etcthroughput
, latency
, memory
Iterate over all combinations of hyperparameters:
learning_rate
, max_depth
, subsample
, n_estimators
max_depth < 10 if learning_rate > 0.1
)accuracy
, AUC
, etcGenerate all valid infrastructure configurations:
region: ["us-west", "eu-central"]
tier: ["basic", "pro"]
replicas: [1, 3, 5]
With conditions like "basic tier cannot have more than one replica:
s.cond(:set) { |v| (v.tier == "basic" ? v.replicas == 1 : true) }
Generate and benchmark all valid CLI calls:
myapp --threads=4 --batch=32 --backend=torch
Capture runtime, output, errors, etc.
Automatically cover input parameter spaces for:
Generate multidimensional experimental spaces for:
Output Cartesian data as:
Use it to drive automated test inputs for:
bundle install
gem build flex-cartesian.gemspec
gem install flex-cartesian-*.gem
#!/usr/bin/ruby
require 'flex-cartesian'
# BASIC CONCEPTS
# 1. Cartesian object is a set of combinations of values of dimansions.
# 2. Dimensions always have names.
puts "\nDefine named dimensions"
example = {
dim1: [1, 2],
dim2: ['x', 'y'],
dim3: [true, false]
}
puts "\nCreate Cartesian space"
s = FlexCartesian.new(example)
def do_something(v)
# do something here on vector v and its components
end
# ITERATION OVER CARTESIAN SPACE
# 3. Iterator is dimensionality-agnostic, that is, has a vector syntax that hides dimensions under the hood.
# This keeps foundational code intact, and isolates modifications in the iterator body 'do_something'.
# 4. For efficiency on VERY largse Cartesian spaces, there are
# a). lazy evaluation of each combination
# b). progress bar to track time-consuming calculations.
puts "\nIterate over all Cartesian combinations and execute action (dimensionality-agnostic style)"
s.cartesian { |v| do_something(v) }
puts "\nIterate over all Cartesian combinations and execute action (dimensionality-aware style)"
s.cartesian { |v| puts "#{v.dim1} & #{v.dim2}" if v.dim3 }
puts "\nIterate and display progress bar (useful for large Cartesian spaces)"
s.progress_each { |v| do_something(v) }
puts "\nIterate in lLazy mode, without materializing entire Cartesian product in memory"
s.cartesian(lazy: true).take(2).each { |v| do_something(v) }
# FUNCTIONS ON CARTESIAN SPACE
# 5. A function is a virtual dimension that is calculated based on a vector of base dimensions.
# You can think of a function as a scalar field defined on Cartesian space.
# 6. Functions are printed as virtual dimensions in `.output`.
# 7. Functions do not add to `.size` of Cartesian space.
puts "\nAdd function 'triple'"
puts "Note: function is visualized in .output as a new dimension"
s.func(:add, :triple) { |v| v.dim1 * 3 + (v.dim3 ? 1: 0) }
s.func(:run)
s.output
puts "\Add and then remove function 'test'"
s.func(:add, :test) { |v| v.dim3.to_i }
s.func(:del, :test)
puts "\nThis function will calculate first, always"
s.func(:add, :test_first, order: :first) { puts "HERE" }
puts "\nThis function will calculate last, always"
s.func(:add, :test_last, order: :last) { puts "HERE" }
puts "\nNew functions respect first and last ones"
s.func(:add, :test_more) { puts "HERE" }
puts "\nFirst and last functions are handy for pre- and post-processing for each combination"
# CONDITIONS ON CARTESIAN SPACE
# 8. A condition is a logical constraint for allowed combitnations of Cartesian space.
# 9. Using conditions, you can take a slice of Cartesian space.
# In particular, you can reflect semantical dependency of dimensional values.
puts "Build Cartesian space that includes only odd values of 'dim1' dimension"
s.cond(:set) { |v| v.dim1.odd? }
puts "print all the conditions in format 'index | condition '"
s.cond
puts "Test the condition: print the updated Cartesian space"
s.output
puts "Test the condition: check the updated size of Cartesian space"
puts "New size: #{s.size}"
puts "Clear condition #0"
s.cond(:unset, index: 0)
puts "Clear all conditions"
s.cond(:clear)
puts "Restored size without conditions: #{s.size}"
# PRINT
puts "\nPrint Cartesian space as plain table, all functions included"
s.output
puts "\nPrint Cartesian space as Markdown"
s.output(format: :markdown)
puts "\nPrint Cartesian space as CSV"
s.output(format: :csv)
puts "\nGranular output of Cartesian space dimensions: #{s.dimensions(values: false)}"
puts "\nGranular output of Cartesian vectors:"
s.cartesian { |v| puts s.dimensions(v, separator: ' ') }
# IMPORT / EXPORT
puts "\nImport Cartesian space from JSON (similar method for YAML)"
File.write('example.json', JSON.pretty_generate(example))
puts "\nNote: after import, all assigned functions will calculate again, and they appear in the output"
s.import('example.json').output
puts "\nExport Cartesian space to YAML (similar method for JSON)"
s.export('example.yaml', format: :yaml)
# UTILITIES
puts "\nGet number of Cartesian combinations"
puts "Note: .size counts only dimensions, it ignores virtual constructs (functions, conditions, etc.)"
puts "Total size of Cartesian space: #{s.size}"
puts "\nPartially converting Cartesian space to array:"
array = s.to_a(limit: 3)
puts array.inspect
The most common use case for FlexCartesian is sweep analysis, that is, analysis of target value on all possible combinations of its parameters. FlexCartesian has been designed to provide a concise form for sweep analysis:
require 'flex-cartesian'
# create Cartesian space from JSON describing input parameters
s = FlexCartesian.new(path: './config.json')
# Define the values we want to calculate on all possible combinations of parameters
s.func(:add, :cmd) { |v| v.threads * v.batch }
s.func(:add, :performance) { |v| v.cmd / 3 }
# Calculate
s.func(:run)
# Save result as CSV, to easily open it in any business analytics tool
s.output(format: :csv, file: './benchmark.csv')
# For convenience, print result to the terminal
s.output
As this code is a little artificial, let us build real-world example. Perhaps, we want to analyze PING perfomance from our machine to several DNS providers: Google DNS, CloudFlare DNS, and Cisco DNS. For each of those services, we would like to know:
These input parameters form the following dimensions.
{
"count": [2, 4],
"size": [32, 64],
"target": [
"8.8.8.8", // Google DNS
"1.1.1.1", // Cloudflare DNS
"208.67.222.222" // Cisco OpenDNS
]
}
Note that //
isn't officially supported by JSON, and you may want to remove the comments if you experience parser errors.
Let us build the code to run over these parameters.
require 'flex-cartesian'
s = FlexCartesian.new(path: './ping_parameters.json') # file with the parameters as given above
result = {} # raw result of each ping
s.func(:add, :command) { |v| "ping -c #{v.count} -s #{v.size} #{v.target}" } # ping command
s.func(:add, :raw_ping, hide: true) { |v| result[v.command] ||= `#{v.command} 2>&1` } # capturing ping result
s.func(:add, :time) { |v| v.raw_ping[/min\/avg\/max\/(?:mdev|stddev) = [^\/]+\/([^\/]+)/, 1]&.to_f } # fetch ping time from result
s.func(:add, :min) { |v| v.raw_ping[/min\/avg\/max\/(?:mdev|stddev) = ([^\/]+)/, 1]&.to_f } # fetch min time from result
s.func(:add, :loss) { |v| v.raw_ping[/(\d+(?:\.\d+)?)% packet loss/, 1]&.to_f } # fetch ping loss from result
s.func(:run, progress: true, title: "Pinging") # Sweep analysis! Benchmark all possible combinations of parameters
s.output(format: :csv, file: './result.csv') # save benchmark result as CSV
s.output(colorize: true) # for convenience, show result in terminal
If you run the code, after a while it will generate benchmark results on the screen:
Additionally, CSV version of this result is saved as ./benchmark.csv
The PING benchmarking code above is 100% practical and illustrative. You can modify it and benchmark virtually anything:
dd
gdsio
In any use case, FlexCartesian will unfold complete landscape of the target performance over all configurable parameters. As result, you will be able to spot optimal configurations, correlations, bottlenecks, and sweet spots. Moreover, you will make your conclusions in a justifiable way.
Here is example of using FlexCartesian for performance/cost analysis of YOLO.
FlexCartesian.new(dimensions = nil, path: nil, format: :json)
dimensions_hash
: optional hash with named dimensions; each value can be an Enumerable
(arrays, ranges, etc)path
: optional path to file with stored dimensions, JSON and YAML supportedformat
: optional format of path
file, defaults to JSONExample:
dimensions = {
dim1: [1, 2],
dim2: ['x', 'y'],
dim3: [true, false]
}
FlexCartesian.new(dimensions)
Example:
# With block
cartesian(dims = nil, lazy: false) { |vector| ... }
# Without block: returns Enumerator
cartesian(dims = nil, lazy: false)
dims
: optional dimensions hash (default is the one provided at initialization).lazy
: if true, returns a lazy enumerator.Each combination is passed as a Struct
with fields matching the dimension names:
s.cartesian { |v| puts "#{v.dim1} - #{v.dim2}" }
func(command = :print, name = nil, hide: false, progress: false, title: "calculating functions", order: nil, &block)
command
: symbol, one of the following
:add
to add function as a virtual dimension to Cartesian space:del
to delete function from Cartesian space:print
as defaut action, prints all the functions added to Cartesian space:run
to calculate all the functions defined for Cartesian spacename
: symbol, name of the virtual dimension, e.g. :my_function
hide
: flag that hides or shows the function in .output; it is useful to hide intermediate calculationsprogress
: show progress bar during :run
, useful for large Cartesian spacetitle
: title of the progress barorder
: can be :first
or :last
to make the function calculate before or after all other functionsblock
: a function that receives each vector and returns a computed valueFunctions show up in .output
like additional (virtual) dimensions.
Note: functions must be calculated excpliticy using
:run
command. Before the first calculation, a function hasnil
values in.output
. Explicit :run is reequired to unambigously control points in the execution flow where high computational resource is to be consumed. Otherwise, automated recalculation of functions, perhaps, during.output
would be a difficult-to-track computational burden.
Example:
s = FlexCartesian.new( { dim1: [1, 2], dim2: ['A', 'B'] } )
s.func(:add, :increment) { |v| v.dim1 + 1 }
s.func(:run)
s.output(format: :markdown)
# | dim1 | dim2 | increment |
# |------|------|--------|
# | 1 | "A" | 2 |
# | 1 | "B" | 2 |
# ...
size(dims = nil) → Integer
Returns the number of possible combinations.
to_a(limit: nil) → Array
limit
: maximum number of combinations to collect.progress_each(dims = nil, lazy: false, title: "Processing") { |v| ... }
Displays a progress bar using ruby-progressbar
.
output(separator: " | ", colorize: false, align: true, format: :plain, limit: nil, file: nil)
separator
: how to visually separate columns in the outputcolorize
: whether to colorize output or notalign
: whether to align output by column or notformat
: one of :plain
, :markdown
, or :csv
limit
: break the output after the first limit
Cartesian combinationsfile
: print to file
Prints all combinations in table form (plain/markdown/CSV).
Markdown example:
| dim1 | dim2 |
|------|------|
| 1 | "a" |
| 2 | "b" |
dimensions(data = @dimensions, raw: false, separator: ', ', dimensions: true, values: true)
Generates formatted Cartesian dimensions or vectors of Cartesian space
data
: what to format, either Cartesian vector (usually `s.cartesian { |v| ... }) or entire Cartesian dimensionsraw
: if enabled, overrides any other formarring flags and returns the same as .inspect
separator
: how to separate individual componentsdimensions
: whether or not show dimension namesvalues
: whether or not show value(s) associated with dimensionsimport(path, format: :json)
path
: input fileformat
: format to read, :json
and :yaml
supportedObsolete import methods:
s.from_json("file.json")
s.from_yaml("file.yaml")
export(path, format: :json)
path
: output fileformat
: format to export, :json
and :yaml
supportedcond(command = :print, index: nil, &block)
command
: one of the following
:set
to set the condition to Cartesian space:unset
to remove the index
condition from Cartesian space:clear
to remove all conditions from Cartesian space:print
default command, prints all the conditions on the Cartesian spaceindex
: index of the condition set to Cartesian space, it is used to remove specified conditionblock
: definition of the condition, it should return true
or false
to avoid unpredictable behaviorExample:
s.cond(:set) { |v| v.dim1 > v.dim3 }
s.cond # defaults to s.cond(:print) and shows all the conditions in the form 'index | definition'
s.cond(:unset, 0) # remove the condition
s.cond(:clear) # remove all conditions, if any
This project is licensed under the terms of the GNU General Public License v3.0.
See LICENSE for more details.
FAQs
Unknown package
We found that flex-cartesian demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
CISA’s 2025 draft SBOM guidance adds new fields like hashes, licenses, and tool metadata to make software inventories more actionable.
Security News
A clarification on our recent research investigating 60 malicious Ruby gems.
Security News
ESLint now supports parallel linting with a new --concurrency flag, delivering major speed gains and closing a 10-year-old feature request.